Learning Bing Maps API

5 (1 reviews total)
By Artan Sinani
    What do you get with a Packt Subscription?

  • Instant access to this title and 7,500+ eBooks & Videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies

About this book

Provided as a part of Microsoft's Bing suite of search engines, Bing Maps is a web mapping service powered by the Bing Maps for Enterprise framework. The need for geospatial data has increased dramatically in the last few years. Adding a mapping context to any location-based data is becoming more and more common, and businesses are embracing it to improve their user experience with new data richness.

Comprising of simple, follow-along examples, Learning Bing Maps API will show you how to use the many features of Bing Maps, from dropping a simple map on a web page, to fetching geospatial data from the Microsoft servers. Through the course of this book you will build a solid foundation for creating your own geo-applications.

Following the hands-on recipes of this book, you will build a different web app in each chapter as you communicate with different APIs provided by Bing Maps. You will build your own library of JavaScript modules that talk to the Microsoft Maps API.

You will create a custom theme for the map, with your own controls. Taking advantage of the global reach of Bing Maps, you will learn how to build a route scheduler for a delivery company in Madrid, Spain, and then you will discover how to create jobs on the Bing Maps servers for geocoding addresses in California, USA.

By the end of the book you will have learned everything you need to know to embed a map on a web page, with your own geo-data, or data obtained by the Bing Map Services.

Publication date:
November 2013
Publisher
Packt
Pages
116
ISBN
9781783550371

 

Chapter 1. Introduction to Bing Maps AJAX Control Version 7

Bing Maps by Microsoft provide a collection of Application Programming Interfaces (APIs) that adds mapping capabilities to location-aware applications. The APIs, open to the public since their first beta release in 2005, can query for location or business by address or coordinates. They enable searching for routes, including traffic information; searching for geometrical shapes of geographical entities such as countries, regions, and other smaller administrative divisions. Consumers of Bing Maps APIs can geocode address, or reverse geocode coordinates via automated jobs.

All the APIs can be used with JavaScript, but server-side languages such as .Net, PHP, Ruby, and so on can also be used. The supported browsers include Internet Explorer Version 7 onwards, Firefox Version 3.6 onwards, Safari Version 5 onwards, Opera, Google Chrome, iPhone, Android, and Blackberry mobile browsers are also supported.

To access the APIs, a Bing Maps key is needed; it can be obtained at the Accounts Center available at https://www.bingmapsportal.com. Bing Maps offer a few access options, including a basic key for non-profit, educational purposes, with limited usage. For production scenarios, Bing Maps offer an enterprise key.

 

Bing Maps AJAX Control Version 7


The AJAX Control is a JavaScript library that adds a fully functional map to a web page. The current version, V7, released in 2010, was written with a modular approach in mind, which reduced the core part of the library to around 35 KB. Additional modules, such as the directions module, or the ones we will write in this chapter, can be loaded dynamically on request.

In this chapter, we are going to place a map on our webpage, and customize its look. First, let's create a simple HTML5 index.html file, as follows:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>LBM | Chapter 1: Introduction to Bing Maps</title>
</head>
<body>
</body>
</html>

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Microsoft recommends using UTF-8 encoding on the page. To load the control, we add the script tag to the header, as shown in the following code snippet:

<script src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>

Now, let's add the div element that will contain the map in the body, as shown in the following code snippet:

<div id="lbm-map"></div>

We need to style our div with height and width, so let's add an external stylesheet to our page, which now should look as shown in the following code:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>LBM | Chapter 1: Introduction to Bing Maps</title>
  <link href="app.css" rel="stylesheet" />
  <script src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
</head>
<body>
  <div id="lbm-map"></div>
</body>
</html>

The width and height of the map container will be used by the AJAX Control to resize the map. The container must also be styled with relative or absolute position. So, let's code them in our stylesheet file, as follows:

#lbm-map {
  position: relative;
  width: 100%;
  margin: 20px auto;
  border: 1px solid #888;
}

We have used Learning Bing Maps (LBM) to prefix our styles.

Now, let's write the function that instantiates the map on page load in a app.js JavaScript file as shown in the following code:

window.onload = function() {
  var container = document.getElementById('lbm-map'),
    resize = function () { container.style.height = window.innerHeight + 'px'; };
  resize();

  var map = new Microsoft.Maps.Map(container, {
    credentials: '[YOUR BING MAPS KEY]',
    center: new Microsoft.Maps.Location(39.554883, -99.052734),
    mapTypeId: Microsoft.Maps.MapTypeId.road,
    zoom: 4
  });

  window.onresize = function() {
    resize()
  };
};

First, we get a reference to the map container and store it in a container variable for later use. Since we want our map to cover the whole browser window, we give our container the same height as the window (we already specified the width value as 100 percent).

