Quantcast
Channel: Maps Blog
Viewing all 102 articles
Browse latest View live

Geocoding a SharePoint List Item

$
0
0

Bing Maps is often integrated into SharePoint sites to display different types of information, from the location of customers to advance business intelligence information. A common task that needs to be carried out at some point is geocoding the location data so it can be displayed on a map. I’ve seen many different implementations, some good and others not so much. Whenever possible you should try and geocode the data ahead of time and store the coordinates. This will improve performance and generate a lot less transactions against your Bing Maps account.

In this blog post we are going to create a SharePoint List and add the functionality to geocode an item while also updating the coordinates when editing. This will allow us to store the coordinate information right in the SharePoint List.

Creating the SharePoint List

Before we start diving into the geocoding logic we will first need to create a SharePoint List and add a geocoding button to the item form. To do this, follow these steps:

1) Create a custom list called MyLocations.

image

2) Once the new list should appears go to the “List Tools” tab and select “List Settings”.

image
3) In the settings page go to the Column section and use the “Create Column” option to create the following columns:

Column Name

Column Type

Location

Single line of text

Latitude

Number (1, 1.0, 100)

Longitude

Number (1, 1.0, 100)

 
4) When you are done the columns section of the list settings should look like this:

image
 
5) Now go back to the List and under the “List Tools” tab select the “Customize Form” button.

image

6) This will open up the item input form with Microsoft InfoPath and will look something like this:

image

7) Next, select the area just right of the textbox for the Location field. Expand the controls menu from the tool bar and insert a button. This will insert a button right under the textbox.

8) Right click the button and go to “Button properties”. Change the label to “Geocode”. Under the Advance tab set the ScreenTip property to “Geocode”

image

9) Edit the properties for the Location, Latitude and Longitude textboxes and under the advance tab give the ScreenTip property the same name as the textbox. Then publish the form. Go back to the list and open the form to add a new item. It will look like the image below. Note that clicking on the geocode button won’t do anything right now.

image

Adding in the Geocoding functionality

To make things easy, we are going to handle all the geocoding functionality from JavaScript. To make this reusable, we will create a JavaScript file that we can pull into the item forms for any list that has the required fields.

1) Open up notepad or the text editor of your choice and copy the following code into it. Be sure to add your Bing Maps key. Save the file as ListGeocoder.js. This code uses the Bing Maps REST geocoding service to geocode the Location field and populates coordinates of the first result into the latitude and longitude fields of our item.

 

var GeocodeTools = {
BingMapsKey : "YOUR_BING_MAPS_KEY",

getField : function (fieldType,fieldTitle) {
var docTags = document.getElementsByTagName(fieldType);
for (var i=0; i < docTags.length; i++) {
if (docTags[i].title == fieldTitle) {
return docTags[i]
}
}
},

GeocodeLocation : function() {
var locField = GeocodeTools.getField('input','Location');
if(locField && locField.value && locField.value != ''){
var geocodeRequest = "http://dev.virtualearth.net/REST/v1/Locations?query=" + encodeURI(locField.value) + "&output=json&jsonp=GeocodeTools.UpdateFields&key=" + GeocodeTools.BingMapsKey;
GeocodeTools.CallRestService(geocodeRequest);
}else{
alert('Invalid \'Location\' value.');
}
},
UpdateFields : function(result){
if (result &&
result.resourceSets &&
result.resourceSets.length > 0 &&
result.resourceSets[0].resources &&
result.resourceSets[0].resources.length > 0)
{

var latField = GeocodeTools.getField('input','Latitude');
var lonField = GeocodeTools.getField('input','Longitude');

if(latField && lonField){
latField.value = result.resourceSets[0].resources[0].point.coordinates[0] + '';
lonField.value = result.resourceSets[0].resources[0].point.coordinates[1] + '';
}
}
},

CallRestService : function (request)
{
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", request);
document.body.appendChild(script);
}
};

window.onload = function(){
setTimeout(function(){
var geocodeBtn = GeocodeTools.getField('input','Geocode');
if(geocodeBtn != null){
geocodeBtn.onclick = function(){ GeocodeTools.GeocodeLocation(); };
}
},500);
};

2) Upload this to your SharePoint site by going to Site Actions -> View All Site Content. Under the Document Libraries section choose “Site Assets” and add the ListGeocoder.js file there.

3) Once the file is uploaded, right click on it and select “Copy Shortcut”. This will get the URL to the file which you will need later.

4) Go back to your SharePoint list and under the List Tools click on the “Form Web Parts” dropdown and select “(Item) New Form”.

image

5) This will open up a page where we can further edit the form. Under the Page tools tab choose the option to insert a Web Part. From the panel that appears select the “Forms” category and the “HTML Form Web Part” 

image

6) The new web part will appear above the item form and consist of a textbox and a button. Drag this web part so that it is below the input form. Then choose the option to edit this web part. 

image

7) Press the Source Editor button. This will open up a panel that has some HTML markup. Remove all the HTML and add the following script tag information, be sure to insert the proper URL to the ListGeocoder.js file you uploaded earlier.

image

8) Go back to the SharePoint List and repeat steps 4 to 7 for the “(Item) Edit Form” web part.

Try it out

At this point we have all the functionality we need to geocode a list item when adding or editing and item. To test it out go to the SharePoint list and select “Add new item”. In the form that opens give the item a title and set the Location property to “New York” and press the geocode button. The Latitude and Longitude values will become filled with numbers and look something like this:

image

Now save the item and choose the item you just created from the list and edit it. Change the location to “London” and press the geocode button. The latitude and longitude fields will update with new coordinates. If they don’t, ensure you added the HTML form web part to the Edit form.

This method of geocoding data in SharePoint is useful for scenarios where your users are manually entering data through the SharePoint portal. There may be situations where you want to geocode a large quantity of locations at once, perhaps a database of locations. In this case you will want to use the batch geocoding functionality in the Bing Spatial Data Services. This allows you to geocode up to 200,000 addresses in a single request and will take a fraction of the time it would take if you made multiple requests to the standard REST geocoding service. As an additional bonus, the batch geocoding service is a free service to all Bing Maps Enterprise customers. If you have a large number of entries being added to a list, you can leverage another approach to geocode the list by creating a workflow that runs after the list has been fully updated and batch geocodes all the ungeocoded locations in the list.

- Ricky Brundritt, EMEA Bing Maps Technology Solution Professional


New Traffic Manager in AJAX v7

$
0
0

Bing Maps AJAX v7 control can now show traffic incidents in addition to traffic flow and gives you the control to show or hide both with the new TrafficManager. Check out the following view of Seattle traffic flow and incidents. Hovering over an incident bring up details including time and duration. You can find the sample that created this view and more details on the TrafficManager page.

clip_image001

Note: The TrafficManager class replaces and enhances the functionality provided by the TrafficFlow class which is now deprecated, but will continue to be supported.

Connecting a SharePoint List to Bing Maps

$
0
0

I have seen a lot of different ways to integrate Bing Maps with SharePoint. Some more complex than others. In this blog post we will look at an easy way to integrate Bing Maps with SharePoint and connect to a SharePoint list. Many of the things shown in this blog post are not limited to SharePoint Server and can extend to SharePoint Online and Office 365. To give us a jump start, we will reuse the SharePoint list we created in a previous blog titled Geocoding a SharePoint List Item. The content of this post is from a recent webcast I gave and can access online here.

Adding Bing Maps to SharePoint

Before we dive into the ‘how to’ we will first demonstrate how simple it is to get Bing Maps into SharePoint. To start off, go to the Bing Maps V7 Interactive SDK and go to the Traffic Module sample.

clip_image002

If you press the View HTML button on the bottom right corner you can get the full HTML for the example.

clip_image004

Simply open up notepad or any other text editing tool, then copy and paste this code into it. Finally, save the file as TrafficMap.html. You may also want to add your Bing Maps key to the code to remove the authentication error message that appears on the map. Now open up your SharePoint site and go to your Site Assets folder.

clip_image006

Take the traffic map webpage you just created and upload it.

clip_image008

Once the file is uploaded open up the traffic map in new window to test it out and to get the URL to the file. The webpage should look something like this:

clip_image010

Next, let’s will create a new page in our SharePoint site called MyLocationMap.

clip_image012

Once you have your page, insert a Page Viewer web part. You can find this under the Media and Content category.

clip_image014

Now open the tool pane of the Page Viewer web part you just created and add the URL to the traffic map file as the Link property. If you want you can also give the web part a title like “Traffic Flow”.

clip_image016

When you are done, save the settings and page should refresh and look something like this:

clip_image018

Essentially, the Page Viewer web part has iframed the traffic map web page into the SharePoint site. With only a few minutes of work we were able to easily integrate a simple Bing Maps application with SharePoint.

Integrating with a SharePoint List

There are a few ways to connect to a SharePoint list from code. The two main ways are: use the ECMAScript in SharePoint which has a library for accessing list data or; use the List REST services which exist for all SharePoint lists. In this post we are going to use the ECMAScript method. Here is a good resource on how to Retrieve Lists Using JavaScript in SharePoint. If you would rather use the SharePoint List REST services take a look at this documentation.

To make this code sample more reusable, we will add support for a query string parameter called ListTitle which will allow us to specify which list we want to connect directly from the URL. As long as the list has a Title, Description, Latitude and Longitude columns this code sample will work. We will also add an infobox control to the map to display the location details when a pushpin is clicked.

Since we are essentially iframing our web page into a SharePoint page, we will have to call the parent of the iframe to get access to the SharePoint ECMAScripts. Normally this would cause a security exception; however, since our webpage is hosted within the same domain as our SharePoint page, this is allowed. To get access to the parent of the iframe we simply need to call functions in the part using window.parent.

Open up notepad or any other text editor and add the following code into it. Save the file as MyLocationListMap.html.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

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

<script type="text/javascript">
var map, infobox, dataLayer, ctx, listItems, listTitle;

function getMap()
{
map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
credentials: 'YOUR_BING_MAPS_KEY'
});

dataLayer = new Microsoft.Maps.EntityCollection();
map.entities.push(dataLayer);

var infoboxLayer = new Microsoft.Maps.EntityCollection();
map.entities.push(infoboxLayer);

infobox = new Microsoft.Maps.Infobox(new Microsoft.Maps.Location(0, 0), {
visible: false,
offset: new Microsoft.Maps.Point(0, 20)
});
infoboxLayer.push(infobox);

listTitle = getQueryStringValue('ListTitle');

if(listTitle){
window.parent.ExecuteOrDelayUntilScriptLoaded(loadList, 'sp.js');
}
}

function loadList(){
getListItems(listTitle);
}

function getListItems(listTitle){
ctx = new window.parent.SP.ClientContext.get_current();

var targetList = ctx.get_web().get_lists().getByTitle(listTitle);
var query = window.parent.SP.CamlQuery.createAllItemsQuery();

listItems = targetList.getItems(query);
ctx.load(listItems);

ctx.executeQueryAsync(onLoadItemsSuccess, onLoadItemsFail);
}

function onLoadItemsFail(sender, args) {
alert('Failed to get lists items. Error:' + args.get_message());
}

function onLoadItemsSuccess(sender, args) {
var listEnumerator = listItems.getEnumerator();

var item, pin, loc;

while (listEnumerator.moveNext()) {
item = listEnumerator.get_current();
loc = new Microsoft.Maps.Location(item.get_item("Latitude"), item.get_item("Longitude"));
pin = new Microsoft.Maps.Pushpin(loc);
pin.Metadata = {
Title : item.get_item("Title"),
Location : item.get_item("Location")
};
Microsoft.Maps.Events.addHandler(pin, 'click', displayInfobox);
map.entities.push(pin);
}
}

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

function getQueryStringValue(key) {
var queryString = document.URL.split("?");
if(queryString && queryString.length> 1)
{
var params = queryString[1].split("&");

for (vari= 0;i<params.length;i = i + 1) {
var value = params[i].split("=");
if (value[0] == key){
return value[1];
}
}
}

return null;

}

window.parent.onload= getMap;
</script>
</head>
<body>
<div id='myMap' style="position:relative;width:100%;height:450px;"></div>
</body>
</html>

What does this code actually do? The getMap function loads the map and adds two layers to the map; one for the data and the other for the infobox. This is based on a blog post I created on displaying Multiple pushpins and Infoboxes in Bing Maps V7. It then gets the ListTitle query string value and then executes the ExecuteOrDelayUntilScriptLoaded function in the parent page which will execute LoadList function in our code after the sp.js file has finished loading in the main page. This ensures that the ECMAScripts we need are loaded. When the LoadList function is called it takes the ListTitle query string value and passes it to the getListItems function. The getListItems function gets the current client context of the SharePoint site and creates a query to get all items in the list. It then executes the list and if successful calls the onLoadItemsSuccessful function which loops through the list items and creates pushpins and overlays them on the map. If the query failed to load the list items an alert is displayed to user.

Now, take the MyLocationListMap.html file and upload it into your Site Assets folder. Get the URL to the page.

SP_Assets2

Navigate back to the MyLocationMap page in your SharePoint site and open the tool pane to the Page Viewer web part we added earlier. Update the Link property with the URL to the MyLocationListMap.html file and change the title to “My Location Map” then save the changes. The page will refresh and look something like this once you click one of the pushpins.

image

Tip for SharePoint 2013 Users

SharePoint 2013 introduces a new field type named Geolocation that enables you to create SharePoint lists with location information. In Geolocation columns, you can enter location information as a pair of latitude and longitude coordinates in decimal degrees or retrieve the coordinates of the user’s current location from the browser if it implements the W3C Geolocation API. In the list, SharePoint 2013 displays the location on Bing Maps. In addition, a new view named Map View displays the list items as pushpins on a Bing Maps. Together, the Geolocation field and the Map View enable you to give a spatial context to any information by integrating data from SharePoint into a mapping experience, and let your users engage in new ways in your web and mobile apps and solutions. Also, its worth mentioning that SharePoint sites are generally internal applications and would require an Enterprise Bing Maps key. Take a look at the Integrating location and map functionality in SharePoint 2013 documentation for more information.

Here is how the out of the box map looks when you hover over a single list item that has location information.

SP2013_Item

Here is what the new Map View looks like:

SP2013_listItems

This is a great first step for anyone wanting to add Bing Maps to their SharePoint site. Note: the out of the box functionality is limited to this. If you want to customize the map or add complex shapes (like polygons), you will need to do some additional custom development. The steps outlined earlier in this post can be used to do just that. There are also a lot of really great web parts available on CodePlex which you could use as a starting point.

- Ricky Brundritt, EMEA Bing Maps Technology Solution Professional

Data source staging, rollback and more comes to Spatial Data Services

$
0
0

Bing Maps Spatial Data Services is now giving you even more options for managing and querying your data source entity data. Check out these new features.  

 

 

Example: This example shows how you can query around an address.

http://spatial.virtualearth.net/REST/v1/data/20181f26d9e94c81acdf9496133d4f23/FourthCoffeeSample/FourthCoffeeShops?spatialFilter=nearby('1Microsoft Way, Redmond, WA 98052',5)&$filter=IsWifiHotSpot%20eq%201&$select=*,__Distance&key=queryKey

Note that querying near an address with an address string will incur a geocoding usage transaction (RESTLocations) as well as a Query API transaction (RESTSpatialDataService:Query)

Bing Maps in AutoCAD 2014

$
0
0

We are excited to announce that Bing Maps is the default mapping service in Autodesk’s AutoCAD 2014 software. For those not familiar with AutoCAD, AutoCAD is Autodesk’s flagship software product and is the world’s most widely used 3D CAD (computer-aided design) software. Chances are that you use a product or have been in a building, public area or other development that was designed with AutoCAD. AutoCAD and AutoCAD vertical products such as AutoCAD Architecture and AutoCAD Civil 3D, are used in a wide range of industries and by architects, designers and engineers.

How is Bing Maps used in AutoCAD 2014 and the AutoCAD vertical products? AutoCAD now has a built-in Geolocation feature and allows users to create and enhance their designs with Bing Maps geocoding and map data. Bing Maps inside AutoCAD 2014 brings in location context and results in more accurate and richer designs. The key geolocation workflows in AutoCAD 2014 supported with Bing Maps include:

  • Locate your AutoCAD design in the real-world using Bing Maps geocoding service
  • View your AutoCAD design in the real-world high resolution aerial and satellite imagery
  • Aggregate and overlay 2D and 3D geolocated data with your AutoCAD designs
  • Locate yourself on your AutoCAD design in the field using GPS and Bing Maps

