Azure + Bing Maps: Working with Bing Maps in a rich HTML application

This is the 7th article in our "Bring the clouds together: Azure + Bing Maps"
series. You can find a preview of live demonstration on

https://sqlazurebingmap.cloudapp.net/. For a list of articles in the series,
please refer to

https://blogs.msdn.com/b/windows-azure-support/archive/2010/08/11/bring-the-clouds-together-azure-bing-maps.aspx.

Update: Now that Bing Maps AJAX SDK 7.0 is released, this post has been
updated to provide code for both SDK 6.3 and SDK 7.0. Our final sample code is
built for v7.0.


Introduction

Our previous posts focused on the services side. We've introduced how to
design a scalable cloud database, how to work with spatial data in SQL
Azure, how to access data using Entity Framework, and how to write data
centric web services using WCF Data Services.

In a team environment, multiple developers can work simultaneously. So
while data and service developers are working on the server side, it is
perfectly valid for a client developer to start working with client
applications.

This article targets client software developers who want to integrate
Bing Maps to their rich HTML applications. We assume the audiences have
no previous knowledge of Bing Maps SDK, but have basic knowledge of HTML
4, JavaScript, CSS, and jQuery. No HTML
5 knowledge is used.

We classify this post as an article for beginners. We'll briefly
introduce how to develop with Bing Maps SDK. After complete reading this
post, you will be able to display a map, invoke REST services to obtain
location information, and operate pushpins. To learn more advanced
topics, please refer to the
interactive SDK.
This post only targets HTML applications. We'll write how to integrate
Bing Maps with Silverlight and Windows Phone applications in future
posts.

Bing Maps SDKs

Bing Maps is a Software as a
Service (SaaS) offer from Microsoft. Both consumers and developers can use it.

For consumers, Bing Maps offers two versions. A HTML version which supports
basic map navigation and searching, and a Silverlight version which offers
smoother browsing experience and more third party applications integration.

For developers, there several versions of Bing Maps SDK:

  • Bing
    Maps AJAX Control
    , which targets rich HTML applications.

  • Bing
    Maps Silverlight Control
    , which targets desktop Silverlight applications
    running on Windows, Mac, or out of browser. Those are applications that are
    not submitted to bing.com.

  • Bing Maps Silverlight Control for Windows Phone, which is included in
    the

    Windows Phone Developer Tools. This is a slight variation of the desktop
    Silverlight version that adopts a smaller touch screen.

  • Bing Maps App
    SDK
    , which is currently in beta, allows third party developers to submit
    their applications directly to bing.com, instead of the developer's own
    server or another cloud offering such as Azure. After being reviewed,
    consumers can use those applications just as if they're part of Bing Maps
    itself.

  • Bing
    Maps SOAP Services
    , which can be used in any platforms that support SOAP
    1.1.

  • Bing
    Maps REST Services
    , which can be used in any platforms that support HTTP
    1.1.

  • Bing
    Maps Spatial Data Services
    , which allows client applications to
    calculate spatial data without a custom service application.

The SOAP/REST services offer the following features:

  • Geocode/Location Service, which is used to search a place's information
    (such as name and address) given the latitude/longitude, or the other way
    round.
  • Imagery Service, which returns individual images that composes a map.
    This service is usually used on platforms (such as WPF, native Windows
    applications, and Java desktop applications) that do not have a built-in map
    control.
  • Route Service, which helps to calculate a route between multiple stops.
  • Search Service, which is used to perform general searches. This is a
    SOAP only service.

As you can see, Bing Maps can be used in almost any platforms. If you're
working with HTML, Silverlight, or Windows Phone, you can take advantage of the
map controls which have done almost everything for you. If you're working with
other platforms, you can either embed a web browser in a client application, or
invoke the SOAP/REST services. The SOAP/REST services can be used in
HTML/Silverlight/Windows Phone as well, to obtain information that are not
included in the map controls.

Obtain a Bing Maps developer key

Most SDKs, although not all, require a developer key to use. It is free to
register a key. Just follow these steps:

  • Type an application name and a URL, choose the application type, and
    click Create key. You can type anything you like, including localhost.

