Universal App – Bing Map : Get current location, show pins and search near by

Years ago I wrote a post for using Bing Map in Windows Phone Windows Phone – Embedded fonts and Bing maps. Following a new trend with Universal App, this post shows how we can host a Bing Map control in Universal App and execute some actions such as show location, pins and search POIs near by.

1. Prerequisites

– I use Caliburn.Micro for MVVM pattern. How to install and configure Caliburn.Micro in Universal App, please follow this post Universal app – Windows 10 Action Center Toast Notification.
– You need a Bing Map Key for your app. Go to https://www.bingmapsportal.com/, sign in with your Microsoft Account. Under My account –> Create or view keys –> Click here to create a new key.

Create or view keys

Create key

Enter application name, application URL then Create, a new key will be created, you’ll need this key later.

BingMapKey

2. MapControl

To host Bing Map in Universal App, we can use the MapControl to embed a map in our app. Remember to set your Bing Map Key which you get from https://www.bingmapsportal.com/.

<Page
    x:Class="Bing_Map.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Bing_Map.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:maps="using:Windows.UI.Xaml.Controls.Maps"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <maps:MapControl x:Name="BingMapControl" MapServiceToken="3fignS747tcub8VO2g3J~lqMcYtdv20oua4a2bFKTGg~At6X_m9DfwNspO2WyaPvxTF6htt3X84oCiWPW2SmKMvhPuzSyyoK14nVNONwJlLw" />
    </Grid>
</Page>

2.1 Show current location

If you want to show your current location on Bing Map, enable Location from Capabilities, get current location and set map center to that point.

Location from Capabilities

Enable this option to provide access to the current location, which is obtained from dedicated hardware like a GPS sensor in the PC or derived from available network information.

public async Task<Geopoint> GetCurrentLocation()
{
	try
	{
		Geolocator geolocator = new Geolocator();
		Geoposition geoposition = await geolocator.GetGeopositionAsync();
		return geoposition.Coordinate.Point;
	}
	catch (Exception ex)
	{
		HttpClient httpClient = new HttpClient();
		httpClient.BaseAddress = new Uri("http://api.hostip.info/");
		string response = await httpClient.GetStringAsync("");
		XmlUtil xmlUtil = new XmlUtil(response);
		string coordinate = xmlUtil.Get(
			"featureMember.Hostip.ipLocation.pointProperty.Point.coordinates").Value;

		BasicGeoposition basicGeoposition = new BasicGeoposition();
		string[] coordinates = coordinate.Split(",".ToCharArray());
		if (coordinates.Length == 2)
		{
			double temp;
			if (double.TryParse(coordinates[0], out temp))
				basicGeoposition.Longitude = temp;
			if (double.TryParse(coordinates[1], out temp))
				basicGeoposition.Latitude = temp;
			return new Geopoint(basicGeoposition);
		}
		return new Geopoint(basicGeoposition);
	}
}

The code listing above uses Geolocator to get your current position. If Geolocator doesn’t work, I’ll use api.hostip.info to get current location through IP instead. Then navigating to current location by setting Center property of MapControl to that position.

private void SetMapLocation(Geopoint coordinate)
{
	mapControl.Center = coordinate;
}

The coordinate will be localized in the center of the map.

2.2 Show pin

If you want to add pin to a location, you can use the code listing below. The pin image can be loaded from Assets resource.

private void SetMapIcon(Geopoint coordinate)
{
	MapIcon mapIcon = new MapIcon
	{
		Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/LocationIcon.png")),
		Location = coordinate,
		NormalizedAnchorPoint = new Point(0.5, 1.0),
		Title = "Your current location"
	};
	mapControl.MapElements.Add(mapIcon);
}

The NormalizedAnchorPoint is the point on the child element that is positioned at the geographic location on the MapControl specified by the MapControl.Location attached property. The value of NormalizedAnchorPoint represents the corner of the child element.

NormalizedAnchorPoint

2.3 Search near by

The MapControl doesn’t support search POI directly. For searching POI with Bing Map, we can subscribe Public Data Sources of Bing Spatial Data Services. I’m living in Germany so I would like to use NAVTEQEU DataSource.

For example, code listing below find gas stations near by within radius of 5 km and show on MapControl.

private async Task Initialize()
{
	...
	await SetNearbyPlaces(currentLocation, NAVTEQEUEnum.PetrolGasolineStation);
}

private async Task SetNearbyPlaces(Geopoint currentLocation, NAVTEQEUEnum navteqeuEnum)
{
	IList<Geopoint> geoPoints = await navteqeuDataSource.SearchNearBy(currentLocation.Position.Latitude, currentLocation.Position.Longitude, 5,
		(int)navteqeuEnum, 5, ConstantValues.BingMapKey);

	foreach (var geoPoint in geoPoints)
	{
		MapIcon mapIcon = new MapIcon
		{
			Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/GasPump.png")),
			Location = geoPoint,
			NormalizedAnchorPoint = new Point(0.5, 0.5),
			Title = "Gas station"
		};
		mapControl.MapElements.Add(mapIcon);
	}
}