Below are some examples of Bing Maps aerial, satellite, road maps and geocoding services being used in the AutoCAD 2014 products.

Bing Maps aerial and satellite imagery maps:

1

Bing Maps road maps:

2

Bing Maps location geocoding:

3b

 

Here is an example of Bing Maps used in an AutoCAD 2014 design:

4

5

Also, here is a short video that demonstrates how real world location context can be brought into a design using the Bing Maps based Geolocation feature of AutoCAD 2014.

For more information on AutoCAD 2014, please visit autodesk.com/autocad.

HTML5 Canvas Pushpins in JavaScript

$
0
0

One of the more interesting features of HTML5 is the canvas elements. The canvas provides us with a feature rich, low level 2D rendering panel and is supported by all the major web browsers.

In this blog post, we are going to create a Bing Maps module that wraps the standard pushpin class to create a new HTML5 Canvas pushpin class. There are a lot of cool and interesting things we can do with the canvas that would require a lot more work using traditional HTML and JavaScript. By using this module, tasks such as rotating a pushpin or programmatically changing the color can be easily achieved. In this post I will use the Bing Maps V7 AJAX control, but all of the code can be easily reused with the Bing Maps for Windows Store Apps JavaScript control as well.

Creating the Canvas Pushpin module

In Bing Maps, pushpins have the ability to render custom HTML content. We can take advantage of this by passing in an HTML5 as the custom HTML5 content into the pushpin options. Since we cannot access this canvas until it has been rendered, we will want to create a CanvasLayer class which wraps the EntityCollection class and fires and event with an entity is added to render the data on our canvas. We can then create a CanvasPushpin class that takes two parameters: a location to display the canvas on the map and a callback function that will receive a reference to the pushpin and to the context of the HTML5 canvas. After this, we will be able to use the canvas context to draw our data. Take the following code for the module and copy it into a new file called CanvasPushpinModule.js and store it in a folder called scripts.

/***************************************************************
* Canvas Pushpin Module
*
* This module creates two classes; CanvasLayer and CanvasPushpin
* The CanvasLayer will render a CanvasPushpin when it is added
* to the layer.
*
* The CanvasPushpin creates a custom HTML pushpin that contains
* an HTML5 canvas. This class takes in two properties; a location
* and a callback function that renders the HTML5 canvas.
****************************************************************/
var CanvasLayer, CanvasPushpin;

(function () {
var canvasIdNumber = 0;

function generateUniqueID() {
var canvasID = 'canvasElm' + canvasIdNumber;
canvasIdNumber++;

if (window[canvasID]) {
return generateUniqueID();
}

return canvasID;
}

function getCanvas(canvasID) {
var c = document.getElementById(canvasID);

if (c) {
c = c.getContext("2d");
}

return c;
}

//The canvas layer will render a CanvasPushpin when it is added to the layer.
CanvasLayer = function () {
var canvasLayer = new Microsoft.Maps.EntityCollection();
Microsoft.Maps.Events.addHandler(canvasLayer, 'entityadded', function (e) {
if (e.entity._canvasID) {
e.entity._renderCanvas();
}
});
return canvasLayer;
};

CanvasPushpin = function (location, renderCallback) {
var canvasID = generateUniqueID();

var pinOptions = {
htmlContent: '<canvas id="' + canvasID + '"></canvas>'
};

var pin = new Microsoft.Maps.Pushpin(location, pinOptions);

pin._canvasID = canvasID;

pin._renderCanvas = function () {
renderCallback(pin, getCanvas(pin._canvasID));
};

return pin;
};
})();

// Call the Module Loaded method
Microsoft.Maps.moduleLoaded('CanvasPushpinModule');

To implement the module, we will need to load the module. Once it is loaded, we will want to create a CanvasLayer entity collection which we will add our canvas pushpins. Once this layer is created, we can create our CanvasPushpin objects and add them to the layer. We will use the following image and draw it onto the canvas.

clip_image002

green_pin.png

When we put this all together we end up with the following code:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>

<script type="text/javascript">
var map, canvasLayer;

function GetMap() {
// Initialize the map
map = new Microsoft.Maps.Map(document.getElementById("myMap"),
{
credentials: "YOUR_BING_MAPS_KEY"
});

//Register and load the Canvas Pushpin Module
Microsoft.Maps.registerModule("CanvasPushpinModule", "scripts/CanvasPushpinModule.js");
Microsoft.Maps.loadModule("CanvasPushpinModule", {
callback: function () {
//Create Canvas Entity Collection
canvasLayer = new CanvasLayer();
map.entities.push(canvasLayer);

//Create the canvas pushpins
createCanvasPins();
}});
}

function createCanvasPins() {
var pin, img;

for (var i = 0; i < 100; i++) {

//Create a canvas pushpin at a random location
pin = new CanvasPushpin(new Microsoft.Maps.Location(Math.random() * 180 - 90, Math.random() * 360 - 180), function (pin, context) {
img = new Image();
img.onload = function () {
if (context) {
context.width = img.width;
context.height = img.height;
context.drawImage(img, 0, 0);
}
};
img.src = 'images/green_pin.png';
});

//Add the pushpin to the Canvas Entity Collection
canvasLayer.push(pin);
}
}
</script>
</head>
<body onload="GetMap();">
<div id='myMap' style="position:relative;width:800px;height:600px;"></div>
</body>
</html>


By using the above code, we will end up with something like this:

canvasBasic

A more colorful pushpin

Using the canvas to render an image isn’t terribly exciting, especially when we could do the same thing by setting the icon property of the pushpin to point to the image and accomplish the same thing. One common question I’ve seen on the Bing Maps forums over the years is “How do I change the color of the pushpin?”. In the past the answer was always the same; create a new image that is a different color. In Bing Maps Silverlight, WPF and Native Windows Store controls we can change the color programmatically by simply setting the background color. When using this canvas pushpin module we can actually do the same thing. The first thing we will need is a base pushpin that has the color region removed and made transparent. Below is the image we will use in this example:

transparent_pin

transparent_pin.png

To get this to work nicely, we will want to provide each pin with a color in which we want it to render as. To do this, we can add a metadata property to each pushpin and store our color information in there. The color information itself is a string representation of a color that the HTML5 canvas can understand. This could be a HEX or RGB color. In this example, we will render random colors for each pushpin. When we go to render the pushpin we will want to draw a circle that is the specified color and then overlay our pushpin template overtop. Below is a modified version of the createCanvasPins method that shows how to do this.

function createCanvasPins() {
var pin, img;

for (var i = 0; i < 100; i++) {

//Create a canvas pushpin at a random location
pin = new CanvasPushpin(new Microsoft.Maps.Location(Math.random() * 180 - 90, Math.random() * 360 - 180), function (pin, context) {
img = new Image();
img.onload = function () {
if (context) {
//Set the dimensions of the canvas
context.width = img.width;
context.height = img.height;

//Draw a colored circle behind the pin
context.beginPath();
context.arc(13, 13, 11, 0, 2 * Math.PI, false);
context.fillStyle = pin.Metadata.color;
context.fill();

//Draw the pushpin icon
context.drawImage(img, 0, 0);
}
};

img.src = 'images/transparent_pin.png';
});

//Give the pushpin a random color
pin.Metadata = {
color: generateRandomColor()
};

//Add the pushpin to the Canvas Entity Collection
canvasLayer.push(pin);
}
}

function generateRandomColor() {
return'rgb(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ')';
}


Using the above code we will end up with something like this:

canvasColoredPins

Rotating a pushpin

Sometimes it is useful to be able to rotate your pushpin. One of the most common scenarios I have come across for this is to be able to draw arrows on the map that point in a specific direction. Using traditional HTML and JavaScript we would do this in one of two ways. The first method would be to create a bunch of images of arrows pointing in different directions and then choose which ever one is closest to the direction we want. The second method is a bit of a CSS hack involving the use of borders which I talked about in a past blog post. When using the HTML5 canvas, we can easily rotate the canvas by translating the canvas to the point we want to rotate about, then rotating the canvas, and translating the canvas back to the original position. One thing we need to take into consideration is that in most cases the dimension of the canvas will need to change as we rotate an image. Think about rotating a rectangle. When rotated by a few degrees, the area the rectangle requires to fit inside the canvas will be larger. When rotating objects on a map, it is useful to know that it is common practice for North to represent a heading of 0 degrees, East to be 90 degrees, South to be 180 degrees and West to be 270 degrees. In this example, we will take the following arrow image and provide and add metadata to the pushpin where we will specify the heading for the arrow to point. And to make things easy, we will make our arrow point up which aligns with the north direction and a heading of 0 degrees.

 

redArrow

redArrow.png

When we put this all together, we end up with the following code:

function createCanvasPins() {
var pin, img;

for (var i = 0; i < 100; i++) {

//Create a canvas pushpin at a random location
pin = new CanvasPushpin(new Microsoft.Maps.Location(Math.random() * 180 - 90, Math.random() * 360 - 180), function (pin, context) {
img = new Image();
img.onload = function () {
if (context) {
//Calculate the new dimensions of the the canvas after the image is rotated
var dx = Math.abs(Math.cos(pin.Metadata.heading * Math.PI / 180));
var dy = Math.abs(Math.sin(pin.Metadata.heading * Math.PI / 180));
var width = Math.round(img.width * dx + img.height * dy);
var height = Math.round(img.width * dy + img.height * dx);

//Set the dimensions of the canvas
context.width = width;
context.height = height;

//Offset the canvas such that we will rotate around the center of our arrow
context.translate(width * 0.5, height * 0.5);

//Rotate the canvas by the desired heading
context.rotate(pin.Metadata.heading * Math.PI / 180);

//Return the canvas offset back to it's original position
context.translate(-img.width * 0.5, –img.height * 0.5);

//Draw the arrow image
context.drawImage(img, 0, 0);
}
};
img.src = 'images/redArrow.png';
});

//Give the pushpin a random heading
pin.Metadata = {
heading: Math.random() * 360
};

canvasLayer.push(pin);
}
}

Using the above code we will end up with something like this:

canvasArrows

Data Driven pushpins

Bing Maps is often used in business intelligence applications. One particular type of business intelligence data that users like to overlay on the map are pie charts. The good news is that creating pie charts using a canvas is fairly easily. Each portion of a pie chart will be a percentage of the total of all the data in the pie chart. Let’s simply make use of the arc drawing functionality available within the canvas to do this.

function createCanvasPins() {
var pin;

//Define a color for each type of data
var colorMap = ['red', 'blue', 'green', 'yellow'];

for (var i = 0; i < 25; i++) {

//Create a canvas pushpin at a random location
pin = new CanvasPushpin(new Microsoft.Maps.Location(Math.random() * 180 - 90, Math.random() * 360 - 180), function (pin, context) {
//Calculate the number of slices each pie we can support
var max = Math.min(pin.Metadata.data.length, colorMap.length);

//Calculate the total of the data in the pie chart
var total = 0;
for (var i = 0; i < max; i++) {
total += pin.Metadata.data[i];
}

//Draw the pie chart
createPieChart(context, total, pin.Metadata.data, colorMap);
});

//Give the pushpin some data
pin.Metadata = {
data: generateRandomData()
};

//Add the pushpin to the Canvas Entity Collection
canvasLayer.push(pin);
}
}

function createPieChart(context, total, data, colorMap) {
var radius = 12.5,
center = { x: 12.5, y: 12.5 },
lastPosition = 0;

context.width = 25;
context.height = 25;

for (var i = 0; i < data.length; i++) {
context.fillStyle = colorMap[i];
context.beginPath();
context.moveTo(center.x, center.y);
context.arc(center.x, center.y, radius, lastPosition, lastPosition + (Math.PI * 2 * (data[i] / total)), false);
context.lineTo(center.x, center.y);
context.fill();
lastPosition += Math.PI * 2 * (data[i] / total);
}
}

function generateRandomData() {
return [Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100];
}

Using the above code we will end up with something like this:

canvasPieChart

If you wanted to take this module even further, you could create pushpins that are animated, or even simulate a 3D object.

- Ricky Brundritt, EMEA Bing Maps Technology Solution Professional

Upcoming Bing Maps Events - Spring 2013

$
0
0

Bing Maps Events

The following events have been organized by the Bing Maps team.

 

Bing Maps Windows Store Apps Developer Event

Date: May 10, 2013 @ 9:30am – 12:30pm

Location: Microsoft Cardinal Place, Auditorium, 100 Victoria Street, London SW1E 5JL

Bing Maps would like to invite you to our Windows Store Apps Developer Event. We have some great speakers lined up who will be showcasing some of the latest Apps developed using Microsoft technology whilst detailing the end to end technical build process.

Register: https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032547101&Culture=en-GB&community=0

 

The Power of Bing Maps & CRM

Date: May 10, 2013 @ 2:00pm – 5:00pm

Location: Microsoft Cardinal Place, Auditorium, 100 Victoria Street, London SW1E 5JL

Microsoft Bing Maps would like to invite you to our CRM & Mapping event. The objective of this event is to showcase how mapping can add value, driving greater insights from your CRM data. Not only will we be showcasing existing solutions, but also the business and technical advantages and opportunities that mapping and CRM can bring.

Register: https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032550691&Culture=en-GB&community=0

 

Attending Events

Bing Maps will have a presence at the following events.

 

SQLBits XI

Date: May 2nd- 3rd 2013

Location: East Midlands Conference Centre, Nottingham, UK

Register: http://sqlbits.com/

 

GeoDATA

Date: May 14, 2013

Location: Birmingham, UK

Register: http://www.geoinformationgroup.co.uk/training/geodata

 

Date: May 16, 2013

Location: Sheffield, UK

Register: http://www.geoinformationgroup.co.uk/training/geodata

 

Date: June 12, 2013

Location: Dublin, IE

Register: http://www.geoinformationgroup.co.uk/training/geodata

 

Date: June 25, 2013

Location: Amsterdam, NL

Register: http://www.geoinformationgroup.co.uk/training/geodata

 

Microsoft Worldwide Partner Conference

Date: July 7th– 11th 2013

Location: Houston, TX

Register: http://www.digitalwpc.com/Pages/Home.aspx

 

Recent Webcasts

Below are some of our most recent Bing Maps webcasts that may have slipped your calendar. You can sign-in with your Event Builder registration details to view the resources for each webcast.

Bing Maps for Windows Store App JavaScript Development

Native Bing Maps for Windows Store App Development

Microsoft Location/Geospatial Solutions for Fleet Tracking and Logistics

Bing Maps and Dynamics CRM Solutions

Bing Maps and Sharepoint Solutions for the Enterprise

Image Overlays with Bing Maps (JavaScript)

$
0
0

There are a lot of different types of data that can be overlaid on top of Bing Maps. In this post, we are going to look at how to overlay images on top of the Bing Maps V7 AJAX and Windows Store JavaScript controls. Over the years, I’ve seen a lot of great use cases for overlaying images on top of the map. Some of the more common use cases include: floor plans, satellite imagery of hurricanes or other natural disasters, historic maps, and other maps,

There are two main approaches for overlaying images on Bing Maps. The first approach is to turn the image into map tiles ahead of time and serve them up to the application as a custom tile layer. This has been a common approach for many and have accomplished this by using a tool called MapCruncher. This approach does require hosting the tiles on a server, which can be slow going due to the number of tile loads. This approach is ideal for when you want to overlay a high resolution image with a large file size. The second approach is to overlay the image directly on the map and lock it to a specific bounding box, scaling and positioning it accordingly as the map moves. With the Bing Maps Silverlight control we are able to overlay images on top of the map by simply adding the image as a child of a layer and providing a bounding box for the image. A sample of this can be found here. While this approach is much simpler than the tiled, it is not recommended for images that have large file sizes.

MapCruncher Approach

MapCruncher is a Microsoft Research project that makes it easy to cross reference an image with a location on a map and then turn the image into a tile layer. Instructions on how to use MapCruncher can be found here. By default, MapCruncher will generate a test application for you that is built on Bing Maps V6.3. For a better user experience you will want use the Bing Maps V7 AJAX control. To do this, simply locate the folder from the output directory that contains the map tiles. You will want to add these as a folder in your project on a server. You can then add it as a custom tile layer.

