REST Services in Bing Maps Windows Store Apps (JavaScript)

When using Bing Maps, you may find that you need to access a REST based web service from time to time. In Bing Maps there are many REST based services available that can be used to bring data into your app. The Bing Maps REST service provides functionalities such as Geocoding, Reverse Geocoding, Routing, Static Imagery, and Traffic Incident data. There is also the Bing Spatial Data Services that can be used to store your own location data and have it exposed as a REST based spatial service. We also provide NAVTEQ point of interest data through the Bing Spatial Data Services. Although integrating REST services into the Bing Maps JavaScript controls is fairly easy, there is a small change required when developing for the Windows Store App platform. In this blog post, we will walk through how to integrate the Bing Maps Traffic Incident REST service with the Bing Maps Windows Store App JavaScript control.

Setting up the project

To set up the project, open Visual Studios 2012 and create a new project. In the window that opens, select JavaScript -> Windows Store. Then, select the Blank App template, call the application BingMapsREST_WinRTJS and press OK.

In an effort to keep things more cleanly, we are going to create a separate JavaScript file for our application. To do this, right click on the js folder and select Add -> New Item, and create a new JavaScript file called BingMapsREST.js.

At this point, your Solution should look something like this:

clip_image002[3]

Next, you will want to add a reference to the Bing Maps JavaScript library. First, right click on the References folder and press Add Reference. Then, select Windows -> Extensions and then select Bing Maps for JavaScript. If you do not see this option, be sure to verify that you have installed the Bing Maps SDK for Windows 8 style apps.

Now, open up the default.html file and add references to the Bing Maps SDK along with a reference to our JavaScript file. The layout for our application will consist of a map and nothing more. The HTML for the default.html file should look like this:

<!DOCTYPE html>

<html>

<head>

    <meta charset="utf-8" />

    <title>BingMapsREST_WinRTJS</title>

 

    <!-- WinJS references -->

    <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />

    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>

    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

 

    <!-- BingMapsREST_WinRTJS references -->

    <link href="/css/default.css" rel="stylesheet" />

    <script src="/js/default.js"></script>

 

    <!-- Bing Maps references -->

    <script type="text/javascript"

            src="ms-appx:///Bing.Maps.JavaScript//js/veapicore.js"></script>

    <script type="text/javascript"

            src="ms-appx:///Bing.Maps.JavaScript//js/veapimodules.js"></script>

 

    <!-- Our Bing Maps JavaScript Code -->

    <script src="/js/BingMapsREST.js"></script>

</head>

<body>

    <div id="myMap"></div>

</body>

</html>

Loading the Bing Maps Control

Next, we will add the functionality to load the Bing Maps control to the BingMapsREST.js file. To start, we are going to create five global variables: map, infobox, dataLayer, sessionKey and trafficTypes. The trafficTypes will be an array of string that has the names of the different types of traffic. The array will be set up so that the index of the array matches the trafficType value which is a number. We will now create a function called GetMap which will load the Bing Maps control for us. In this function, we will also add an entity collection to the map for our data, an Infobox for displaying information, and generate a session key to be used with the Traffic REST service (making those calls non-billable). Once the session key is created, we will attach a throttled event to the map which will fire when the map has finished moving. When this event is triggered, a request for traffic data in the current map view will be made. The GetMap function will be loaded after the Microsoft.Maps.Map module has completed loading. Your code should look like this:

var map, infobox, dataLayer, sessionKey;

var trafficTypes = [null, "Accident", "Congestion", "Disabled Vehicle", "Mass Transit", "Miscellaneous", "Other News", "Planned Event", "Road Hazard", "Construction", "Alert", "Weather"];

 

function GetMap() {

    // Initialize the map

    map = new Microsoft.Maps.Map(document.getElementById("myMap"), {

        credentials: "YOUR_BING_MAPS_KEY",

        center: new Microsoft.Maps.Location(51.5, 0),

        zoom: 10

    });

 

    //Add a layer for pushpin data

    dataLayer = new Microsoft.Maps.EntityCollection();

    map.entities.push(dataLayer);

 

    //Add a layer for the infobox

    var infoboxLayer = new Microsoft.Maps.EntityCollection();

    map.entities.push(infoboxLayer);

 

    //Create a global infobox control

    infobox = new Microsoft.Maps.Infobox(new Microsoft.Maps.Location(0, 0), {

        visible: false,

        offset: new Microsoft.Maps.Point(0, 20),

        height: 150,

        width: 300

    });

    infoboxLayer.push(infobox);

 

    map.getCredentials(function (c) {

        sessionKey = c;

 

        //Use a throttled event to reduce the number of unwanted events being fired.

        Microsoft.Maps.Events.addThrottledHandler(map, 'viewchangeend', MakePOIRequest, 250);

 

        //Make a request as the map to initially fill the map

        MakePOIRequest();

    });

}

 

//Initialization logic for loading the map control

(function () {

    function initialize() {

        Microsoft.Maps.loadModule('Microsoft.Maps.Map', { callback: GetMap });

    }

 

    document.addEventListener("DOMContentLoaded", initialize, false);

})();