public async Task<IList<Geopoint>> SearchNearBy(double latitude, double longitude, double radius, int entityTypeId, int maxResult, string bingMapKey)
{
	const string spatialBaseUrl = "http://spatial.virtualearth.net/REST/v1/data/";
	string url =
		"c2ae584bbccc4916a0acf75d1e6947b4/NavteqEU/NavteqPOIs?spatialFilter=nearby({0},{1},{2})&$filter=EntityTypeID%20eq%20'{3}'&$select=EntityID,DisplayName,Latitude,Longitude,__Distance&$top={4}&key={5}";
	HttpClient httpClient = new HttpClient { BaseAddress = new Uri(spatialBaseUrl) };
	url = string.Format(url, latitude, longitude, radius, entityTypeId, maxResult, bingMapKey);
	string response = await httpClient.GetStringAsync(url);
	XmlUtil xmlUtil = new XmlUtil(response);
	IList<XElement> properties = xmlUtil.GetElements("entry").ToList();
	IList<Geopoint> result = new List<Geopoint>();
	foreach (var property in properties)
	{
		BasicGeoposition basicGeoposition = new BasicGeoposition();

		double temp;
		if (double.TryParse(xmlUtil.GetRelativeElement(property, "content.properties.Latitude").Value, out temp))
			basicGeoposition.Latitude = temp;
		if (double.TryParse(xmlUtil.GetRelativeElement(property, "content.properties.Longitude").Value, out temp))
			basicGeoposition.Longitude = temp;
		result.Add(new Geopoint(basicGeoposition));
	}

	return result;
}

The NAVTEQEUEnum represents the list of supported entities. This list of POI Entities is available at POI Enitity Types. The POI coordinates are provided by REST service of Bing Spatial Data Services. Data can be queried with HttpClient as usual.

2.4 Search traffic incidents

Bing Map provides us also API for getting traffic incidents. Not like POI which we can call from Bing Spatial Service, traffic incidents are provided by Bing Map REST Service. The following code listing shows how we can make a HTTP request to Bing Map REST Service, parse JSON result back to object then show points on MapControl

private async Task SearchNearbyIncidents(Geopoint location)
{
	IList<Geopoint> geoPoints = await bingMapRestService.GetIncidents(MapUtil.GetBoundingBox(location.Position, 5), ConstantValues.BingMapKey);
	foreach (var geoPoint in geoPoints)
	{
		MapIcon mapIcon = new MapIcon
		{
			Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/TrafficYield.png")),
			Location = geoPoint,
			NormalizedAnchorPoint = new Point(0.5, 0.5),
			Title = "Incidents"
		};
		mapControl.MapElements.Add(mapIcon);
	}
}

internal class BingMapRESTService : IBingMapRESTService
{
	public async Task<IList<Geopoint>> GetIncidents(MapUtil.BoundingBox boundingBox, string bingMapKey)
	{
		try
		{
			const string spatialBaseUrl = "http://dev.virtualearth.net/REST/v1/Traffic/Incidents/";
			string url =
				"{0},{1},{2},{3}?key={4}";
			HttpClient httpClient = new HttpClient { BaseAddress = new Uri(spatialBaseUrl) };
			url = string.Format(url, boundingBox.MinPoint.Latitude, boundingBox.MinPoint.Longitude, boundingBox.MaxPoint.Latitude, boundingBox.MaxPoint.Longitude, bingMapKey);
			string response = await httpClient.GetStringAsync(url);
			BingMapTrafficIncidentData data = JsonConvert.DeserializeObject<BingMapTrafficIncidentData>(response);
			IList<string> coordinates =
				data.ResourceSets.SelectMany(x => x.Resources).Select(x => StringJoin(x.Point.Coordinates)).ToList();
			IList<Geopoint> result = new List<Geopoint>();
			foreach (var coordinate in coordinates)
			{
				result.Add(MapUtil.GetGeopoint(coordinate));
			}
			return result;
		}
		catch (Exception ex)
		{
			return null;
		}
	}

	private string StringJoin(double[] coordinates)
	{
		return string.Join(",", coordinates);
	}
}

3. Source code

Source code: https://bitbucket.org/hintdesk/dotnet-universal-app-bing-map-get-current-location-show-pins

The HDUniLib used in sample is my custom library for universal app. The library is referenced as NuGet packaged in sample but if you need the source code. You can check out from following link.

Source code library: https://bitbucket.org/hintdesk/dotnet-hdunilib

Leave a Reply

Your email address will not be published. Required fields are marked *