Scaling Image Approach

When the Bing Maps Silverlight control was released one of the new features we found was the ability to overlay any UIElement such as an image or video on top of the map and having it bound to a specific bounding box on the map. This feature was never available in the JavaScript version of Bing Maps, but has been something of interest to me.

Looking at the Bing Maps JavaScript controls we have the ability to overlay custom HTML pushpins. We can easily use a custom image to create a pushpin, but it will not be positioned or scaled properly without a little work. If we attached to the viewchangeevent of the map, and update the size and position of the image, we can properly bind it to a specific bounding box. To accomplish this, lets create a reusable module for Bing Maps.

Open up a text editor and copy and paste the following code and save it as ImageOverlayModule.js.

var ImageOverlay;

(function () {
var canvasIdNumber = 0;

function generateUniqueID() {
var canvasID = 'strechedImg' + canvasIdNumber;
canvasIdNumber++;

if (window[canvasID]) {
return generateUniqueID();
}

return canvasID;
}

// map - Microsoft.Maps.Map object
// imageURL - String URL to where the image is located
// boundingBox - Microsoft.Maps.LocationRect object
ImageOverlay = function (map, imageURL, boundingBox) {
var _basePushpin = new Microsoft.Maps.Pushpin(boundingBox.center);
var _opacity = 1;
var _id = generateUniqueID();

function render(){
var size = calculateSize();

var pushpinOptions = {
width: null,
height: null,
anchor: new Microsoft.Maps.Point(size.width/2, size.height/2),
htmlContent: "<img id='" + _id + "' style='width:" + size.width + "px;height:" + size.height + "px;opacity:" + _opacity + ";filter:alpha(opacity=" + (_opacity * 100) + ");' src='" + imageURL + "'/>"
};

_basePushpin.setOptions(pushpinOptions);
}

function calculateSize(){
var nwPixel = map.tryLocationToPixel(boundingBox.getNorthwest());
var sePixel = map.tryLocationToPixel(boundingBox.getSoutheast());

var width = Math.abs(sePixel.x - nwPixel.x);
var height = Math.abs(nwPixel.y - sePixel.y);

return {
width: width,
height: height
};
}

_basePushpin.Refresh = function () {
var size = calculateSize();

_basePushpin.setOptions({anchor : new Microsoft.Maps.Point(size.width/2, size.height/2)});

var elm = document.getElementById(_id);

if(elm){
elm.style.width = size.width + 'px';
elm.style.height = size.height + 'px';
}
};

_basePushpin.SetOpacity = function (opacity) {
if (opacity >= 0 || opctity <= 1) {
_opacity = opacity;
render();
}
};

//Map view change event to resize the image
Microsoft.Maps.Events.addHandler(map, 'viewchange', function (e) {
if (!e.linear) {
//Check if zoom level has changed. If it has then resize the pushpin image
_basePushpin.Refresh();
}
});

render();

return _basePushpin;
};
})();

//Call the Module Loaded method
Microsoft.Maps.moduleLoaded('ImageOverlayModule');

This module creates a class called ImageOverlay which we takes in a reference of the map, a URL to the image we want to overlay and a LocationRect of the bounding box to bind the image. The following is an example of how you can use this module with the Bing Maps V7 AJAX control.

Note: you could also use this module with the Bing Maps Windows Store JavaScript control.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>

<script type="text/javascript">
var map;

function GetMap() {
// Initialize the map
map = new Microsoft.Maps.Map(document.getElementById("myMap"),
{
credentials: "YOUR_BING_MAPS_KEY",
center: new Microsoft.Maps.Location(40.25, -123.25),
zoom: 7
});

//Register and load the Image Overlay Module
Microsoft.Maps.registerModule("ImageOverlayModule", "scripts/ImageOverlayModule.js");
Microsoft.Maps.loadModule("ImageOverlayModule", { callback: function () {
var imageRect = Microsoft.Maps.LocationRect.fromCorners(new Microsoft.Maps.Location(40.5, -123.5), new Microsoft.Maps.Location(40, -123));
var imgPin = ImageOverlay(map, 'images/topographicMap.gif', imageRect);
imgPin.SetOpacity(0.5);
map.entities.push(imgPin);
}});
}
</script>
</head>
<body onload="GetMap();">
<div id='myMap' style="position:relative;width:800px;height:600px;"></div>
</body>
</html>

For this example, I grabbed a topographic map image from Bing image search and gave it an approximate bounding box. NASA also has a lot of great imagery you can overlay that is geo-referenced. I’ve even seen some pretty cool animated gif’sthat would work too. When you run the application you should see your image overlaid on top of the map like this:

clip_image001

This module has also been made available through the Bing Maps V7 Modules CodePlex project.

- Ricky Brundritt, EMEA Bing Maps Technology Solution Professional


Venue Maps in Windows Store Apps (JavaScript)

$
0
0

Within Bing Maps you have the ability to view Venue Maps. Venue Maps are often thought of as maps of indoor structures such as malls and airports; however, venue maps can be opened up to so much more. A few examples of other types of venue maps include: the layout of shopping districts, stadiums, race courses, and universities.

Using Venue Maps in Bing Maps requires loading in the Venue Map module. If you are not familiar with modules in Bing Maps take a look at the blog post on Bing Maps Modules in Windows Store Apps.

In this blog post, we are going to create a simple application that when zoomed in, will load in nearby venues into Bing Maps in your Windows Store App as you pan the map around.

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. Select the Blank App template, and call the application VenueMaps_JS and press OK.

To keep things clean we are going to create a separate JavaScript file for our application. Right click on the js folder and select Add -> New Item. Create a new JavaScript file called VenueMaps.js.

At this point your Solution should look something like this:

 

clip_image001

 

Next, add a reference to the Bing Maps JavaScript library. To do this, right click on the References folder and press Add Reference. Select Windows -> Extensions select Bing Maps for JavaScript. If you do not see this option ensure that you have installed the Bing Maps Windows Store App SDK.

Now we can open up the default.html file and add references to the Bing Maps SDK along with references to the Bing Maps modules API and our JavaScript file. While we are at it, we will also add a div to the body of the page where we want the map control to load. Your default.html file should look like this:

 

<!DOCTYPEhtml>

<html>

<head>

    <metacharset="utf-8"/>

    <title></title>

 

    <!-- WinJS references -->

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

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

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

 

    <!-- VenueMaps_JS references -->

    <linkhref="/css/default.css"rel="stylesheet"/>

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

 

    <!-- Bing Maps references -->

    <scripttype="text/javascript"

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

    <scripttype="text/javascript"

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

 

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

    <scriptsrc="/js/VenueMaps.js"></script>

 

    <styletype="text/css">

        div {

            color:#000;

        }

    </style>

</head>

<body>

    <divid="myMap"></div>

</body>

</html>

Loading the Bing Maps Control

We will add the functionality to load the Bing Maps control to the VenueMap.js file. To start, we are going to create six global variables called: map, _mapBounds, _zoom, venueFactory, loadedVenues, and loadedVenueIds. Then, we will create a function called GetMap which will load the Bing Maps control for us. When loading the Bing Maps control we will also turn on the breadcrumb. The breadcrumb allows us to select different floors when viewing a venue. Inside of this function we will load in the Venue Maps module and initialize the venueFactory object. Now we will add a throttled event handler for when the map has finished moving that will get nearby venues. The GetMap function will be loaded after the Microsoft.Maps.Map module has completed loading. Your code should look like this:

 

var map,

    loadedVenues = [],

    loadedVenueIds = [],

    _mapBounds,

    _zoom,

    venueFactory;

 

function GetMap() {

    var mapOptions =

    {

        credentials: "YOUR_BING_MAPS_KEY",

        zoom: 2,

        showBreadcrumb : true

    };

 

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

 

    //Load the venue map module

    Microsoft.Maps.loadModule('Microsoft.Maps.VenueMaps', {

        callback: function () {

            venueFactory = new Microsoft.Maps.VenueMaps.VenueMapFactory(map);

 

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

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

        }

    });

}

 

//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 veiwchangeend event handler is fired, it will trigger a function called GetNearbyVenues. This function will first loop through all loaded venues and dispose those that are out of view or all of them if the zoom level is less than 14. When disposing a venue we will also delete the stored venue id in the loadedVenueIds array. If the zoom level is greater or equal to 14 we will do a search for all venues that are within 4000 meters of the center of the map.

 

function GetNearbyVenues() {

    _mapBounds = map.getBounds();

    _zoom = map.getZoom();

 

    //Dispose a venue if it is outside of the map view or if the zoom  level is too high

    var venuesInView = [], venue;

 

    for (var i = 0; i < loadedVenues.length; i++) {

        venue = loadedVenues[i];

 

        if (_zoom < 14 || !_mapBounds.contains(venue.center)) {

            delete loadedVenueIds[venue.id];

            venue.dispose();

        } else {

            venuesInView.push(venue);

        }

    }

 

    loadedVenues = venuesInView;

 

    if (_zoom >= 14) {

        // Search for venues that are within 4km of the center of the map

        venueFactory.getNearbyVenues({ map: map, location: map.getCenter(), radius: 4000, callback: DisplayNearbyVenues });

    }

}

 

When the search for nearby venues completes, a function called DisplayNearbyVenues will be fired. This function will loop through the venues that are returned and check if the venue has been loaded by checking the loadedVenueIds array. If the venue hasn’t been loaded we will create the venue and check to see if it is in the current map view and add both the venue and the venue id to the loadVenues and loadedVenueIds arrays. If it was loaded, we will then show it on the map. If it hasn't, we will dispose it. We will also attach a close event handler to the venue so that we can properly dispose it and remove any reference from our arrays.

 

function DisplayNearbyVenues(venues) {

    if (venues) {

        //Load new venues that are in view

        for (var i = 0; i < venues.length; i++) {

            if (loadedVenueIds[venues[i].metadata.MapId] == undefined) {

                venueFactory.create({

                    venueMapId: venues[i].metadata.MapId,

                    success: function (v) {

                        if (_mapBounds.contains(v.center)) {

 

                            //Handle close event of venue

                            Microsoft.Maps.Events.addHandler(v, 'close', function (e) {

                                delete loadedVenueIds[v.id];

                               

                                //Remove venue from loadedVenues array

                                for (var j = 0; j < loadedVenues.length; j++) {

                                    if (loadedVenues[j].id == v.id) {

                                        loadedVenues.splice(j ,ji+1);

                                        break;

                                    }

                                }

 

                                v.dispose();

                            });

 

                            loadedVenues.push(v);

                            loadedVenueIds[v.id] = true;

                            v.show();

                        } else {

                            v.dispose();

                        }

                    }

                });

            }

        }

    }

}

Running the App

At this point we have created all the functionality needed for nearby venues to be loaded as we navigate the map. If we run the app, zoom in and pan around in an area with the venue maps we should see the footprint of the venue load on the map. If you click on a venue footprint the map will zoom in on that venue and show the inside of the venue map. Here is a screen shot of the app with the venue map of Alder Hey Children’s Hospital located in Liverpool, UK.

 

Win8_Venue

- Ricky Brundritt, EMEA Bing Maps Technology Solution Professional

Geocoding and Routing in Bing Maps Windows Store Apps (JavaScript)

$
0
0

In this blog post we are going to look at how implement Geocoding and Routing using the Bing Maps Windows Store App JavaScript SDK. If you are new to JavaScript development with the Bing Maps Windows Store App SDK I recommend reading the Getting started with Bing Maps Windows Store Apps blog post first. We will also be making use of Bing Maps Modules. If you are unfamiliar with modules, I recommend reading the blog post: Modules in Bing Maps Windows Store Apps.

Geocoding is one of the most common tasks done by users of online maps. Geocoding is the process of taking an address or query and returning its equivalent coordinate on the map. Routing is the task of calculating the directions between two or more locations. In Bing Maps there are a lot of different options around routing such as routing by different modes of transportation; driving, walking, or transit. In Bing Maps there are several ways to carry out these tasks. The most common method is to use the Bing Maps REST services which are very easy to use from just about any programming language. In the JavaScript version of Bing Maps there a modules which wraps the REST services and exposes it as an easy to use JavaScript library. We will be making use of the Search module for geocoding and the Directions module for routing.

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. Select the Blank App template, call the application BingMapsSearch_WinRTJS and press OK.

To keep things clean, we are going to create a separate CSS Style Sheet and JavaScript file for our application. To do this, right click on the js folder and select Add -> New Item. Create a new JavaScript file called BingMapsSearch.js. Then, right click on the css folder and select Add -> New Item and create a new Style Sheet called BingMapsSearch.css.

At this point your Solution should look something like this:

Win8_Project_Geo

Next, you’ll want to add a reference to the Bing Maps JavaScript library. Right click on the References folder and press Add Reference. Then select Windows -> Extensions and select Bing Maps for JavaScript. If you do not see this option, you may want to verify that you have installed the Bing Maps for Windows Store App SDK .

We can now open up the default.html file and add references to the Bing Maps SDK along with references to our CSS Style Sheet and JavaScript file. To do this, simply add the following in the head of the page:

<!-- Bing Maps references -->

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

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

 

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

<linkhref="/css/BingMapsSearch.css"rel="stylesheet"/>

<scriptsrc="/js/BingMapsSearch.js"></script>


Creating the UI Layout

Before we worry about how to do all the programming of the logic required for this application, let’s first focus on creating the UI. For this app we are going to have a panel on the left side where all our input controls will reside and the map will fill up the rest of the space to the right. In the left frame we will have Clear Map button, an area for inputting geocode and route requests, and a div for rendering routing itineraries. The geocode input area will consist of a single textbox and a button. The routing input area will have two textboxes and a button. The HTML for the default.html file should look like this:

<!DOCTYPEhtml>

<html>

<head>

    <metacharset="utf-8"/>

    <title>BingMapsSearch_WinRTJS</title>

 

    <!-- WinJS references -->

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

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

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

 

    <!-- BingMapsSearch_WinRTJS references -->

    <linkhref="/css/default.css"rel="stylesheet"/>

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

 

    <!-- Bing Maps references -->

    <scripttype="text/javascript"

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

    <scripttype="text/javascript"

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

 

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

    <linkhref="/css/BingMapsSearch.css"rel="stylesheet"/>

    <scriptsrc="/js/BingMapsSearch.js"></script>

</head>

<body>

    <divclass="leftFrame">

        <!-- Clear Map Button-->

        <divstyle="height:30px;">

            <buttononclick="ClearMap();"class="greenBtn">Clear Map</button>

        </div>

 

        <!-- Geocoding Input -->

        <divstyle="height:100px;">

            <h2>Geocode</h2>

            <inputid="searchTbx"type="text"style="width:240px;"/>

            <buttononclick="Geocode();"class="greenBtn">Go</button>

        </div>

 

        <!-- Routing Input -->

        <divstyle="height:150px;">

            <h2>Route</h2>

            From: <inputid="fromTbx"type="text"style="width:200px;"/>

            To: <inputid="toTbx"type="text"style="margin-left:17px;width:200px;"/>

            <buttononclick="GetRoute();"class="greenBtn">Get Directions</button>

        </div>    

 

        <!-- Route Itinerary Area -->

        <divid="itineraryDiv"></div>

    </div>

 

    <!-- Map Area-->

    <divid="myMap"></div>

</body>

</html>

To keep things clean we will add some of the styling information into the BingMapsSearch.css file. Add the following to this file:

.leftFrame {

    position:absolute;

    height:100%;

    width:260px;

    padding:10px;

    background-color:#808080;

    overflow-y:auto;

}

 

.greenBtn {

    background-color:green;

    float:right;

    margin-right:15px;

}

 

#myMap {

    position:absolute;

    width:100%;

    height:100%;

    margin-left:280px;

}

 

#itineraryDiv {

    position:absolute;

    width:240px;

}

At this point we can run the application and see what it looks like. You should end up with something like this:

Win8_Geo_layout

Note that the map does not appear as we have not yet added the logic to load it. Also, since we haven’t wired up any of the functionality for the buttons, clicking on them now would throw an error.

Loading the Bing Maps Control

We will add the functionality to load the Bing Maps control to the BingMapsSearch.js file. We are now going to create three global variables called: map, searchManager and directionsManager. Next, we will create a function called GetMap which will load the Bing Maps control for us. The GetMap function will be loaded after the Microsoft.Maps.Map module has completed loading. Your code should look like this:

var map, searchManager, directionsManager;

 

function GetMap() {

    var mapOptions =

    {

        credentials: "YOUR_BING_MAPS_KEY",

        zoom: 2

    };

 

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

}

 

//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 Geocoding Logic

When a user adds content to the searchTbx textbox and presses the Go button a request will be fired to a Geocode function. For this functionality we will be making use of the Search module. The first thing we will do in this function is check to see if the searchManager variable has been initialized. If it hasn’t, we will load the Search module and initialize this variable and then process the search request. If this variable has been initialized, we will then create a geocode request passing in the users input value and requests a maximum of 1 result. We will also pass in the names of callback functions that get called if the request is successful or not.

If the request is successful we will display the result on the map with a pushpin and zoom into the location. If it is not successful, we will alert the user. The request will then be geocoded against the search manager. I would like to take a moment to point out that the alert JavaScript function that is commonly used in web development is not supported in Windows Store Apps. Instead, we need to make use of the Windows.UI.Popups.MessageDialog class. And since this is likely something we will want to use regularly, we will create a simple function for displaying messages to the user called ShowMessage. Add the following code to the BingMapsSearch.js file.

function GeocodeModule() {

    ClearMap();

 

    if (searchManager) {

        var request = {

            where: document.getElementById('searchTbx').value,

            count:1,

            callback: geocodeCallback,

            errorCallback: geocodeError

        };

 

        searchManager.geocode(request);

    } else {

        //Load the Search module and create a search manager.

        Microsoft.Maps.loadModule('Microsoft.Maps.Search', {

            callback: function () {

                //Create the search manager

                searchManager = new Microsoft.Maps.Search.SearchManager(map);

 

                //Perfrom search logic

                Geocode();

            }

        });

    }

}

 

function geocodeCallback(response, userData) {

    if (response &&

        response.results &&

        response.results.length > 0) {

        var r = response.results[0];

        var l = new Microsoft.Maps.Location(r.location.latitude, r.location.longitude);

 

        //Display result on map       

        var p = new Microsoft.Maps.Pushpin(l);

        map.entities.push(p);

 

        //Zoom to result

        map.setView({ center: l, zoom : 15 });

    } else {

        ShowMessage("Geocode Response", "Not results found.");

    }

}

 

function geocodeError(request) {

    ShowMessage("Geocode Error", "Unable to Geocode request.");

}

 

function ShowMessage(title, msg) {

    var m = new Windows.UI.Popups.MessageDialog(title, msg);

    m.showAsync();

}


Adding the Routing Logic

When a user adds content to the fromTbx and toTbx textboxes and presses the Get Directions button a request will be fired to a GetRoute function. For this functionality we will be making use of the Directions module. The first thing we want to do in this function is to verify that the directionsManager variable has been initialized. If it hasn’t, we will load the Directions module and initialize this variable and then process the route request. If this variable has been initialized, we will then pass the start and end points to the directions manager and pass in the itinerary div id as the location to display the directions. We will then have the directions manager calculate the directions. Add the following code to the BingMapsSearch.js file.

function GetRoute() {

    ClearMap();

 

    if (directionsManager) {

        // Set Route Mode to driving

        directionsManager.setRequestOptions({ routeMode: Microsoft.Maps.Directions.RouteMode.driving });

 

        // Create start and end waypoints

        var startWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('fromTbx').value });

        var endWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('toTbx').value });

 

        directionsManager.addWaypoint(startWaypoint);

        directionsManager.addWaypoint(endWaypoint);

 

        // Set the id of the div to use to display the directions

        directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('itineraryDiv') });

 

        // Calculate directions, which displays a route on the map

        directionsManager.calculateDirections();

    } else {

        //Load the Directions module and create a directions manager.

        Microsoft.Maps.loadModule('Microsoft.Maps.Directions', {

            callback: function () {

                //Create the directions manager

                directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);

 

                //Perfrom route logic

                GetRoute();

            }

        });

    }

}

The directions manager takes care of notifying the user if there is an issue calculating the route such as one of the route points having ambiguous results. This saves use a lot of time as we won’t have to worry about handling and developing this functionality. Additionally, this gives us the ability to easily drag the route if we want to customize it or avoid an area.

Clearing the Map

Whenever we process a new geocode or route request we will want to clear the map. We will also need to create a function named ClearMap as this is needed for the Clear Map button. When this function is called we will want to remove any entities from the map and reset the directions manager. This is rather simple to do, just add the following code to the BingMapsSearch.js file.

function ClearMap() {

    map.entities.clear();

 

    if (directionsManager) {

        directionsManager.resetDirections();

    }

}

Running the App

At this point we have created all the functionality needed for the UI we created originally. If you run the app now you can make geocode and routing requests. Here is a screen shot of the app with a route from New York to Toronto.

Win8_Geo_complete

- Ricky Brundritt, EMEA Bing Maps Technology Solution Professional

Moore Tornado App with High-Res Before/After Imagery

$
0
0

Our hearts go out to all the people affected by the tornadoes that recently struck the Midwest. We ask everyone to please consider supporting the disaster response and relief efforts. Here are a couple of ways you can donate:

It’s really sobering to look at how destructive an EF-5 tornado can be, and adds perspective to the impact a disaster like this has on a city and its residents.

As such, we’ve developed the Moore Tornado Map App that allows people to toggle between high-resolution aerial imagery of Moore, Oklahoma before and after the May 20thstorm. All disasters are tragic, no matter the size – we chose to focus on this one because of its magnitude and our possession of relevant imagery.

The striking imagery of the town was captured by satellite 48 hours after the event at 50cm resolution. This type of imagery can be used to help authorities assess damage and plan for rebuilding the town of Moore.

Below is just an example of what the powerful storm left behind:

Moore, Oklahoma
-Before-

 
MooreOklahomaBefore

Moore, Oklahoma
-After-

 
MooreOklahomaAfter 

Again, please consider supporting the disaster response and relief efforts through one of the below channels:

- The Bing Maps Team

Bing Maps Publishes Equivalent of 100,000 DVD’s of Bird’s Eye Imagery

$
0
0

Today, we are excited to announce a number of updates to Bing Maps, including the largest shipment of Bird’s Eye imagery yet, nearly 270 terabytes of data or the equivalent of 100,000 DVD’s, along with expanded venue maps and our new “Report a problem” feature.

BirdsEye_divider_550x3

birdseye

Bird's Eye
Imagery captured at a 45 degree angle, giving depth and three-dimensionality to ortho photography

BirdsEye_divider_550x3

To date Bing has published a total of 1,452,958 sq km, or half a petabyte of data, of Bird's Eye scenes from around the world. Look for yellow in the below World map to see our new Bird’s Eye coverage from this release:

BE_CoverageMap

Click the links below, and view more imagery on Bing Maps!

Tampere, Finland

W8 Link | Bing Maps Link

BE_Tampere_Large

Rome, Italy

W8 Link | Bing Maps Link

BE_Rome_Large

Kaanapali, Hawaii

W8 Link | Bing Maps Link

Stavanger, Norway

W8 Link | Bing Maps Link

BE_Hawaii_MedBE_StavengerNorway

Milan, Italy

W8 Link | Bing Maps Link

Ringerike, Norway

W8 Link | Bing Maps Link

BE_MilanItaly_SmallBE_Ringerike_Small

Toledo, Ohio

W8 Link | Bing Maps Link

Kuopio, Finland

W8 Link | Bing Maps Link

BE_ToledoOHBirdsEye_Finland_Small

Additional Highlights

Melbourne, Australia
W8 Link | Bing Maps Link
Eugene, Oregon
W8 Link | Bing Maps Link
Pescara, Italy
W8 Link | Bing Maps Link
Saratoga Springs, NY
W8 Link | Bing Maps Link
Tokyo, Japan
W8 Link | Bing Maps Link

VenueMap_divider_550x3

VM_Icon_Small

Venue Maps
Provides detailed maps illustrating a point of interest

VenueMap_divider_550x3

Ever need to find your seats at a football game? Lost in the Mall of America? With more than 4,700 Venue Maps in more than 59 countries on Bing Maps, you can navigate your way around malls, airports, amusement parks and more using your Windows phone, tablet, or PC!

Mall of America, St. Paul, Minnesota

W8 Link | Bing Maps Link

VENUE_MofA_large

While on Bing Maps and within a zoom level of 1000 feet, purple and green polygons will appear to outline particular points of interest for a given venue. Green shading indicates restaurants, while stores appear purple.

When you click on any outlined polygon, a directory will appear with a full list of points of interest and offer options to visually explore additional floors of the venue. Try it with the additional links below:

Boston, Massachusetts

W8 Link | Bing Maps Link

Singapore Zoo, Singapore

W8 Link | Bing Maps Link

VENUE_BostonVENUE_Singapore

Las Vegas, Nevada

W8 Link | Bing Maps Link

King's College, London

W8 Link | Bing Maps Link

VENUE_VegasVENUE_London

VenueMap_divider_550x3

If you see an outlined feature that is not correct, please tell us about it. All you have to do is click on the point of interest in question and click “Report a problem”.

Follow the steps below for a walk-through at Hartsfield-Jackson International Airport, Atlanta, Georgia.

Step 1: Click on US Post Office, then click "Report a problem"
W8 Link |
Bing Maps Link

VM_Report_Main
Step 2: Select an option and click "Continue"
VM_Report_Image2
Step 3: Select option or type in comment and hit "Submit"
VM_Report_Image3

- The Bing Maps Team

Infoboxes for Native Windows Store Apps

$
0
0

Recently there has been a number of requests for information on how to create infoboxes using the Bing Maps Native control. Many developers who have used our JavaScript controls are used to an infobox control being available out of the box and are a bit surprised that there isn’t one in the Native control. This wasn’t an oversight, but really not needed. With the Native control, we have the ability to overlay user controls directly on top of the map and tie them to a location on the map. This means that rather than being restricted to having to use an infobox control that looks and feels the way we think it should, you have the ability to create an infobox that looks and feels how you want.

In this blog post we will take a look at how to create a simple infobox control. To optimize this application, we will use a common method of having one Infobox control which we will reuse and update rather than creating an infobox for each pushpin we create. This will drastically reduce the number of objects your application will need to render and keep track of. This same approach is recommended when using the JavaScript control as well. If using JavaScript, take a look at this blog post.

Creating the View

To start off, create a Blank Windows Store project in Visual Studio called BingMaps_Native_Infobox and add a reference to the Bing Maps SDK. If you are not familiar with how to do this, take a look at the Getting started with Bing Maps Windows Store Apps (Native) blog post.

Once your project is created, open up the MainPage.xaml file and add a reference to the Bing Maps SDK and add a map to the main Grid. Inside of the map, we will add two MapLayers and give the first layer a name of DataLayer. We will use this layer to add our pushpins to. We will put the infobox in the second layer and this will ensure that the infobox always appears above the data layer. For the infobox itself, we will use a Grid control and add some Textboxes for Title and Description information. We will also add a close button to the infobox. Putting this together, your XAML should look like this:

<Page
x:Class="BingMaps_Infoboxes_Native.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BingMaps_Infoboxes_Native"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:Bing.Maps"
mc:Ignorable="d">

<GridBackground="{StaticResource ApplicationPageBackgroundThemeBrush}">
<m:MapCredentials="YOUR_BING_MAPS_KEY">
<m:Map.Children>
<!-- Data Layer-->
<m:MapLayerName="DataLayer"/>

<!--Common Infobox-->
<m:MapLayer>
<Gridx:Name="Infobox"Visibility="Collapsed"Margin="0,-115,-15,0">
<BorderWidth="300"Height="110"Background="Black"Opacity="0.8"BorderBrush="White"BorderThickness="2"CornerRadius="5"/>

<StackPanelHeight="100"Margin="5">
<GridHeight="40">
<TextBlockText="{Binding Title}"FontSize="20"Width="250"TextWrapping="Wrap"HorizontalAlignment="Left"/>
<ButtonContent="X"Tapped="CloseInfobox_Tapped"HorizontalAlignment="Right"VerticalAlignment="Top"/>
</Grid>
<ScrollViewerHorizontalScrollBarVisibility="Auto"VerticalScrollBarVisibility="Auto"MaxHeight="60">
<TextBlockText="{Binding Description}"FontSize="16"Width="290"TextWrapping="Wrap"Height="Auto"/>
</ScrollViewer>
</StackPanel>
</Grid>
</m:MapLayer>
</m:Map.Children>
</m:Map>
</Grid>
</Page>

Adding the Application Logic

If you try running the application now, an error will be thrown as we haven’t defined the CloseInfobox_Tapped event handler. To fix this, open the MainPage.xaml.cs file and add the following event handler:

 
privatevoid CloseInfobox_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
Infobox.Visibility = Visibility.Collapsed;
}

Next, we need to add some pushpins to the map. To do this, we will create a reusable function that takes in a Location, title, description and a reference to a MapLayer to add the pushpin to. When we create the pushpin we will store the title and description inside the Tag property of the pushpin. We will then add a Tapped event which we will use to open our infobox. Finally, we will add the pushpin to the layer. Add the following code to your application:
publicvoid AddPushpin(Location latlong, string title, string description, MapLayer layer)
{
Pushpin p = new Pushpin()
{
Tag = new Metadata()
{
Title = title,
Description = description
}
};

MapLayer.SetPosition(p, latlong);

p.Tapped += PinTapped;

layer.Children.Add(p);
}

publicclass Metadata
{
publicstring Title { get; set; }
publicstring Description { get; set; }
}

Now we will create the PinTapped event handler. When the user taps the pushpin, we will take the metadata we stored in the pushpin and bind it to the Infobox. We will then make the infobox visibility and then use the MapLayer to set the position of the infobox.

privatevoid PinTapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
Pushpin p = sender as Pushpin;
Metadata m = (Metadata)p.Tag;

//Ensure there is content to be displayed before modifying the infobox control
if (!String.IsNullOrEmpty(m.Title) || !String.IsNullOrEmpty(m.Description))
{
Infobox.DataContext = m;

Infobox.Visibility = Visibility.Visible;

MapLayer.SetPosition(Infobox, MapLayer.GetPosition(p));
}
else
{
Infobox.Visibility = Visibility.Collapsed;
}
}

We now have all the code we need to create pushpins that open up an Infobox when tapped. All we need to do now is create some pushpins. To do this, update the constructor to look like this:

public MainPage()
{
this.InitializeComponent();

AddPushpin(new Location(47.6035, -122.3294), "Seattle", "Seattle is in the state of Washington. ", DataLayer);

AddPushpin(new Location(51.5063, -0.1271), "London", "London is the capital city of England and the United Kingdom, and the largest city in the United Kingdom.", DataLayer);
}

If you run the application, you will see two pushpins on the map. If you tap on one, the Infobox will appear with the relevant information for that pushpin and look something like this:

image

You can take this a step further and create a custom UserControl that has a lot more advanced features. In the end you simply just need to add it to a MapLayer and set its position.

How To Load Spatial Data From SQLite In A Windows Store App

$
0
0

Sometimes it can be helpful to hold geospatial data locally and to distribute it with the application. In this example, we will use SQLite to store geospatial information and visualize it as a layer in the Bing Maps Control for Windows Store Apps.

SQLite is a software library that implements a self-contained, server-less, zero-configuration, transactional SQL database engine.

Preparing the Data

For this example we will be using trails which we retrieved from the King County GIS Data Portal. This data is available in Esri Shapefile format with coordinates in the North American Datum 1983 (NAD83) system. I converted them into a SQLite database with coordinates described as latitudes and longitudes with decimal degrees in the World Geodetic System 1984 (GS84) using the ogr2ogr command-line tool from Geospatial Data Abstraction Library (GDAL). The command for this conversion is:

ogr2ogr.exe -f sqlite -lco FORMAT=WKT "D:\Downloads\GeoData\King County\trail_SHP\trail.db" 
"D:\Downloads\GeoData\King County\trail_SHP\trail.shp" -t_srs EPSG:4326

Prerequisites

Since we are developing a Windows Store App, we need access to a Windows 8 machine as well as Visual Studio 2012. A free version of Visual Studio Express 2012 for Windows 8 is available here.