The main function (we will also refer to them as classes, even though this term is not supported as such in JavaScript) of the AJAX Control is named Microsoft.Maps.Map, and accepts two arguments—the first argument is a reference to the DOM element of the map container and the second argument is a Microsoft.Maps.MapOptions object—used to customize the map.

One of the important properties of the MapOptions object is credentials, and it holds the Bing Maps key. Other properties control the view of the map, such as the zoom level, the background color, the default controls; or the map's behavior, such as panning, user clicks or touches, keyboard input, and so on (the full reference of MapOptions can be found at http://msdn.microsoft.com/en-us/library/gg427603.aspx). Please note that Microsoft.Maps is the namespace that groups all the JavaScript classes of the control.

Going back to our code, at the end of it, we make sure the map is resized to fill the whole window, even when the user resizes the browser window. Note how the map follows the dimensions of its container.

We need to add a reference to this file on our index.html page, and we place the reference just before the closing </body> tag, so that the file is loaded after all the other assets of the page have been downloaded by the browser.

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

If we open the index.html file on the browser, we should get the map as shown in the following figure:

The map is centered somewhere in Kansas, United States. To do this, we set the center option to a Microsoft.Maps.Location instance, which accepts two parameters: latitude and longitude.

new Microsoft.Maps.Location(39.554883, -99.052734)

The zoom option can take any value between 1 and 20, with 1 zooming out far and 20 zooming in close. The mapTypeId property is a value of Microsoft.Maps.MapTypeId, which includes aerial, birdseye, and so on. (The list can be found at http://msdn.microsoft.com/en-us/library/gg427625.aspx).

Now, let's remove the map's dashboard, the controls at the top-right corner of the map, and replace them with our own. To achieve the former, we add two more options to our map instantiation function, as shown in the following code snippet:

var map = new Microsoft.Maps.Map(document.getElementById('lbm-map'), {
    credentials: '[YOUR BING MAPS KEY]',
    center: new Microsoft.Maps.Location(39.554883, -99.052734),
    mapTypeId: Microsoft.Maps.MapTypeId.road,
    zoom: 4,
    showBreadcrumb: false,
    showDashboard: false
});
 

Custom modules


We'll add our own controls to the map; they will change the zoom level and the map type. For this, we will take advantage of the module structure of Bing Maps. The modules are just JavaScript functions that are loaded dynamically by Bing Maps AJAX Control. Apart from better code organization, the modules provide on-demand downloads, reducing the initial script load.

On the flip side, this means that the JavaScript files will be loaded in multiple HTTP requests. If you prefer your scripts merged into a single HTTP request, you can use tools that merge and minimize website assets, such as Asset Bundler in ASP.NET, Sprockets in Ruby, gzipit in PHP, and so forth. In this case, make sure that the custom modules are instantiated after the map is loaded (more information about the Bing Maps modules can be found at http://msdn.microsoft.com/en-us/library/hh125836.aspx).

We'll call our module LearningTheme, and place it under a learningTheme folder. Let's write our module within a self-calling, immediately invoked function in the learningTheme.js file:

(function () {
  var lbm = window.lbm = {};
  lbm.LearningTheme = function(map) {
    this._map = map;
  };
  Microsoft.Maps.moduleLoaded('lbm.LearningTheme');
})(this);

We have placed our LearningTheme module under the namespace lbm, just as we did with the CSS styles. This way, we expose only one global variable, lbm.

The module is just a normal JavaScript function with one argument, that is, map. This is a reference to the Bing Maps map we instantiated earlier, which we'll store into a private variable named as _map.

Note

We'll use the convention of starting the variable names with an underscore in this book, to indicate that they should be considered as private variables, and not be called outside our module. There are ways to achieve better access restriction for objects in JavaScript, but they are outside the scope of this book.

After the module body, we add the following line, which turns our function into a Bing Maps module:

Microsoft.Maps.moduleLoaded('lbm.LearningTheme');

We then load the module in our window.onload function by first registering it:

Microsoft.Maps.registerModule('lbm.LearningTheme', 'learningTheme/learningTheme.js');
Microsoft.Maps.loadModule('lbm.LearningTheme', {
  callback: function() {
    var learningTheme = new lbm.LearningTheme(map);
  }
});

The registration function accepts two arguments: the name of the module, and its relative or absolute (or remote) location. We then call Microsoft.Maps.loadModule, which again needs the module name, and an options object. With the latter argument, we can specify what should happen when the module is loaded. In this case, we instantiate it by passing a reference to the map we created earlier.

Let's add the two buttons that will control the zoom level, one with a minus (-) sign that will decrease the zoom level, and one with a plus (+) sign that will increase it. We'll also add a third button that will toggle the map type between road and aerial.

A function within the module's prototype (a private instance method we can call later with the this keyword) achieves the following:

lbm.LearningTheme.prototype = {
  _addHeader: function() {
    var header = document.createElement('div');
    header.className = 'lbm-map-header';
    header.innerHTML = [
      '<button class="lbm-btn" id="lbm-map-btn-zoomin">-</button>',
      '<button class="lbm-btn" id="lbm-map-btn-zoomout">+</button>',
      '<button class="lbm-btn" id="lbm-map-btn-maptype">Aerial</button>'
    ].join('');
    
    this._map.getRootElement().appendChild(header);
  }
};

First, we create a DOM div element and give it a class name OF lbm-map-header, so that we can style it later. The markup for the buttons is simple, shown as follows:

<button class="lbm-btn" id="lbm-map-btn-zoomin">-</button>

Again we specify a class (lbm-btn) for styling and an ID (lbm-map-btn-zoomin) for controlling its behavior. We place the buttons in an array that we later join with an empty string. This is a common technique for building HTML strings in a JavaScript code, BECAUSE it improves the readability of the code.

At the end, we append the markup to the map's root element. Bing Maps provide a nice utility method to get the latter, getRootElement. (The list of methods, properties, and events of the map can be found at http://msdn.microsoft.com/en-us/library/gg427609.aspx).

We want the header to be added to the map when the module is loaded; therefore, we call the _addHandler method in the module's constructor, which now looks like this:

lbm.LearningTheme = function(map) {
  this._map = map;
  this._addHeader();
};

If you view the index.html file on the browser, you will not see the default Bing Maps dashboard, but you won't see our controls either. This is because we have not added our styles yet. Let's place them in a learningTheme.css file inside the learningTheme folder, as shown in the following code:

.lbm-map-header {
  position: absolute;
  top: 1em;
  left: 1em;
  width: 100%;
  color: #fff;
  z-index: 100;
}

.lbm-map-header .lbm-btn {
  font-size: 1em;
  border: 0;
  background: #F04C40;
  color: #fff;
  display: inline-block;
  text-decoration: none;
  width: 2.5em;
  height: 2em;
  line-height: 2em;	
  padding: 0;
  margin: 0 1px 0 0;
  outline: 0;
  text-align: center;
  cursor: pointer;

  opacity: .8;

  border-radius: .2em;
  -webkit-border-radius: .2em;
  -moz-border-radius: .2em;
}

.lbm-map-header #lbm-map-btn-zoomin {
  border-top-right-radius: 0;
  -webkit-border-top-right-radius: 0;
  -moz-border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  -webkit-border-bottom-right-radius: 0;
  -moz-border-bottom-right-radius: 0;
}

.lbm-map-header #lbm-map-btn-zoomout {
  border-top-left-radius: 0;
  -webkit-border-top-left-radius: 0;
  -moz-border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  -webkit-border-bottom-left-radius: 0;
  -moz-border-bottom-left-radius: 0;
}