Adding the Application Logic

When the user moves the map, an event will be triggered and call a function named MakePOIRequest. This function will generate a REST URL for querying the Traffic Incident service by bounding box. This URL will then be used to request data. Since this service is a Bing Maps service, it does not sit in the same domain as your application, and as such, we need to make a cross domain request. Making this request in a standard JavaScript application can be made by using the XMLHttpRequest object. When developing for the Windows Store App platform, we should use the WinRT.xhr class. This class wraps the XMLHttpRequest object and makes a call to it asynchronously. We can then add a Promise function (callback or ‘then’ function) which we will call POICallback. This callback function will process the response. The MakePOIRequest function looks like this:

function MakePOIRequest() {

    var bbox = map.getBounds();

 

    //Create the request to the traffic incidents service.

    var poiRequest = "https://dev.virtualearth.net/REST/v1/Traffic/Incidents/" +

        bbox.getSouth() + "," + bbox.getWest() + "," + bbox.getNorth() + "," +

        bbox.getEast() + "?key=" + sessionKey;

 

    //Process the request and fire the callback

    WinJS.xhr({ url: poiRequest }).then(POICallback);

}

When the service responds and the POICallback function is fired, we will clear the map and verify that the request was successful. If successful, we will take the response text and parse it into a JSON object. Once the response is in the form of a JSON object we can loop through the results and render the data on the map. Since the traffic data has different severity levels, we will use different colored pushpins to display the locations on the map (as shown in the table below).

Pushpin

Severity

Description

green_pingreen_pin.png

1

Low Impact

yellow_pinyellow_pin.png

2

Minor

red_pinred_pin.png

3

Moderate

black_pinblack_pin.png

4

Serious

Add these pushpins to the images folder of the project. Your project should now look like this:

clip_image012[3]

We will also add a Title and Description property to the pushpins and add details for the incident in these fields. We will be able to retrieve these properties from the Infobox control. One of the properties of an incident is a date and time of the last time the incident information was updated. This is in the form of an OData date value. We will need to parse this date value into a nicely formatted date. Finally, as the pushpin is clicked, we will need to add the logic to display the infobox and fill the infobox with the Title and Description values that we added to the pushpin. Putting this all together, we end up with the following JavaScript code:

function POICallback(response) {     dataLayer.clear();     infobox.setOptions({ visible: false });

    if (response.status == 200) {         //Parse JSON response         var result = JSON.parse(response.responseText);

        if (result != null &&             result.resourceSets != null &&             result.resourceSets.length > 0 &&             result.resourceSets[0].resources != null &&             result.resourceSets[0].resources.length > 0) {                         var r = result.resourceSets[0].resources;

            //add the POI data to the map             for (var i = 0; i < r.length; i++) {                 //Get coordinate of incident                 var location = new Microsoft.Maps.Location(r[i].point.coordinates[0], r[i].point.coordinates[1]);

                //Get custom icon based on severity                 var customIcon = '/images/';

                switch (r[i].severity) {                     case 2: //Minor                         customIcon += 'yellow_pin.png';                         break;                     case 3: //Moderate                         customIcon += 'red_pin.png';                         break;                     case 4: //Serious                         customIcon += 'black_pin.png';                         break;                     case 1: //Low Impact                     default:                         customIcon += 'green_pin.png';                         break;                 }

                //Create a pushpin for the incident                 var pushpin = new Microsoft.Maps.Pushpin(location, { icon: customIcon });                 pushpin.Title = trafficTypes[r[i].type];

                //Generate the description HTML markup                 pushpin.Description = "<b>Last Update:</b> " + parseJsonDate(r[i].lastModified) +                         '<br/><br/><b>Description:</b> ' + trafficTypes[r[i].type] + " " + r[i].description;

                //Add a click event to the pushpin                 Microsoft.Maps.Events.addHandler(pushpin, 'click', displayInfobox);

                //Add the pushpin to the data layer                 dataLayer.push(pushpin);             }         }     } }

function displayInfobox(e) {     if (e.targetType == 'pushpin') {         infobox.setLocation(e.target.getLocation());         infobox.setOptions({ visible: true, title: e.target.Title, description: e.target.Description });     } }

function parseJsonDate(jsonDate) {     var offset = new Date().getTimezoneOffset();     var parts = /\/Date\((-?\d+)([+-]\d{2})?(\d{2})?.*/.exec(jsonDate);

    if (parts[2] == undefined)         parts[2] = 0;

    if (parts[3] == undefined)         parts[3] = 0;

    return new Date(+parts[1] + offset + parts[2] * 3600000 + parts[3] * 60000); }

If you run the application you should see a bunch of different colored pushpins appear on the map. If nothing loads, you may be zoomed out too much, or there are no traffic incidents in the area you are viewing. Here is a screenshot of the application displaying traffic data for part of Europe and the infobox open for one of the incidents.

TrafficIncidentMap

For information on Traffic data availability, check out the Bing Maps Traffic Coverage page in MSDN.

- Ricky