For this project we require the “Bing Maps SDK for Windows Store Apps” as well as “SQLite for Windows Runtime” you can install both from Visual Studio 2012 by selecting “Extensions and Updates” from the menu “Tools” and searching for the respective SDKs in the online gallery.

We will also require a Bing Maps Key. If you don’t have one yet, you can follow the instructions to get a free trial or basic key.

Preparing the Project

Let’s start by creating a new project using the blank Visual C# template for Windows Store Apps.

BlankVisualCSharpTemplateWindowsStoreApps

Next we add references to the Bing Maps SDK, the Visual C++ Runtime and SQLite.

ReferenceManager-SQLite_Blog

The Bing Maps SDK requires that we compile separately for each processor architecture. So we need to open the “Configuration Manager” and change the platform from “Any CPU” to a specific one – here “x64”.

ConfigurationManager

We also require the “sqlite-net” library and we can add this from NuGet by opening the menu “Tools” “Library Package Manager” “Manage NuGet Packages for Solution” and searching for “sqlite-net”.

ManageNuGetPackages

Finally we add the SQLite database trail.db to the folder “Assets” of the project, set the property “Build Action” to “Content” and “Copy to Output Directory” to “Copy if Newer”.

SolutionExplorer

Adding the Application Markup

Now that our project is prepared, we open the MainPage.xaml add the namespace for the Bing Maps control and define the user interface. In the user interface, we load Bing Maps centered to a location in King County at zoom-level 13 and specify that we want to display the aerial imagery.

<Pagex:Class="SQLite_Blog.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:SQLite_Blog"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:bm="using:Bing.Maps"mc:Ignorable="d">
<GridBackground="{StaticResourceApplicationPageBackgroundThemeBrush}"><Grid.RowDefinitions><RowDefinitionHeight="140"/><RowDefinitionHeight="*"/> <RowDefinitionHeight="Auto"/></Grid.RowDefinitions>
   <TextBlockx:Name="pageTitle"Text="WinRT and Spatial Data  from SQLite"IsHitTestVisible="false"Style="{StaticResourcePageHeaderTextStyle}"VerticalAlignment="Center"Margin="30,0,30,40"/>
   <bm:Mapx:Name="myMap"Grid.Row="1"MapType="Aerial"ZoomLevel="13"Credentials="Your_Bing_Maps_Key"><bm:Map.Center><bm:LocationLatitude="47.702894"Longitude="-122.054860" /></bm:Map.Center></bm:Map>
   <TextBlockx:Name="myAttribution"Text="Data provided by  permission of King County"Grid.Row="2"/></Grid></Page>

Adding the Code-Behind

In the code-file MainPage.xaml.cs we define a class that describes the SQLite table.

public class trail
{    public string OGC_FID { get; set; } public string WKT_GEOMETRY { get; set; }  public string kc_fac_fid {  get;  set; }public string trail_name {  get;  set; }public string trail_type {  get;  set; }public string surf_type {  get;  set; }public string sitefacfid {  get;  set; }public string sitename {  get;  set; }public string sitetype {  get;  set; }public string owner {  get;  set; }public string ownertype {  get;  set; }public string manager {  get;  set; }public string managertype {  get;  set; }public string maintd_by {  get;  set; }public string mainttype {  get;  set; } public string shape_len {  get;  set; }
 }

For the class that will actually read the data and display it on the map, we import three libraries:

using Bing.Maps;using Windows.Storage;using Windows.UI.Popups; 

The class that reads the trail-database copies it first in the local application directory. It creates a MapShapeLayer, reads through the table-records and adds the records for the trails to the layer before it adds the entire layer to the map.

One point to call out here is that the data is stored as Well Known Text (WKT) in SQLite. The WKT has the coordinates in the order Longitude and then Latitude while Bing Maps expects them in the order Latitude and then Longitude. So we have to swap the order of the coordinates.

public async void GetTrails()
{var uri = newUri("ms-appx:///Assets/trail.db"); var file = awaitStorageFile.GetFileFromApplicationUriAsync(uri);
   var destinationFolder = ApplicationData.Current.LocalFolder;//local appdata dir
   try 
   {await  file.CopyAsync(destinationFolder); //copied application local folder}}
   }catch { }var dbpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "trail.db");var db = new SQLite.SQLiteConnection(dbpath);var trails = db.Table<trail>();
   MapShapeLayer  shapeLayer = newMapShapeLayer();int numLocs = 0;int numTrails = 0;
   foreach (trail thisTrail in trails)
   {var wkt =  thisTrail.WKT_GEOMETRY.Replace("LINESTRING  (" , "").Replace(")", "");string[] wktArray = wkt.Split(',');LocationCollection bmPolylineLocs = newLocationCollection();for (var i = 0; i <  wktArray.Length; i++)
   { var loc = wktArray[i];var locArray = loc.Split(' ');
   bmPolylineLocs.Add(new Location(Convert.ToDouble(locArray[1]), 
   Convert.ToDouble(locArray[0])));
   numLocs  = numLocs + 1;
   }
   MapPolyline bmPolyline = newMapPolyline();
   bmPolyline.Locations = bmPolylineLocs;
   bmPolyline.Color = Windows.UI.Colors.Red;
   bmPolyline.Width = 5;
   shapeLayer.Shapes.Add(bmPolyline);
   numTrails  = numTrails + 1;
  }
  myMap.ShapeLayers.Add(shapeLayer);
  var myMsg = newMessageDialog("Loaded " + numTrails.ToString() + " with " + numLocs.ToString() + " locations" );await myMsg.ShowAsync(); 
}

Finally we add a call to this new class right after we initialize the app.

public MainPage()
{this.InitializeComponent();
  GetTrails();
} 

And that’s it. Below you see a screenshot of the trails in King County on top of Bing Maps.

Happy Coding!

WinRTSpatialDataSQLite

Automating the Windows Map App

$
0
0

Windows allows a Windows Store application to register as the default handler for a certain URI schema. This process is called Protocol Activation and it can be leveraged in the WinRT as well as the WinJS framework.

The Windows Map app is registered for protocol activation and the URI schema is documented here. Through protocol activation you can control the map view, find places or business listings, calculate routes or even map entire collections of points of interest.

 ProtocolActivation

In this quick example, we use this capability to display the location where we captured a photo. Rather than starting from the scratch, we build upon the Simple Image Sample from the Windows Dev Center. The first scenario in this application reads and writes properties of an image – including the coordinates (latitude and longitude) where we captured the photo.

SimpleImagingSample

There are a few changes that we need to apply to this app in order to launch the Map app and center on this location.

In the application markup language of scenario 1 we do the following: 

  1. Replace the boxes for degrees, minutes and seconds as well as the one for the compass orientation with a single text-box in which we will display the latitude and longitude in decimal degrees.
  2. Add a button that will launch the Map app with parameters.

The new XAML will look like this:

<TextBlockGrid.Column="0"Grid.Row="6"TextWrapping="Wrap"	 Style="{StaticResourceBasicTextStyle}"	 HorizontalAlignment="Left"VerticalAlignment="Center">Latitude</TextBlock><TextBoxGrid.Column="1"Grid.Row="6"Margin="0,0,10,10"x:Name="LatTextbox"         HorizontalAlignment="Left"  Text=""Width="300" />

<
TextBlockGrid.Column="0"Grid.Row="7"TextWrapping="Wrap" Style="{StaticResourceBasicTextStyle}" HorizontalAlignment="Left"VerticalAlignment="Center">Longitude</TextBlock><TextBoxGrid.Column="1"Grid.Row="7"Margin="0,0,10,10"x:Name="LonTextbox" HorizontalAlignment="Left"Text=""Width="300" />
<Buttonx:Name="myPhotoLoc"Grid.Column="1"Grid.Row="8"Click="myPhotoLoc_Click"Width="300"Margin="0,0,10,0">Open Map</Button> 

Next we look at the code behind and in the function GetImagePropertiesForDisplay. We replace the code that splits the coordinates in degrees, minutes and seconds with the snippet below.

' Do a simple check if GPS data exists.If  (m_imageProperties.Latitude.HasValue) AndAlso  (m_imageProperties.Longitude.HasValue) Then
     LatText = Math.Round(m_imageProperties.Latitude.Value,  6).ToString
     LatTextbox.Text = LatText
  LonText = Math.Round(m_imageProperties.Longitude.Value,  6).ToString
  LonTextbox.Text = LonTextEnd If

We also introduce a new function to handle the click-event on our button and launch the Map app:

Private Async Sub myPhotoLoc_Click(sender As Object, e AsRoutedEventArgs)' Create the URI to launch  from a string.Dim uri = NewUri("bingmaps:?collection=name.Photo%20Locations~point." + _
     LatText + "_" + LonText + "_My%20Photo&sty=a")
  ' Launch the URI.Dim success As Boolean = Await Windows.System.Launcher.LaunchUriAsync(uri)End Sub

And that’s it. You’ll find the complete source code here.

Happy Mapping

PhotoViewer


Geocoding With the Search Charm

$
0
0

In this blog post we are going to create a simple mapping application that allows the user to search for locations using the Search charm. To do this, there are three main tasks that have to be done:

  1. Create a basic mapping app
  2. Integrate with the Search Charm
  3. Add geocoding logic to the Search Charm

By making use of the Search charm, users can search within your app from anywhere in their system. The full source code in C# and VB for this blog post is available in the Visual Studio Galleries here.

Creating a Basic Mapping App

Before diving into adding logic for tying into the Search charm, let’s start off with creating a basic mapping application which will be used to display the results on.

1)  Start by opening Visual Studios 2012 and create a new project. In the window that opens, select Visual C# -> Windows Store. Select the Blank App template. Call the application GeocodingSearchCharm and press OK.

GeocodingSearchCharmNewProject

2)  Add a reference to the Bing Maps SDK. To do this, right click on the References folder and press Add Reference. Select Windows -> Extensions select Bing Maps for C#, C++ and Visual Basic. If you do not see this option, ensure that you have installed the Bing Maps SDK for Windows Store apps. While you are here, also add a reference to the Microsoft Visual C++ Runtime Package as this is required by the Bing Maps SDK when developing using C# or Visual Basic.

GeocodingSearchCharmReferenceManager

3)  You may notice that there is a little yellow indicator on the references that you just added. The reason for this is that when using the C++ runtime package you have to set the Active solution platform in Visual Studio to one of the following options: ARM, x86 or x64. To do this, right click on the Solution folder and select Properties. Then go to Configuration Properties -> Configuration. Find your project and under the Platform column set the target platform. For this blog post, I’m going to select x86. Press Ok and the yellow indicator should disappear from our references.

GeocodingSearchCharmConfigurationManager 

4)  Now add a map to our application. To do this open the MainPage.xaml file. You will first need to add the Bing Maps SDK as a namespace at the top of the file. After you do this you can add a Map object to the Grid control and add your Bing Maps key to the credentials properties of the map. Give the map a name of MyMap.

<Pagex:Class="GeocodingSearchCharm.MainPage"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   xmlns:local="using:GeocodingSearchCharm"   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   xmlns:m="using:Bing.Maps"    mc:Ignorable="d">
   <GridBackground="{StaticResourceApplicationPageBackgroundThemeBrush}"><m:MapName="MyMap"Credentials="YOUR_BING_MAPS_KEY"/> </Grid>
</Page>

5)  At this point if you run the application, you will end up with a Bing map that takes up the whole screen and looks like this:

GeocodingSearchCharmMap

Integrating with the Search Charm

Now that you have a map to display search results on the next step is to tie into the Search charm.

1) Add a Search contract to the project. To do this right click on the project and select Add -> New Item. Locate the Search contract item and call it SearchResultsPage1.xaml.

GeocodingSearchCharmAddNewItem

2) An alert will be displayed that asks if you would like to add some missing files to your project. Press Yes.

GeocodingSearchCharmVisualStudio

3)  At this point you will notice a number of changes have been made to the project.

  • In the app manifest the Search contract is declared under the Declarations tab.
  • A basic search results page is created for your app. This results page adheres to the UX guidelines for results pages in Guidelines and checklist for search.
  • OnSearchActivated event handler is added to the App.xaml.cs file. This allows your application to respond to search queries, even when your app isn't on the screen.
  • A number of helper files are added under the Common folder.

4)  For this sample you don’t need the SearchCharmResults1.xaml. So delete this file from the project.

5)  Open the App.xaml.cs file and locate the OnSearchActivated event handler. Near the end of this event handler, find the following line of code:

frame.Navigate(typeof(SearchCharmResults1), args.QueryText); 

Replace the SearchCharmResults1 value with MainPage. This will cause the main application to start when performing a search against it, even if the app isn’t loaded.

6)  Open the MainPage.xaml.cs file. In here you will want to get a reference to the search charm panel and attach a QuerySubmitted event when the user navigates to the app. You will also want to remove this event handler when the user navigates away from the app. This file should look like this:

using Windows.ApplicationModel.Search;using Windows.UI.Xaml.Controls;using Windows.UI.Xaml.Navigation;
namespace GeocodingSearchCharm
{public sealed partial classMainPage : Page
   {privateSearchPane searchPane; 
    public MainPage()
      {this.InitializeComponent();
         //Get a reference to the Search charm 
         searchPane = SearchPane.GetForCurrentView();     //Add watermark text to the search  textbox
        searchPane.PlaceholderText = "Search for an  address, city or place.";
        searchPane.ShowOnKeyboardInput = true;
   }
   protected override void OnNavigatedTo(NavigationEventArgs e)
   {base.OnNavigatedTo(e);
      searchPane.QuerySubmitted +=  searchPane_QuerySubmitted;
   }
   protected override void OnNavigatedFrom(NavigationEventArgs e)
   { base.OnNavigatedFrom(e);
      searchPane.QuerySubmitted -=  searchPane_QuerySubmitted;
   }
   private void searchPane_QuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args)
   { //TODO: Add logic for geocoding the query  text.
   }
 }
} 

Adding the Geocoding Logic

At this point we have two of the three main tasks completed. The next step is to integrate in the geocoding logic by using the Bing Maps REST services.

1)  First we add the JSON data contracts for the Bing Maps REST services to your project. To do this, right click on your project and select Add -> New Item. Create a class called BingMapsRESTServices.cs. Open the file and delete the contents.

2)  Copy the C# JSON data contracts for the Bing Maps REST services from the MSDN documentation. Paste in the JSON data contracts into the BingMapsRESTServices.cs

3)  In the MainPage.xaml.cs file add the following helper method for handing requests to the Bing Maps REST services.

private asyncTask<Response> GetResponse(Uri uri)
{
   System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();var response = await client.GetAsync(uri);
    using (var stream = await response.Content.ReadAsStreamAsync())
   {   DataContractJsonSerializer ser = new
DataContractJsonSerializer(typeof(Response));   return ser.ReadObject(stream) asResponse;
   }
} 

4)  Next the logic for creating the geocoding request can be added to the searchPane_QuerySubmitted event handler. Once a request has been made, you can loop through the results and add them as pushpins on the map. Update the searchPane_QuerySubmitted event handler such that it looks like this:

private async void searchPane_QuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args)
{//Remove any existing search result items from the map.
   MyMap.Children.Clear();
        //Logic for geocoding the query text.   if (!string.IsNullOrWhiteSpace(args.QueryText))
   {Uri geocodeUri = newUri(string.Format("http://dev.virtualearth.net/REST/v1/Locations?q={0}&c={1}&key={2}",Uri.EscapeUriString(args.QueryText),  args.Language, MyMap.Credentials));
        //Get response from Bing Maps REST  services        Response r = await GetResponse(geocodeUri);
        if (r != null&&
            r.ResourceSets != null&&
            r.ResourceSets.Length > 0  &&
            r.ResourceSets[0].Resources != null&&
            r.ResourceSets[0].Resources.Length  > 0)
   { LocationCollection locations = newLocationCollection();
            int i = 1;
            foreach (BingMapsRESTService.Common.JSON.Location lin r.ResourceSets[0].Resources)
   {//Get the location of each result
               Bing.Maps.Location location =new Bing.Maps.Location(l.Point.Coordinates[0],  
l.Point.Coordinates[1]);
              //Create a pushpin each location              Pushpin pin = newPushpin()
              {
                  Tag = l.Name,
                  Text = i.ToString()
              };
              i++;
              //Add a tapped event that will display  the name of the location 
              pin.Tapped += (s, a) =>
              {var p = s as Pushpin;newMessageDialog(p.Tag as string).ShowAsync();
              };
              //Set the location of the pushpin              MapLayer.SetPosition(pin, location);
              //Add the pushpin to the map 
              MyMap.Children.Add(pin);
              //Add the coordinates of the location to a location  collection
              locations.Add(location);
          }
          //Set the map view based on the location  collection 
          MyMap.SetView(newLocationRect(locations));
   }else
   {await newMessageDialog("No results found.").ShowAsync();
   }
 }
} 

