As many developers using the Bing Maps Platform know, some Bing Maps API calls generate billable transactions while other Bing Maps API calls don’t. The Bing Maps Platform team has documented which Bing Maps API calls are billable vs. non-billable (and when) here. As a side note, understanding and accessing your Bing Maps usage reports was previously discussed in the Bing Maps blog post here. Please refer to that blog post for info on accessing and understanding your Bing Maps transaction and session usage.
An important thing to understand when it comes to what causes billable vs. non-billable transactions is whether the API call was done within a Bing Maps ‘session’. A session begins anytime the Bing Maps AJAX Control, Bing Maps Silverlight Control, Bing Maps WPF Control or Bing Maps Windows Phone Control is loaded. For example, if you were to first load any of these Map Controls, then subsequently call the Bing Maps REST Locations API (i.e. to geocode a location), that REST Locations API call would generate a non-billable transaction. This is because the REST Locations API call was done within the Map Control session.
This is fine if your application loads the Map Control prior to making additional Bing Maps API calls (i.e. a REST Locations API call). However, what if you first wanted to call the REST Locations API prior to loading the map? In such a scenario, the REST Locations API call would be deemed billable because it occurred before the Map Control was loaded (and thus before the session started). The sequence of events is obviously important here when it comes to what constitutes a billable transaction vs. non-billable transaction. This goes for several other Bing Maps API calls (i.e. Bing Maps REST Routes call, Bing Maps Spatial Data Service Query call, Bing Maps SOAP Service Search call, etc.).
With this in mind, and to ensure that the Bing Maps API call (i.e. REST Locations API call) is done within a map session, you need to be sure to load the Map Control prior to making the REST Locations API call. What if you don’t actually want to display the map until after the REST Locations API call response comes back? One trick you can use to accomplish this is to hide the map after the Map Control loads. Here is an example using the Bing Maps AJAX Control v7 and the Bing Maps REST Locations API:
function getMap() {
map = new Microsoft.Maps.Map(document.getElementById('myMap'), { credentials: 'BingMapsKey' });
document.getElementById('myMap').style.display = 'none';
}
...
function showMap()
{
Microsoft.Maps.Events.removeHandler({handleId:viewChangedHandleId});
document.getElementById('myMap').style.display = 'block';
}
You can then display the map immediately after the REST Locations API call response is completed.
The full code for this example is as follows:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Find a location by query</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<script type="text/javascript">
var map = null;
var query;
var viewChangedHandleId;
function getMap() {
map = new Microsoft.Maps.Map(document.getElementById('myMap'), { credentials: 'BingMapsKey' });
document.getElementById('myMap').style.display = 'none';
}
function showMap() {
Microsoft.Maps.Events.removeHandler({ handleId: viewChangedHandleId });
document.getElementById('myMap').style.display = 'block';
}
function findLocation() {
query = 'Redmond, WA';
map.getCredentials(callSearchService);
}
function callSearchService(credentials) {
var searchRequest = 'http://dev.virtualearth.net/REST/v1/Locations/' + query + '?output=json&jsonp=searchServiceCallback&key=' + credentials;
var mapscript = document.createElement('script');
mapscript.type = 'text/javascript';
mapscript.src = searchRequest;
document.getElementById('myMap').appendChild(mapscript)
}
function searchServiceCallback(result) {
var output = document.getElementById("output");
if (output) {
while (output.hasChildNodes()) {
output.removeChild(output.lastChild);
}
}
var resultsHeader = document.createElement("h5");
output.appendChild(resultsHeader);
if (result &&
result.resourceSets &&
result.resourceSets.length > 0 &&
result.resourceSets[0].resources &&
result.resourceSets[0].resources.length > 0) {
resultsHeader.innerHTML = "Bing Maps REST Search API <br/> Found location " + result.resourceSets[0].resources[0].name;
var bbox = result.resourceSets[0].resources[0].bbox;
var viewBoundaries = Microsoft.Maps.LocationRect.fromLocations(new Microsoft.Maps.Location(bbox[0], bbox[1]), new Microsoft.Maps.Location(bbox[2], bbox[3]));
viewChangedHandleId = Microsoft.Maps.Events.addHandler(map, 'viewchangeend', showMap);
map.setView({ bounds: viewBoundaries });
var location = new Microsoft.Maps.Location(result.resourceSets[0].resources[0].point.coordinates[0], result.resourceSets[0].resources[0].point.coordinates[1]);
var pushpin = new Microsoft.Maps.Pushpin(location);
map.entities.push(pushpin);
}
else {
if (typeof (response) == 'undefined' || response == null) {
alert("Invalid credentials or no response");
}
else {
if (typeof (response) != 'undefined' && response && result && result.errorDetails) {
resultsHeader.innerHTML = "Message :" + response.errorDetails[0];
}
alert("No results for the query");
}
}
}
</script>
</head>
<body onload="getMap();">
<div id='myMap' style="position:relative;width:400px; height:400px;" ></div>
<div>
<input type="button" value="Geocode Location" onclick="findLocation();" />
</div>
<div id="output"></div>
</body>
This technique can be used for any of the Bing Maps API’s listed here that are considered non-billable if executed within a map session.