Display a basic map

To display a map, follow these steps:

1. Create a container, which is usually a div. Give it an ID so we can
reference it later.

<div
id="MainMap"
style="width:
100%; height:
600px; position:
absolute" />

2. If you're using SDK 6.3, there's a compatibility issue between IE9 beta and Bing Maps
AJAX Control. So it is recommended to force IE9 to use IE8's standard to render
the document. This issue doesn't affect the AJAX map on bing.com. It also
doesn't affect Silverlight applications. If you're using SDK 7.0, IE9 beta
is supported, so you don't need to fallback to IE8's standard.

<meta
http-equiv="X-UA-Compatible"
content="IE=EmulateIE8"> 

3. Link to the SDK JavaScript code. Currently the highest SDK version is
v7.0. But you can also use v6.3 if you don't want to change existing code.

<script
type="text/javascript"
src="https://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.3"></script>   

<script
type="text/javascript"
src="https://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>

4. After the HTML document is loaded, we can intialize the map. create a new instance of the map object, set the credential (the key you registered
in the previous step), and load the map.

Note the initialization code for SDK 6.3 is different from 7.0.

If you're using SDK 6.3, create a new instance of VEMap, and pass the ID of
the container as a parameter. Then invoke SetCredentials to set the credential
(the key you registered in the previous step). Finally invoke LoadMap to load
the map. LoadMap takes an optional parameter representing the central location
(latitude/longitude) of the map.

If you're using SDK 7.0 create a new instance of Microsoft.Maps.Map. The
constructor itself accepts parameters representing the map's credential and
center point. So no additional method invoking is needed.

In SDK 7.0, you will encounter the namespace Microsoft.Maps in a lot of
places. So you may want to give it an alias:

var
Bing = Microsoft.Maps;

Later you can use new Bing.Map instead of new Microsoft.Maps.Map. This is
what we will use in the remaining of the post. But please do not confuse. The
actual namespace is Microsoft.Maps. Bing is just a short alias we use in the
sample.

Also note our application heavily uses jQuery. If
you haven't worked with jQuery before, it is recommended to get started from
https://docs.jquery.com/Tutorials.

var
mapCredential = 'change to your key';

var
map; 

$(document).ready(LoadMap);

// SDK
6.3:

function
LoadMap() {

map =
new VEMap('MainMap');

map.SetCredentials(mapCredential);

map.LoadMap(new
VELatLong(31, 121));

}

// SDK
7.0:

function
LoadMap() {

map =
new Bing.Map($('#MainMap')[0],

{

credentials: mapCredential,

center: new Bing.Location(31, 121)

});

}

Now a bare map will be displayed:

 

Attach event handlers

To attach event handlers to the map control, you invoke the AttachEvent
method in SDK 6.3, or invoke Microsoft.Maps.Events.addHandler in SDK 7.0. Note
certain event names are different for different SDKs.

// SDK
6.3:   

map.AttachEvent('onmousedown',
Map_OnMouseDown);

map.AttachEvent('onmouseup',
Map_OnMouseUp);

// SDK
7.0:

Bing.Events.addHandler(map,
'mousedown', Map_OnMouseDown);

Bing.Events.addHandler(map,
'mouseup', Map_OnMouseUp);

There're a bunch of events on the map class you can handle. Please refer to

https://msdn.microsoft.com/en-us/library/bb412543.aspx (SDK 6.3) and

https://msdn.microsoft.com/en-us/library/gg427609.aspx (SDK 7.0) for the complete list.
But be aware, while there is an onclick event, it will be fired whenever the
mouse is clicked. That is, if you hold down the mouse, pan the map, and then
release the mouse, this event will also be fired. In certain cases, this may not
be the behavior you want. For example, you may wish to add a pushpin only if the
user clicks the map without panning around. In such cases, you need to handle
onmousedown/up events directly, and write logic to figure out if the mouse has
moved.

// SDK
6.3:

var
mouseDownLocation;

function
Map_OnMouseDown(e) {

mouseDownLocation =
new VEPixel(e.mapX, e.mapY);

}

 