At this point the application is complete. The final step is to run and test the application. Press the Debug button to launch the application. Open up the Search charm from the right side panel and perform a search. Below is a screenshot of a search from London:

GeocodingSearchCharm

- Ricky Brundritt, EMEA Bing Maps Technology Solution Professional

Traffic Notifications with Bing Maps & Windows Azure Mobile Services

$
0
0

Windows Azure Mobile Services is a great set of services which provide a cloud backend for your mobile applications. It provides native SDKs for Windows Store apps, Windows Phone, iOS and Android as well as HTML5. Those SDKs support amongst many other things push notifications, authentication via Microsoft, Facebook, Google or Twitter accounts as well as schedulers combined with the on-demand scaling that you expect from a cloud platform. Getting started is free and described in excellent tutorials for each of the supported platforms.

A while ago I blogged about using Windows Azure Mobile Services along with Bing Maps in a Windows Store app that supports live tiles as well as toast notifications. Another example that combines the two services was published by Nick Harris on the Windows Dev Center.

In this sample, we will build a Windows Store App that provides tile and toast notifications to inform you in regular intervals about the delay on your typical route to work. To build this application we use Windows Azure Mobile Services for the backend – including the job scheduler for periodic notifications – and Bing Maps to calculate the route, get the current traffic situation and generate maps for the tile and toast notifications.

TrafficNotificationsUpdatedTile

Prerequisites

Since we are developing a Windows Store App, we require access to a Windows 8 machine as well as Visual Studio 2012. A free version of Visual Studio Express 2012 for Windows 8 is available here.

To access Windows Azure Mobile Services, we require a Windows Azure subscription. If you don’t have one yet, you can sign up for a free trial.

The “Bing Maps SDK or Windows Store Apps” can be installed directly from Visual Studio by selecting “Extensions and Updates” from the menu “Tools” and searching in the online gallery.

We will also require a Bing Maps Key. If you don’t have one, you can follow these instructions to get a free trial or basic key.

Getting Started

As mentioned earlier there are excellent tutorials to get started with Windows Azure Services. Rather than repeating those initial steps, I’m only going to point out the ones that help you over the first hurdles:

  1. Follow the tutorial Get started with Mobile Services for Windows Store apps. This will set up the mobile service, create a Windows Azure SQL Database and allow you to download a sample Windows Store app. For this example we chose JavaScript as language. The application will be pre-configured with the application key and Mobile Service URL.
  2. Next, follow the tutorial Get started with authentication in Mobile Services in JavaScript. I used Microsoft as the authentication provider, but as mentioned before, you can also choose Facebook, Google or Twitter.
  3. Finally, follow the tutorial Get started with push notifications in Mobile Services; again for JavaScript.

By now we have a sample application implementing a simple Todo list powered by Windows Azure Mobile Services.

TrafficNotificationsToDoList

Creating a New Table in Windows Azure Mobile Services

In the Windows Azure Management Portal we select the tab “Data” under our Mobile Service and create a new table RouteItem. We set the permissions to “Only Authenticated Users”.

One of the beauties of Windows Azure Mobile Services is that we don’t have to declare columns and data types at this point. By default, Mobile Services use a “Dynamic Schema”. We just define the columns in our code and Mobile Services handles the rest for us and adds new columns with the appropriate data types in the backend.

TrafficNotificationsCreateNewTable

Modifying the Windows Store App

In the application, we want to be able to calculate a route and define it as the default for our way to work. We will store the information with start-point and end-point as well as the default time – assuming that there were no traffic – in Windows Azure Mobile Services. This will be the baseline against which Mobile Services periodically compares the traffic situation.

We start by adding a reference to Bing Maps for JavaScript to the project.

TrafficNotificationsSolutionExplorer

Now we open the default.html and make a few modifications.

We add the references for Bing Maps to the head of the document:

<!-- Bing Maps references --> <scriptsrc="/Bing.Maps.JavaScript/js/veapicore.js"></script><scriptsrc="/Bing.Maps.JavaScript/js/veapiModules.js"></script>

Then we modify the body such that we have div-elements that will host the map and another div-element where we will render the driving directions. We also add a few textboxes and buttons:

<divstyle="height: 100% ; " ><divstyle="display: -ms-grid; margin:30px; -ms-grid-columns: 1fr 1fr; -ms-grid-rows: auto 1fr; height: 100%"><divstyle="-ms-grid-column-span: 2;"><divstyle="color: #0094ff; font-size: 8pt; margin:0px 0px 0px 3px">
   BING MAPS & WINDOWS AZURE MOBILE SERVICES</div><divstyle="color: #808080; font-size: 32pt">DonkeyRoutes</div></div>
    <divstyle="-ms-grid-row: 2; -ms-grid-column: 1;  -ms-grid-rows:auto auto auto; margin: 0px 0px 40px 3px;"><divstyle="display: -ms-grid; -ms-grid-row:1; -ms-grid-columns: 50px 1fr; -ms-grid-rows: auto auto; margin-right:4px;" ><divstyle="-ms-grid-column:1; -ms-grid-row-align:center"><astyle="color: #808080;">Start</a></div><inputstyle="-ms-grid-column:2; width:100%;"type="text"id="txtStart" /><divstyle="-ms-grid-column:1; -ms-grid-row:2; -ms-grid-row-align:center;"><astyle="color: #808080;">End</a></div><inputstyle="-ms-grid-column:2; -ms-grid-row:2; width:100%;"type="text"id="txtEnd" /></div>
      <divstyle="display: -ms-grid; -ms-grid-row:2;  -ms-grid-columns: 1fr 1fr 1fr 1fr; "><buttonstyle="-ms-grid-column:1; margin-right:5px"id="btnShowTraffic">
   Show Traffic</button><buttonstyle="-ms-grid-column:2; margin-right:5px"id="btnHideTraffic">
   Hide Traffic</button><buttonstyle="-ms-grid-column:3; margin-right:5px"id="btnGetRoute">
   Get Route</button><buttonstyle="-ms-grid-column:4;"id="btnSaveRoute">
   Save Route</button></div> 
      <divid="divMap"style="-ms-grid-row:3; position:relative; top:5px; margin-bottom:100px; width:100%; height:82.6%; left: 0px;"></div></div><divid="divDirections"style="margin:0px 0px 40px 10px; overflow-y:auto; -ms-grid-column: 2; -ms-grid-row: 2; "></div></div></div>

Tip: A good way to get a visual reference is to edit the default.html in Blend for Visual Studio.

TrafficNotificationsBlendforVS

Now we move on to the JavaScript. One of the beauties of the Bing Maps JavaScript Control is that it’s mostly identical with the Bing Maps AJAX Control for the web. That means we can play around with the Interactive SDK and we can use the same build-in modules that extend core-functionality to render traffic information or driving-directions, for example. Additionally, the community has picked up this modular concept and developed modules which extend the AJAX control and can also be used within a Windows Store app.

TrafficNotificationsTiles

Now let’s look at the JavaScript code and start with the event app.onactivated. We leave the first part that handles the authentication and acquisition of the notification channel as is, remove the code that handles select, inserts or updates in the table todoitem and implement new code that will be inserting data into the Mobile Services table RouteItem, which we just created. We also create event-handlers for the various buttons.

app.onactivated  = function  (args) {if  (args.detail.kind === activation.ActivationKind.launch) {var routeTable = client.getTable('RouteItem');
 var insertRouteItem = function (routeItem) {
  routeTable.insert(routeItem).done(function (item) {var md = new  Windows.UI.Popups.MessageDialog('Your route is now  saved.');
   md.showAsync();
   });
  };
 btnGetRoute.addEventListener("click",  createDirections);
 btnSaveRoute.addEventListener("click", function () {
  insertRouteItem({
   StartLat: startLat,
   StartLon: startLon,
   EndLat: endLat,
   EndLon:  endLon,
   TimeWithoutTraffic: travelTime,
   channel: channel.uri
     });
     }
   );
    btnShowTraffic.addEventListener("click",  showTrafficLayer);
    btnHideTraffic.addEventListener("click",  hideTrafficLayer);
    authenticate();
   }
 };

Those were all the modifications required for the Mobile Services on the client-side. Now we move on to the map implementation.

We declare a few global variables:

var map = null;var directionsManager;var directionsErrorEventObj;var directionsUpdatedEventObj;var trafficLayer;var startLat = 0;var startLon = 0;var endLat = 0;var endLon = 0;var travelTime = 0;

Under the line app.start() we kick-off the loading of the map by adding an event-handler that fires when the entire DOM content is loaded. In this case, we execute a function initialize.

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

In between the lines app.start() and this event handler, we add the map-specific code. We start with a function that loads the map-module with a callback-function initMap and optional parameters that can be useful for localization.