.lbm-map-header .lbm-btn:hover {
  background: #cf343e;
}

.lbm-map-header .lbm-btn:active {
  background: #cc1d28;
}

.lbm-map-header #lbm-map-btn-maptype {
  margin-left: 2em;
  width: auto;
  padding: 0 .8em;
}

We make sure that the buttons' container has an absolute position and a high z-index value, so that it appears above the map.

Now, we need to add a link to the CSS file to our HTML document, and we'll do this when the module is loaded. This way, if the module is never loaded, then the stylesheet is not loaded either. Again, loading our CSS file on demand means another HTTP request, and if you prefer to have the styles merged into a single file, then you can use the same technique as for the JavaScript files, and skip the following step:

We'll add a link to our CSS file to the head of the page as shown in the following code snippet:

_addCss: function() {
  var link = document.createElement('link');
  link.setAttribute('rel', 'stylesheet');
  link.setAttribute('href', 'learningTheme /learningTheme.css');
  var head = document.getElementsByTagName('head')[0];
  head.appendChild(link);
}

First, the preceding function creates a link element, with a href attribute pointing at our learningTheme.css file. Then, it gets a reference to the head of the document, and it appends to it the newly created element.

As with the buttons markup, we need the stylesheet to be loaded with the module, so we'll call this function in the module's constructor, as shown in the following code:

lbm.LearningTheme = function(map) {
  this._map = map;
  this._addCss();
  this._addHeader();
};

If we open the index.html file on a browser now, we can finally see our controls, as shown in the following screenshot:

 

Map events


We need our buttons to respond to mouse clicks. Since we added them to the map's root element, we can use the Bing Maps events to add this functionality. Bing Maps AJAX Control provides a rich event system, that can be attached to the map or its entities. Some of the events include click, dblclick, keydown, keypress, imagerychanged, and so on.