function
Map_OnMouseUp(e) {

var pixel = new
VEPixel(e.mapX, e.mapY);

// Only add a pushpin if the user is not panning
the map.

if (mouseDownLocation !=
null && mouseDownLocation.x == pixel.x &&
mouseDownLocation.y == pixel.y) {

// Do something…

}

}

// SDK
7.0:

function
Map_OnMouseDown(e) {

mouseDownLocation =
new Bing.Point(e.pageX, e.pageY);

}

 

function
Map_OnMouseUp(e) {

var pixel = new
Bing.Point(e.pageX, e.pageY);

// Only add a pushpin if the user is not panning
the map.

if (mouseDownLocation !=
null && mouseDownLocation.x == pixel.x &&
mouseDownLocation.y == pixel.y) {

// Do something…

}

}

Invoke Location Service

When the user clicks the map, we will add a pushpin on the clicked location.
This operation doesn't require a round trip to the service, because you can use
the PixelToLatLong (SDK 6.3) or tryPixelToLocation (SDK 7.0) method to obtain the clicked point's location, which is
enough to create a pushpin. However, our application requires more than the
location's latitude and longitude. We also need at least the name (place) of the
clicked location. This must be done with a service call.

Since we're working with an HTML application, the simplest solution is to use
the REST services, which supports both XML and JSON formats. We'll use JSON here
since we're using JavaScript and jQuery. To obtain detailed information of a
location given the latitude and longitude, we can use the Geocode/Location
Service. There're several usages of this service. If you know the
latitude/longitude, the URI of the service is:

https://dev.virtualearth.net/REST/v1/Locations/\[latitude\],\[longitude\]?key=\[your
key]

You can also specify additional query strings such as o (format) and jsonp
(to workaround the cross domain issue).

// SDK
6.3:

var latLong = map.PixelToLatLong(pixel);

// SDK
7.0:

var latLong = map.tryPixelToLocation(pixel,
Bing.PixelReference.page);       

//
Common:

// Invoke the Location REST service to
obtain information of the clicked place.

$.ajax(

{

url:
'https://dev.virtualearth.net/REST/v1/Locations/'
+ latLong.Latitude + ',' + latLong.Longitude
+ '?o=json&jsonp=LocationCallback&key=' +
mapCredential,

dataType:
'jsonp',

jsonp:
'LocationCallback',

success:
LocationCallback

});

The response looks like the following:

LocationCallback(

{

"authenticationResultCode":"ValidCredentials",

"brandLogoUri":"http:\/\/dev.virtualearth.net\/Branding\/logo_powered_by.png",

"copyright":"Copyright
© 2010 Microsoft and its suppliers. All rights reserved. This API cannot be
accessed and the content and any results may not be used, reproduced or
transmitted in any manner without express written permission from Microsoft
Corporation.",

"resourceSets":[

{

"estimatedTotal":1,

"resources":[

{

"__type":"Location:http:\/\/schemas.microsoft.com\/search\/local\/ws\/rest\/v1",

"bbox":[35.885187361790223,139.56395563345109,35.892912796931576,139.57666936654891],

"name":"Saitama",

"point":

{

"type":"Point",

"coordinates":[35.8890500793609,139.5703125]

},

"address":

{

"adminDistrict":"Saitama",

"countryRegion":"Japan",

"formattedAddress":"Saitama"

},

"confidence":"Medium",

"entityType":"AdminDivision1"

}]

}],

"statusCode":200,

"statusDescription":"OK",

"traceId":"c1d54e51791b465ca0ebc34e04d34618|BAYM001224|02.00.147.700|BY2MSNVM001066,
BY2MSNVM001087"

})

 

As you can see, the response may contain multiple results (resources) that
are located near the provided latitude/longitude. Our application only cares
about the first resource. Each resource contains multiple information, such as
the name of the location, and the exact latitude/longitude. Those are the
information we need for our application.