function initialize() {
   Microsoft.Maps.loadModule('Microsoft.Maps.Map', { 
 callback: initMap, culture: 'en-US', homeRegion: 'US' });

This callback function initMap defines some options for the map such as credentials. It then loads another module with a specific “theme” that determines the look and feel of navigation controls before it renders the map and moves on to load yet another module which handles the display of traffic-flow information.

function initMap() {try {var mapOptions =
   {
      credentials: "Your Bing Maps Key",
      mapTypeId:  Microsoft.Maps.MapTypeId.collinsBart,
      enableClickableLogo: false,
      enableSearchLogo: false,
      theme: new Microsoft.Maps.Themes.BingTheme() 
    };
   Microsoft.Maps.loadModule('Microsoft.Maps.Themes.BingTheme', {
    callback: function () {
     map = new Microsoft.Maps.Map(document.getElementById("divMap"),  mapOptions);
     }
   });
   loadTrafficModule();
  }catch (e) {var md = new Windows.UI.Popups.MessageDialog(e.message);
     md.showAsync();
    }
  }function loadTrafficModule() {
     Microsoft.Maps.loadModule('Microsoft.Maps.Traffic',  { callback: trafficModuleLoaded });
}

Once the traffic module is loaded, we set the center and zoom of the map and load the real-time traffic flow information into a new layer.

function trafficModuleLoaded() {
   setMapView();
   }
function setMapView() {
   map.setView({ zoom: 10, center: new  Microsoft.Maps.Location(47.603561, -122.329437) })
   showTrafficLayer();
   }
function showTrafficLayer() {
   trafficLayer = new Microsoft.Maps.Traffic.TrafficLayer(map);
   trafficLayer.show();
   }
function hideTrafficLayer() {
   trafficLayer.hide();
   }

Let’s see what we’e got so far. When we run the application, we should be able to authenticate as before, see a map and be able to toggle the traffic layer.

TrafficNotificationsDonkeyRoutes

However, we haven’t implemented the driving directions yet. So let’s move on to that. We start by creating the function that handles the click-event for the button “Get Route”. This function will load the directions-module with a callback-function that requests the route based on the start- and end-locations that we entered in the text-boxes.

function createDirections() {if (!directionsManager) {
   Microsoft.Maps.loadModule('Microsoft.Maps.Directions',  { 
     callback: createDrivingRoute    });
  }else {
   createDrivingRoute();
  }
 }
function createDrivingRoute() {if (!directionsManager) { createDirectionsManager(); }
   directionsManager.resetDirections(); // Set  Route Mode to driving 
   directionsManager.setRequestOptions({ 
   routeMode:  Microsoft.Maps.Directions.RouteMode.driving });var startWaypoint = new Microsoft.Maps.Directions.Waypoint({ 
   address: document.getElementById('txtStart').value });
   directionsManager.addWaypoint(startWaypoint);var endWaypoint = new Microsoft.Maps.Directions.Waypoint({ 
   address: document.getElementById('txtEnd').value });
   directionsManager.addWaypoint(endWaypoint);// Set  the element in which the itinerary will be rendered 
   directionsManager.setRenderOptions({ 
   itineraryContainer:  document.getElementById('divDirections') });
   directionsManager.calculateDirections();
   }function createDirectionsManager() { var displayMessage;if (!directionsManager) {
   directionsManager = new  Microsoft.Maps.Directions.DirectionsManager(map);
   }
   directionsManager.resetDirections();
   directionsUpdatedEventObj =  Microsoft.Maps.Events.addHandler(
     directionsManager, 'directionsUpdated',  getWayPoints);
}

The directions-manager (part of the directions-module) will take care of setting the map view and rendering the itinerary, but we require some post-processing so that we can submit the data to the Mobile Services and that’s what we do in the callback-function getWayPoints. We basically retrieve some coordinates and the travel-time from the JSON-response.

function getWayPoints(e) {
   startLat =  e.route[0].routeLegs[0].startWaypointLocation.latitude;
   startLon = e.route[0].routeLegs[0].startWaypointLocation.longitude;
   endLat =  e.route[0].routeLegs[0].endWaypointLocation.latitude;
   endLon =  e.route[0].routeLegs[0].endWaypointLocation.longitude;
   travelTime =  e.route[0].routeLegs[0].summary.time;
}

This is it for the client-side. We can test the calculation of driving directions but before we can fully test the submission of the data to the Mobile Services, we will look at the server-side as well.

TrafficNotificationsDonkeyRoutes2

Inserting Data and Responding with Notification from Mobile Services

On the Mobile Services side, we want to respond to insert operations with a notification that includes a static image with the route and the current traffic-flow as well as the drive-time considering the actual traffic situation.

To do that, we navigate in the Windows Azure Management Portal to our table, open the tabulator “Script” and select the operation “Insert” from the dropdown-box.

TrafficNotificationsAzureManagementPortal

Then we replace the existing script with the following. In this server-side JavaScript, we call the Bing Maps REST Route service to calculate the drive-time considering the current traffic situation and compare it to the drive-time without traffic in order to determine the possible delay. Then we send a push-notification back to the client using the Windows Notification Service. We update the tile with an image generated from the Bing Maps REST Imagery service as well as some text including the delay.

function insert(item, user, request) {
   item.userId = user.userId;
  var httpRequest = require('request');var currentDriveTime = null;var delay = null;var addToNote = null;var uri = 'http://dev.virtualearth.net/REST/V1/Routes/Driving?' +'wp.0=' + item.StartLat + ',' + item.StartLon +'&wp.1=' + item.EndLat + ',' + item.EndLon +'&optmz=timeWithTraffic' +'&key=Your  Bing Maps Key';
   httpRequest(uri, function (err, response,  body) {if (err) {
        console.log(statusCodes.INTERNAL_SERVER_ERROR, 'Unable to connect to Bing Maps.');
     } else {
       currentDriveTime =  JSON.parse(body).resourceSets[0].resources[0].travelDuration;
       delay = Math.round((currentDriveTime -  item.TimeWithoutTraffic) / 60);if (delay > 10) {
         addToNote = 'Have another coffee';
      }else {
        addToNote = 'Good time to go to work';
      }
    }
  });
  request.execute({
   success: function () {   // Write to the response and then send the notification in the background
   request.respond();
   push.wns.sendTileWidePeekImageAndText02(item.channel, {
     image1src: 'http://dev.virtualearth.net/REST/v1/Imagery/Map/Road/Routes?' +'wp.0=' + item.StartLat + ',' + item.StartLon +'&wp.1=' + item.EndLat + ',' + item.EndLon +'&ml=TrafficFlow' +'&ms=310,150&key=Your  Bing Maps Key',
     image1alt: 'Your Route',
     text1: 'Current  Delay:',
     text2: delay + ' min',
     text3: addToNote
   }, {
     success: function (pushResponse) {
       console.log("Sent push:",  pushResponse);
      }
    });
   }
 });
}

Save the script, run the Windows Store app again and this time calculate the route and insert the item to Mobile Services. You should see an updated tile for your application.

TrafficNotificationsUpdatedTile

In the Windows Azure Management Portal you should also see the data that you just inserted.

TrafficNotificationsAzureManagementPortal2

Scheduling Periodic Updates

We’re on the final stretch. The last remaining work item in this sample is the periodic notification and for that, we use the job scheduler in Mobile Services. In the Windows Azure Management Portal, go to your Mobile Service and create a new scheduler job.

TrafficNotificationsAzureManagementPortal3

We can set up periodic intervals and alternatively run the job on-demand for our testing purposes. In the script that holds the logic for our scheduled jobs, we enter the following code. This will read the table with registered clients and follow the same procedure as we defined for the insert operation with the main difference being that we now send a tile and a toast notification to each client.

function sendUpdate() {var routeItems = tables.getTable('RouteItem');
  routeItems.read({
   success: function (routes) {
   routes.forEach(function (route) {var httpRequest = require('request');var currentDriveTime;var delay;var addToNote;var uri = 'http://dev.virtualearth.net/REST/V1/Routes/Driving?' +'wp.0=' + route.StartLat + ',' + route.StartLon +'&wp.1=' +  route.EndLat + ',' + route.EndLon +'&optmz=timeWithTraffic' +'&key=Your Bing Maps Key';
   httpRequest(uri, function (err, response,  body) {if (err) {
     console.log(statusCodes.INTERNAL_SERVER_ERROR, 'Unable to connect to Bing Maps.');
   } else {
     currentDriveTime = 
       JSON.parse(body).resourceSets[0].resources[0].travelDuration;
     delay = Math.round((currentDriveTime  - route.TimeWithoutTraffic) / 60);if (delay > 10) {
        addToNote = 'Have  another coffee';
        }else {
        addToNote = 'Good  time to go to work';
       }
       push.wns.sendTileWidePeekImageAndText02(route.channel, {
         image1src: 'http://dev.virtualearth.net/REST/v1/Imagery/Map/Road/Routes?' +'wp.0=' + route.StartLat + ',' + route.StartLon +'&wp.1=' +  route.EndLat + ',' + route.EndLon +'&ml=TrafficFlow' + '&ms=310,150&key=Bing Maps Key',
        image1alt: 'Your Route',
        text1: 'Current Delay:',
        text2: delay + ' min',
        text3: addToNote
      }, {
        success: function (pushResponse) {
          console.log("Sent  push:", pushResponse);
        }
      });
        push.wns.sendToastImageAndText04(route.channel,  {
          image1src: 'http://dev.virtualearth.net/REST/v1/Imagery/Map/Road/Routes?' +'wp.0=' + route.StartLat + ',' + route.StartLon +'&wp.1=' +  route.EndLat + ',' + route.EndLon +'&ml=TrafficFlow' +'&ms=150,150&key=Bing Maps Key',
         image1alt: 'Your Route',
         text1: 'Current Delay:',
         text2: delay + ' min',
         text3: addToNote
       }, {
         success:  function  (pushResponse) {
           console.log("Sent  push:", pushResponse);
          }
        });
       }
     });
   });
  }
 });
 }

And this completes our example.

TrafficNotificationsCompleteSample

Happy Mapping

Augmented Reality with Bing Maps in a Windows Store App

$
0
0

In this blog post we are going to take a look at augmented reality apps and how to create them as Windows Store apps. When many people hear the words “augmented reality” the first thought that comes to mind is the large helmet type video games that never really took off in the 90’s. This was largely due to the high costs and bulky hardware that was required. This is also a bit inaccurate, as these video games were actually “virtual reality” games. Augmented reality (AR) is when an object that is not present in the real world, but appears to be because of a view showing a modified version of reality. Virtual reality is similar, but instead of being in the real world, the user is viewing a simulated version of the world.

The idea behind AR was first envisioned in 1901 by author Lyman Frank Baum in the booked titled “The Master Key”. In this book the main character has a pair of glasses that, when worn, would show the type of person someone was (i.e. good or bad). Now if this author’s name sounds familiar to you, but you can’t put your finger out it, he is best known for writing “The Wizard of Oz”.

In recent years AR has become increasingly popular. It has the smartphone to thank for this. With smartphones essentially being small-scaled computers filled with motion sensors, they have become the ideal platform for creating relatively inexpensive augmented reality applications.

With the release of Windows 7, Microsoft added in a new location and sensors platform. This sensor platform has grown significantly in Windows 8, to the point where many new computers have the same sensors that are found in most smartphones. This means that creating many of the AR applications that exist for smartphones can also be created as a Windows Store app.

Sensors in Augmented Reality Apps

AR apps rely heavily on sensors. There are many different sensors that can be used. These sensors return a number of different measurements, many of them are relative to the x, y and z axis of the device. When facing the screen of the device, the x-axis runs in a left to right direction, the y-axis runs from the bottom up, and the z-axis runs from the back of the screen through to the front. Here is a diagram showing these axes relative to a device.

clip_image002

You can create a simple geospatial augmented reality app using nothing more than the location and compass sensors. The location sensor will provide you with the current location of the user and the compass will give you information on where the user is pointing the device. This will work fine in two dimensions, but if you want to take elevations into account, then you will need to look at the inclinometer sensor. The inclinometer measures the angle of rotation around the axes of the device and exposes these angles as yaw, pitch, and roll readings. Now as I mentioned, these will work fine for simple augmented reality apps, but you will likely find that things don’t move as smoothly on the screen as you would like.

Alternatively, the orientation sensor can be used instead of the inclinometer and compass. The orientation sensor combines the accelerometer, compass, and gyrometer (measures angular velocity about the axis) to report even more sensitive movements than any of those sensors can on their own. This makes the movement within the app much smoother, but also makes the mathematics much more complicated as a rotation matrix is returned by this sensor. As such, this sensor is often reserved for more complex augmented reality apps.

Tag Based Augmented Reality

There are many different types of augmented reality apps out there. The two most common types are tag or geospatial based. Tag-based AR apps use markers or tags as points on which to render AR items on top of. Tags such as QR codes are commonly used, as they can be used to identify different types of objects and their orientation can often be tracked. A good example is the Corinth Micro Anatomy Augmented appin the Windows Store which uses tags to layer different anatomy systems over top the tag. Wearing the tag on your chest might seem a bit odd at first, but when you see the app in action, it’s pretty cool and educational too.

clip_image004

If you are interested in creating this type of AR application, then I recommend taking a look at the following projects for starters:

· SLARToolkit

· ARTag

Also take a look at these blog posts:

· Augmented Reality with SLARToolkit on Windows Phone

· Augmented Reality Using C# and OpenCV

Geospatial Augmented Reality

Geospatial AR is when items are displayed in view based on their physical location. The most common app of this type shows nearby points of interests. A good example is the Nokia Here City Lens appfor Windows Phone 8.

clip_image006

Besides points of interests, these types of apps are often used to display other things:uch as;

· Pictures that have been taken around you

· Nearby information from Twitter, Wikipedia, Foursquare, Urban spoon

· Where you parked the car

· Golf course information, such as the location of the pin or hazards

Another type of data that is sometimes used are the stars. One of my favorite apps of this type is the SkyMap for Windows Phone and Windows 8. This app allows you to explore the wonders of the universe simply by pointing your device at the sky. In addition to this, you can tap on many of the objects on the screen to get more information.

clip_image008

As mentioned at the start of this blog, we are going to focus on creating this type of AR app. One of the best starting points for creating this type of app for Windows Store and Windows Phone is the GART CodePlex project. GART stands for geo augmented reality toolkit and was originally created for Windows Phone 7. Since then, support for Windows Phone 8 and, just recently, Windows Store apps has been added. This provides us with a great AR framework that already handles the orientation sensors and other calculations for us.

Creating a Bing Maps Powered App with GART

I’ve put together a nice sample app that is built on top of GART. This app makes use of Bing Maps in a four different of ways:

1. An interactive map is used to give an overhead view of all the nearby locations.

2. The app then uses the NAVTEQ point of interest data in the Bing Spatial Data Services as the data source that powers the app. By using the Bing Spatial Data Services anyone can easily create their own data source and modify this sample to use their data in minutes.

3. Rather than adding in all the functionality for calculating routes to selected locations the app launches the built-in Maps app in Windows 8 to provide this functionality. This greatly simplifies the sample application and also puts the user into an app they are familiar with when calculating directions.

4. The Bing Maps Imagery REST services are used to generate a static map image of a location which is included into an email when sharing a location.

The sample app consists of a Windows Store and a Windows Phone 8 app which includes a lot of features and functionalities, many of which are mainly customizations to the UI. Rather than going through all 1,000+ lines of the sample app the remaining section of this blog post will show how to create a simplified version of the sample. This will allow us to focus on the key aspects of creating an AR app using GART and Bing Maps. You can access the full source code for the sample application here. You can find instructions on how to run that sample on the download page. When you run the full sample application, you will be able to find nearby locations simply by pointing your computer towards them. Here is a screenshot of the full sample application.

clip_image010

Creating the Visual Studio Solution

Before we can dive into AR functionality, we need to create the Visual Studio solution for the project. The following steps outline how to do this:

1. Start by opening Visual Studios 2012 and create a new project. In the window that opens, select Visual C# -> Windows Store. Select the Blank App template. Call the application BingMapsARand press OK.

2. Add a reference to the Bing Maps SDK. To do this, right click on the References folder and press Add Reference. Select Windows -> Extensions select Bing Maps for C#, C++ and Visual Basic. If you do not see this option, ensure that you have installed the Bing Maps SDK for Windows Store apps. While you are here, also add a reference to the Microsoft Visual C++ Runtime Packageas this is required by the Bing Maps SDK when developing using C# or Visual Basic.

3. You may notice that there is a little yellow indicator on the references that you just added. The reason for this is that C++ runtime package you have to set the Active solution platform in Visual Studio to one of the following options; ARM, x86 or x64. To do this, right click on the Solution folder and select Properties. Then go to Configuration Properties -> Configuration. Find your project and under the Platformcolumn set the target platform. For this blog post, I’m going to select x86. Press OK and the yellow indicator should disappear from our references.

4. Go to the GART CodePlex project, select the Source Code tab and press the download button to download a zip file containing the latest source code for this project. Note that you need to use the source code rather than the binaries of the project, as you will need to compile the application against the different platforms (ARM, x86, x64).

5. Once the source code is downloaded unzip the file and copy it over the GeoARToolkit folder to the folder your project is in. In Visual Studios right click on the solution and select Add -> Existing Project. Select the Gart.Win8.csproj file which is in the GeoARToolkit\Lib\Win8\GART.Win8directory.

6. In the BingMapsAR project add a reference to the Gart.Win8project.

7. Verify that the solution builds without any errors. If you do see a bunch of errors, right click on the Gart.Win8project and select properties. Select the Build tab and locate the Conditional compilation symbols textbox. Ensure that this textbox contains “WIN_RT;X3D” and then try rebuilding the solution.

Implementing the GART Project

The main control in GART is called ARDisplay. GART has a number of other controls which are added to the ARDisplay. These controls include;

· VideoPreview– A panel used to show the video preview from a rear facing camera if one is available.

· WorldView– Displays a virtual world in 3D space and applies matrix math to keep the virtual world aligned with what’s seen through the camera.

· HeadingIndicator– A simple user control used to show the direction in which the device is facing (similar to a compass needle).

· OverheadMap– Displays a Bing map that remains centered on the user’s location and shows nearby locations.

All of these controls can be easily customized using templates and styles in xaml. The ARDisplay control has a property called ARItems. This is a collection of data which is rendered by GART. To add your own data GART, it must inherit from the ARItem class. In this app we will be pulling in point of interest data from the Bing Spatial Data Services, so let’s create a class that inherits from the ARItem class that we can use to represent this data. In the BingMapsAR project add a folder called Models. Right click on this folder and select Add -> New Item and create a class file called PoiItem.cs. Open this file and copy and paste the following code:

using GART.Data;

namespace BingMapsAR.Models
{
publicclass PoiItem : ARItem
{
publicstring Name { get; set; }

publicstring AddressLine { get; set; }

publicstring Locality { get; set; }

publicstring PostalCode { get; set; }

publicstring Phone { get; set; }

publicstring EntityTypeID { get; set; }

publicdouble Distance { get; set; }
}
}

Since we will be pulling in data from the Bing Spatial Data Services, we will need some classes to parse the JSON response from the service. To do this, create three class files in the Models folder called Response.cs, ResultSet.cs, and Result.cs. Open up the Response.csfile and add the following code:

using System.Runtime.Serialization;

namespace BingMapsAR.Models
{
[DataContract]
publicclass Response
{
[DataMember(Name = "d", EmitDefaultValue = false)]
public ResultSet ResultSet { get; set; }
}
}

Open up the ResultSet.csfile and add the following code:

using System.Runtime.Serialization;

namespace BingMapsAR.Models
{
[DataContract]
publicclass ResultSet
{
[DataMember(Name = "__copyright", EmitDefaultValue = false)]
publicstring Copyright { get; set; }

[DataMember(Name = "results", EmitDefaultValue = false)]
public Result[] Results { get; set; }
}
}

Open up the Result.csfile and add the following code:

using System.Runtime.Serialization;

namespace BingMapsAR.Models
{
[DataContract]
publicclass Result
{
[DataMember(Name = "LanguageCode", EmitDefaultValue = false)]
publicstring LanguageCode { get; set; }

[DataMember(Name = "Name", EmitDefaultValue = false)]
publicstring Name { get; set; }

[DataMember(Name = "DisplayName", EmitDefaultValue = false)]
publicstring DisplayName { get; set; }

[DataMember(Name = "Latitude", EmitDefaultValue = false)]
publicdouble Latitude { get; set; }

[DataMember(Name = "Longitude", EmitDefaultValue = false)]
publicdouble Longitude { get; set; }

[DataMember(Name = "AddressLine", EmitDefaultValue = false)]
publicstring AddressLine { get; set; }

[DataMember(Name = "Locality", EmitDefaultValue = false)]
publicstring Locality { get; set; }

[DataMember(Name = "AdminDistrict", EmitDefaultValue = false)]
publicstring AdminDistrict { get; set; }

[DataMember(Name = "AdminDistrict2", EmitDefaultValue = false)]
publicstring AdminDistrict2 { get; set; }

[DataMember(Name = "PostalCode", EmitDefaultValue = false)]
publicstring PostalCode { get; set; }

[DataMember(Name = "CountryRegion", EmitDefaultValue = false)]
publicstring CountryRegion { get; set; }

[DataMember(Name = "Phone", EmitDefaultValue = false)]
publicstring Phone { get; set; }

[DataMember(Name = "EntityTypeID", EmitDefaultValue = false)]
publicstring EntityTypeID { get; set; }
}
}

Next enable the app to use the user’s location, internet and webcam. To do this, open up the app manifest and go to the capabilities tab and check the Internet, Location and Webcam checkboxes:

clip_image012

Now open the App.xaml file and add a string parameter with a key name of BingCredentials and provide your Bing Maps key. Your App.xamlfile should look like this:

<Application
x:Class="BingMapsAR.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BingMapsAR">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionarySource="Common/StandardStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<x:Stringx:Key="BingCredentials">YOUR_BING_MAPS_KEY</x:String>
</ResourceDictionary>
</Application.Resources>
</Application>

Open the MainPage.xaml file and add a reference to the GART and Bing Maps controls. After that you will want to add page resources that contain styles and templates that customize how the items are rendered. First create a DataTemplate called PoiItem. This template is used to define how a location item is displayed on the screen when you point your device at it. Then create a DataTemplate called PoiPushpin. This template is used to define how a location appears on the map. Create a Style that targets the OverheadMap control from the GART project. In the main Grid add an ARDisplay control and inside of it add VideoPreview, WorldView and OverheadMap controls. Putting this all together your MainPage.xamlfile will look like this:

<Page
x:Class="BingMapsAR.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BingMapsAR"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ARControls="using:GART.Controls"
xmlns:m="using:Bing.Maps"
mc:Ignorable="d">

<Page.Resources>
<DataTemplatex:Key="PoiItem">
<BorderBorderBrush="Black"BorderThickness="2"CornerRadius="8"Background="#FF003847"Width="250">
<GridMargin="4"Tapped="PoiItem_Tapped">
<TextBlockText="{Binding Name}"Margin="5"/>
</Grid>
</Border>
</DataTemplate>

<DataTemplatex:Key="PoiPushpin">
<m:Pushpinm:MapLayer.Position="{Binding GeoLocation}"Background="#FF003847"/>
</DataTemplate>

<StyleTargetType="ARControls:OverheadMap">
<SetterProperty="Template">
<Setter.Value>
<ControlTemplateTargetType="ARControls:OverheadMap">
<Grid>
<m:Mapx:Name="Map">
<m:MapLayer>
<m:MapItemsControlItemTemplate="{StaticResource PoiPushpin}"ItemsSource="{Binding}"/>
</m:MapLayer>
</m:Map>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>

<GridBackground="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ARControls:ARDisplayx:Name="ARDisplay"LocationEnabled="True"MovementThreshold="100"LocationChanged="ARDisplay_LocationChanged"ServiceErrors="ARDisplay_ServiceErrors">
<ARControls:VideoPreviewx:Name="VideoPanel"/>
<ARControls:WorldViewx:Name="WorldView"ItemTemplate="{StaticResource PoiItem}"/>
<ARControls:OverheadMapx:Name="OverheadMap"Credentials="{StaticResource BingCredentials}"Width="300"Height="200"Margin="10"VerticalAlignment="Top"HorizontalAlignment="Right"/>
</ARControls:ARDisplay>
</Grid>
</Page>

Now open the MainPage.xaml.cs file and copy and paste the following code which contains all the required event handlers for the controls added to the MainPage.xamlfile along with some settings which we will use later. Also notice that we have added an event for when the MainPage has finished loading. In this event handler we will add an event that fires when the visibility of the main window changes. This will allow us to turn the services used by the ARDisplay on and off depending on if the app is in view. This will help conserve resources, as the services will not run in the background when the app isn’t being used.

using Bing.Maps;
using BingMapsAR.Models;
using GART.Data;
using System;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;

namespace BingMapsAR
{
publicsealedpartialclass MainPage : Page
{
/// <summary>
/// Search Radius in meters
/// </summary>
privatedouble SearchRadius = 300;

/// <summary>
/// Maximium number of items returned by the Bing Spatial Data Services
/// </summary>
privateconstint MaxResultsPerQuery = 30;

private Location LastSearchLocation;

public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}

privatevoid MainPage_Loaded(object sender, RoutedEventArgs e)
{
Window.Current.CoreWindow.VisibilityChanged += async (s, a) =>
{
if (a.Visible)
{
//Start or restart the services used by the ARDisplay
await ARDisplay.StartServices();
}
else
{
//Stop the services used by the ARDisplay to free up those resources while the app is not in use.
ARDisplay.StopServices();
}
};
}

privatevoid ARDisplay_ServiceErrors(object sender, GART.ServiceErrorsEventArgs e)
{
}

private async void ARDisplay_LocationChanged(object sender, EventArgs e)
{
}

privatevoid PoiItem_Tapped(object sender, TappedRoutedEventArgs e)
{
}
}
}