Let's add our eventing functionality to a private method, which accepts a context argument, as shown in the following code:

_bind: function(self) {
  Microsoft.Maps.Events.addHandler(this._map, 'click', function(e) {
    if (e.originalEvent && e.originalEvent.target) {
     var btn = e.originalEvent.target;
      if (btn.tagName === 'BUTTON' && btn.className.match(/lbm-btn/) ) {
        if (btn.id === 'lbm-map-btn-maptype') {
          self._changeMapType(btn);
        } else {
          self._zoom(btn);
        }
        e.handled = true;
      }
    }
  });
}

The self argument passed to the _bind function will call any functions declared within it in the module's context. This is necessary, because JavaScript changes the meaning of this inside the handling function from our module to the DOM element firing the event.

We then add an event handler to the map by calling the Microsoft.Maps.Events.addHandler method, which accepts three parameters—the map (or its entity, such as a pin or shape), the name of the event, and the handler function. To the latter is then passed a MouseEventsArgs object, which exposes a few properties and methods (see the full description available at http://msdn.microsoft.com/en-us/library/gg406731.aspx).

One of these properties is originalEvent. It is tied to the DOM event of the clicked element, which can be found by calling e.originalEvent.target. We then make sure that the elements clicked are, in fact, our buttons, and we call the respective handler depending on their ID, and passing it the button clicked as an argument.

We set the handled property to the Events object to true, telling the map to not call any original handlers attached to the same event.

In order for the events handlers to work, we need to call our binding function in the module's constructor, passing it this as the context, because this in the constructor references the module itself. Now the constructor looks as shown in the following code snippet:

lbm.LearningTheme = function(map) {
  this._map = map;
  this._addCss();
  this._addHeader();
  this._bind(this);
};

To implement the handlers, we start with the _changeMapType:

_changeMapType: function(btn) {
  var options = this._map.getOptions();
  if (btn.innerHTML.match(/aerial/i)) {
    btn.innerHTML = 'Road';
    options.mapTypeId = Microsoft.Maps.MapTypeId.aerial;
  } else {
    btn.innerHTML = 'Aerial';
    options.mapTypeId = Microsoft.Maps.MapTypeId.road;
  }
  this._map.setView(options);
}

First, we cache the map's current options by calling the getOptions method. This allows us to change the options object and pass it back to the map later, without having to set all the options.

The handler then checks the text of the button, and it changes the mapTypeId option to the corresponding Microsoft.Maps.MapTypeId enum value. At the end it passes the options back to the map's setView method, which in return changes the map type.

If we open the index.html file on the browser, and click on the Aerial button, we see how the map changes from the road view to the aerial view. The button's text also changes to Road. Clicking on it again, will change its text back to Aerial and the map view to road. The AJAX Control adds a nice animation when changing the map view; it can be disabled by specifying animation: false in the options object.

The _zoom handler looks like this:

_zoom: function(btn) {
  var zoom = this._map.getZoom();
  if (btn.id === 'lbm-map-btn-zoomout') {
    zoom++
  } else if (btn.id === 'lbm-map-btn-zoomin') {
    zoom--
  }
  var options = this._map.getOptions();
  options.zoom = zoom;
  this._map.setView(options);
}

First, we get the map's current zoom value, by calling the getZoom method, and store it on a zoom variable. We then increase or decrease the value of this variable based on which button was clicked. The IDs we specified earlier for the buttons come in handy here, BECAUSE we can easily identify the event target by its DOM ID. Please note that the Microsoft.Maps API will ignore any zoom levels outside the established range between 1 and 20.

We then get the existing view options of the map and extend them with the updated zoom level. As in the previous handler, we pass them to the setView method of the map. After refreshing the index.html file on the browser, we see how our zoom buttons change the map's zoom level as we click on them.

 

Summary


The Bing Maps AJAX Control is a JavaScript library that adds mapping functionality to a web page. In this chapter, we've learned how to load a map on a page, and how easy it is to customize its appearance and functionality. We've looked at the map methods for getting and setting options, zoom, map type, and so on. We've also introduced the map events, and have added handlers to a custom map dashboard. In the next chapter, we will extend these events to draw polygons on the map.

About the Author

  • Artan Sinani

    Artan Sinani is a Web Developer living between London and a small town in the north-west of Spain. Currently, he works as a Senior Developer at Local Data Company, where he uses his passion for good design and clean programming to build beautiful interfaces, including geospatial data visualizations. He hacks open source code at github.com/artisinani and blogs at lugolabs.com.

    Browse publications by this author

Latest Reviews

(1 reviews total)
I have read few chapters, but so far it is a nice book.
Learning Bing Maps API
Unlock this book and the full library FREE for 7 days
Start now