function
LocationCallback(result) {

if (result.resourceSets.length > 0) {

var resourceSet =
result.resourceSets[0];

if (resourceSet.resources.length > 0) {

var resource = resourceSet.resources[0];

 

// Add a pushpin.

// SDK
6.3:

var point =
new VEShape(VEShapeType.Pushpin, new
VELatLong(resource.point.coordinates[0], resource.point.coordinates[1]));

point.SetTitle(resource.name);

map.AddShape(point);

// SDK 7.0:

var pushpin =
new Bing.Pushpin(new
Bing.Location(resource.point.coordinates[0], resource.point.coordinates[1]));

map.entities.push(pushpin);

}

}

}

For more information about the response format, please refer to

https://msdn.microsoft.com/en-us/library/ff701710.aspx.

Create a shape and add it to the map

As demonstrated in the above code, to add a pushpin to the map, first create
a VEShape (SDK 6.3) or Microsoft.Maps.Pushpin (SDK 7.0), and then invoke VEMap.AddShape
or Microsoft.Maps.Map.entities.push. There're 3 kinds of shapes: pushpin
(a single point), polyline, and polygon. Our application only uses pushpin. But
it is also very easy to add a polyline and a polygon. Simply provide multiple
points. And in SDK 7.0, choose a different class. Take the sample from

https://msdn.microsoft.com/en-us/library/bb877865.aspx:

var points = new Array(

new VELatLong(45.01188,-111.06687, 0, VEAltitudeMode. RelativeToGround),

new VELatLong(45.01534,-104.06324, 0, VEAltitudeMode. RelativeToGround),

new VELatLong(41.01929,-104.06, 0, VEAltitudeMode. RelativeToGround),

new VELatLong(41.003,-111.05878, 0, VEAltitudeMode. RelativeToGround)

);

var myPolygon = new VEShape(VEShapeType.Polygon, points);

var myPolygon = map.AddShape(myPolygon);

 

In SDK 6.3, you can also set a title for the shape by invoking the SetTitle method, which
will be displayed when the user hovers mouse over the shape.

In SDK 7.0, this behavior is not provided out of box. So we have to it
ourself. Since JavaScript is a dynamic language, we can add properties
dynamically to an object. For example, you can write pushpin.title =
resource.name;, even if the Pushpin class doesn't have a title property defined.

To display a popup, you're required to create the html elements, and write
some code to display the elements. Here we provide a simple implementation.
First we put a div inside the map:

<div
id="MainMap"
style="width:
100%; height:
600px; position:
absolute">

<div
id="PushpinPopup">

<img
src="Images/Cloud
Dialog.png" alt="Pushpin
Popup" style="position:absolute"/>

<div
id="PushpinText"></div>

</div>

</div>

Then set the styles for the elements. Note the popup's z-index is set to a
very large value, so the map images won't cover it.

#PushpinPopup

{

z-index: 10000;

position: absolute;

opacity: 0;

width: 200px;

text-align: center;

}

 

#PushpinText

{

position: relative;

margin-top: 20px;

}

Now we can handle the pushpin's mouseover/out events, and display/hide the
popup:

Bing.Events.addHandler(pushpin, 'mouseover',
Pushpin_MouseOver);

Bing.Events.addHandler(pushpin, 'mouseout',
Pushpin_MouseOut);

 

function
Pushpin_MouseOver(e) {

$('#PushpinText').text(e.target.title);

var pushpinPopup = $('#PushpinPopup');

var pixel =
map.tryLocationToPixel(e.target.getLocation(), Bing.PixelReference.control);

pushpinPopup.css('left',
pixel.x - 100);

pushpinPopup.css('top',
pixel.y);

pushpinPopup.animate({
opacity: 1 });

}

function
Pushpin_MouseOut() {

$('#PushpinPopup').animate({
opacity: 0 })

}

Conclusion

This post provided an introduction to Bing Maps AJAX Controls and Bing Maps
REST Services. As you can see, it is very easy to use Bing Maps in your own
application. Most of the code described in this post uses standard JavaScript
and jQuery. If you wish to learn more, please refer to the documentation at

https://msdn.microsoft.com/en-us/library/dd877180.aspx, and the interaction
SDK at
https://www.microsoft.com/maps/isdk/ajax/.

The next post will introduce
jQuery Templates,
a joint investment of Microsoft and jQuery. You'll learn how it helps you to
display structured data (the travel stop list).