Now we will take a look at the ARDisplay_ServiceErrors event handler. In this event handler we will want to loop through all the errors and create a message that we can display to the user. Since having a rear-facing camera is optional, we will ignore any errors related to the camera. Putting this together the ARDisplay_ServiceErrorsevent handler will look like this:

privatevoid ARDisplay_ServiceErrors(object sender, GART.ServiceErrorsEventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (var error in e.Errors)
{
//Ignore errors from the camera. The camera is optional.
if (error.Service != GART.ARService.Camera)
{
sb.AppendLine(string.Format("There was a problem with {0}: {1}", error.Service, error.Exception.Message));
}
}

if (sb.Length > 0)
{
new MessageDialog(sb.ToString(), "Error").ShowAsync();
}
}

Inside the he ARDisplay_LocationChanged event handler, we will want to add the logic for finding nearby locations and adding them to the ARDisplay. Since we are using the NAVTEQ point of interest data sources from the Bing Spatial Data Services, we can add a bit of simple logic that searches the North American or European data source based on where the user is. To do this we simply need to check to see if the user’s current longitude is less than -30. If it is, then use the North American data source. We then just need to add our search parameters to the query URL and then loop through the results after downloading them and add them as PoiItem objects to the ARDisplay. Putting this together the ARDisplay_LocationChangedevent handler will look like this:

private async void ARDisplay_LocationChanged(object sender, EventArgs e)
{
// Last search location is now this location
LastSearchLocation = ARDisplay.Location;

if(LastSearchLocation != null)
{
//Remove items from ARDisplay
ARDisplay.ARItems.Clear();

//Create Search URL for Bing Spatial Data Service
string baseURL;

//Switch between the NAVTEQ POI data sets for NA and EU based on where the user is.
if (LastSearchLocation.Longitude < -30)
{
//Use the NAVTEQ NA data source: http://msdn.microsoft.com/en-us/library/hh478192.aspx
baseURL = "http://spatial.virtualearth.net/REST/v1/data/f22876ec257b474b82fe2ffcb8393150/NavteqNA/NavteqPOIs";
}
else
{
//Use the NAVTEQ EU data source: http://msdn.microsoft.com/en-us/library/hh478193.aspx
baseURL = "http://spatial.virtualearth.net/REST/v1/data/c2ae584bbccc4916a0acf75d1e6947b4/NavteqEU/NavteqPOIs";
}

string poiRequest = string.Format("{0}?spatialFilter=nearby({1:N5},{2:N5},{3:N2})&$format=json&$top={4}&key={5}",
baseURL, LastSearchLocation.Latitude, LastSearchLocation.Longitude, SearchRadius / 1000, MaxResultsPerQuery, OverheadMap.Map.Credentials);

Response response = await GetResponse(new Uri(poiRequest));

if (response != null&&
response.ResultSet != null&&
response.ResultSet.Results != null&&
response.ResultSet.Results.Length > 0)
{
//Loop through the results and create PoiItems that can be added to the ARDisplay
foreach (var r in response.ResultSet.Results)
{
Location loc = new Location(r.Latitude, r.Longitude);

PoiItem item = new PoiItem()
{
Name = r.DisplayName,
AddressLine = r.AddressLine,
Locality = r.Locality,
PostalCode = r.PostalCode,
EntityTypeID = r.EntityTypeID,
Phone = r.Phone,
GeoLocation = loc,
Distance = Math.Round(ARHelper.DistanceTo(loc, LastSearchLocation))

};
ARDisplay.ARItems.Add(item);
}

//Set the map view to show all the locations.
OverheadMap.Map.SetView(ARDisplay.Location, 15);
}
}
}

You will notice that a method called GetResponse is missing. This method is used to call REST services and serialize the response into a Response object. Add the following method to the MainPage.xaml.csfile:

private async Task<Response> GetResponse(Uri uri)
{
try
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
var response = await client.GetAsync(uri);

using (var stream = await response.Content.ReadAsStreamAsync())
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Response));

return ser.ReadObject(stream) as Response;
}
}
catch (Exception)
{
returnnull;
}
}

Inside the PoiItem_Tappedevent handler, we can add some logic to get the item the user tapped on. This may be useful if you want to get additional details about the selected location. You can use the following code to get the item that the user tapped on.

privatevoid PoiItem_Tapped(object sender, TappedRoutedEventArgs e)
{
ARItem selectedItem = null;

if (sender is Grid)
{
selectedItem = (sender as Grid).DataContext as ARItem;

//Do something with the selected item
}
}

At this point you have all the functionality needed to run this simple AR app. Here is a screenshot of the app near where I live.

clip_image014

Changing the Data Source

Since this sample app makes use of data stored in the Bing Spatial Data Services, you can create your own data source to create a custom AR app. You can find information on how to create and upload your own data source to the Bing Spatial Data Services here.

Once you have a data source uploaded to the Bing Spatial Data Services you can modify the BingMapsAR project to make use of your data. The first step is to update the PoiItem.cs file which is located in the Models folder. This file describes the format of the data in your data source. Simply add and remove properties such that they match the column header names and types in your data source. Next open the MainPage.xaml.cs file and find the ARDisplay_LocationChanged event handler. In this event handler is some logic for switching between the NAVTEQ NA and EU data sources based on where the user is. Remove this logic and set the baseURL value to the query URL of your data source. Details on how to find the query URL of your data source can be found here. Once you have done this, you can customize what information is being displayed to the user by changing the properties being bound in the ItemPanel and PoiItem DataTemplate in the MainPage.xamlfile.

- Ricky Brundritt, EMEA Bing Maps Technology Solution Professional

Retrieving Boundaries from the Bing Spatial Data Services (Preview)

$
0
0

The Bing Spatial Data Services (SDS) allow you to submit large amounts of addresses for batch-geocoding as well as GPS-coordinates for reverse geocoding. You can download the results or keep them in our data centers and retrieve your points of interest (POI) within a distance of a specific location or along a route through the SDS Query API. Aside from your own POI which you can manage through the SDS, you can also query POI from our public data sources which are grouped into categories.

These API and data are useful for a variety of scenarios and are most often used in typical “locator” scenarios where you want to find stores, dealerships, etc.

Today we are beginning to preview a new capability of the SDS which allows you to not only retrieve points but also polygons. Initially, this includes boundaries for countries, administrative levels and more. This new capability is exposed through the GeoData API and could be useful to highlight areas of interest…

 

RetrievingBoundaries-Map1

…or create “thematic maps” where you color-code regions based on key performance indicators (KPI) such as the revenue, number of customers, crime statistics, environmental data, etc.

RetrievingBoundaries-Map2

You’ll find the preliminary documentation for the preview here. So, rather than talking about different parameters let’s go straight into a sample where we retrieve a polygon and visualize it in Windows Store app.

Prerequisites

Since we are developing a Windows Store App, we require access to a Windows 8 machine as well as Visual Studio 2012. A free version of Visual Studio Express 2012 for Windows 8 is available here.

The “Bing Maps SDK or Windows Store Apps” can be installed directly from Visual Studio by selecting “Extensions and Updates” from the menu “Tools” and searching in the online gallery.

We will also require a Bing Maps Key. If you don’t have one, you can follow these instructions to get a free trial or basic key.

Getting Started

We start by creating a new project. For this example, we select the template for JavaScript applications in the Windows Store and create a ‘Blank App’.

RetrievingBoundaries-NewProject

Next we add a reference to the ‘Bing Maps for JavaScript’ control.

RetrievingBoundaries-SolutionExplorer

Let’s code

In the head of default.html we add script-references to load the Bing Maps core as well as additional modules.

<!--  Bing Maps --><scriptsrc="/Bing.Maps.JavaScript/js/veapicore.js"></script><scriptsrc="/Bing.Maps.JavaScript/js/veapiModules.js"></script>

In the body of default.html we just add a div-element that will host our map.

<divid="divMap"></div>

Moving on to the JavaScript default.js we add a few global variables for the map, the base-URL for the GeoData API as well as a list of safe characters. We will come back to these safe characters later.

var map = null;var baseUrl = "http://platform.bing.com/geo/spatial/v1/public/geodata?SpatialFilter=";var safeCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";

Just underneath the line…

app.start();

…we add an event-handler that fires when the entire DOM content is loaded…

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

…and executes the function initialize. The following code will be in between those two lines.

We start by loading the Bing Maps core and once this is done, we execute a function initMap.

function initialize() {
Microsoft.Maps.loadModule('Microsoft.Maps.Map', { callback: initMap });
}

The function initMap defines center-point and zoom-level of the map as well as a few other options and renders the map in the div-element we reserved for it. Then we move on to load the AdvancedShapes-module.

function initMap() {
try {
var mapOptions =
{
credentials: "Your Bing Maps Key",
mapTypeId: "r",
enableClickableLogo: false,
enableSearchLogo: false,
center: new Microsoft.Maps.Location(47.490860, -121.835747),
zoom:9
};
map = new Microsoft.Maps.Map(document.getElementById("divMap"), mapOptions); loadAdvancedShapeModule();
}
catch (e) {
var md = new Windows.UI.Popups.MessageDialog(e.message);
md.showAsync();
} }

Loading the AdvancedShape-module may not be necessary, but it will be useful whenever you want to render polygons with holes. If you wanted to render, for example, the polygon that represents Italy, but exclude the enclosed areas representing San Marino and Vatican City, you would require the AdvancedShape-module.

RetrievingBoundaries-AdvancedShapeModule

When the module is loaded, we execute a function that calls the GeoData API and retrieves the polygon.

function loadAdvancedShapeModule() {
Microsoft.Maps.loadModule('Microsoft.Maps.AdvancedShapes', {  callback: getBoundary
});
}

Notice that we retrieve the credentials from the map rather than just re-using the same key that we specified for the map. By doing so we generate a session-key which will indicate to the transaction counting and reporting system in our backend that this call originated within a map control. Such transactions appear as “non-billable” in your usage reports. For more information on this particular aspect, see Viewing Bing Maps Usage Reports.

When we call the GeoData API, we can provide either an address string or a latitude and longitude as parameter. In this case, we use the string “King County”. We also specify a few more parameters which you will find explained in more detail in the documentation before we execute the request.

function getBoundary() {
map.entities.clear(); map.getCredentials(function (credentials) {
var boundaryUrl = baseUrl
+ "GetBoundary('King County',1,'AdminDivision2',0,0,'en','US')&$format=json&key="
+ credentials;
WinJS.xhr({ url: boundaryUrl }).then(boundaryCallback);
});
}

To optimize for performance, the response is highly compressed and therefore we call a function ParseEncodedValue for each polygon to decompress it. We also define stroke- and fill-color before we add the polygon to the map.

function boundaryCallback(result) {
result = JSON.parse(result.responseText);var entity = result.d.results[0];
var entityMetadata = entity.EntityMetadata;
var entityName = entity.Name.EntityName;
var primitives = entity.Primitives;var polygoncolor = null;
var strokecolor = null;
var boundaryVertices = null;
var numOfVertices = 0; polygoncolor = new Microsoft.Maps.Color(100, 128, 128, 128);
strokecolor = new Microsoft.Maps.Color(255, 128, 128, 128);var polygonArray = new Array();
for (var i = 0; i < primitives.length; i++) {
var ringStr = primitives[i].Shape;var ringArray = ringStr.split(",");for (var j = 1; j < ringArray.length; j++) {
var array = ParseEncodedValue(ringArray[j]);if (array.length > numOfVertices) {
numOfVertices = array.length;
boundaryVertices = array;
}
polygonArray.push(array); }var polygon = new Microsoft.Maps.Polygon(polygonArray,
{ fillColor: polygoncolor, strokeColor: strokecolor });
map.entities.push(polygon)
}
}

The compression algorithm is the same as the one we documented for the Elevations API.

function ParseEncodedValue(value) {
var list = new Array();
var index = 0;
var xsum = 0;
var ysum = 0;var max = 4294967296;while (index < value.length) {
var n = 0;var k = 0;while (1) {
if (index >= value.length)
{
return null;
}
var b = safeCharacters.indexOf(value.charAt(index++));
if (b == -1) {
return null;
}
var tmp = ((b & 31) * (Math.pow(2, k)));var ht = tmp / max;var lt = tmp % max; var hn = n / max;var ln = n % max; var nl = (lt | ln) >>> 0;
n = (ht | hn) * max + nl;
k += 5;
if (b < 32) break; }var diagonal = parseInt((Math.sqrt(8 * n + 5) - 1) / 2);
n -= diagonal * (diagonal + 1) / 2;
var ny = parseInt(n);
var nx = diagonal - ny;
nx = (nx >> 1) ^ -(nx & 1);
ny = (ny >> 1) ^ -(ny & 1);
xsum += nx;
ysum += ny;
var lat = ysum * 0.00001;
var lon = xsum * 0.00001
list.push(new Microsoft.Maps.Location(lat, lon));
}
return list;
}

And that’s it.

We have a lot of ideas on what we want to add in the future, but we always welcome your feedback on what works well and what else you would like to see. Please let us know via the forum.

Build 2013 Announcements: Developer Services, New Bing Developer Center, and Much More

$
0
0

A new set of developer services and the new Bing Developer Center were announced at Build 2013 today. The new streamlined Bing Developer Center puts all the content you need in one single location, with links to documentation, downloads, sample code, how-to’s, as well as links to partner blogs where you can find even more technical content. 

Check out the blog on the Bing Developer Center to learn about the new version of the Bing Maps SDK for Windows 8 and a beta-release of a functional equivalent for the Windows 8.1 Preview along with other cool announcements.

Read full blog post.

Viewing all 102 articles
Browse latest View live