2017-08-04 21:38:58 +02:00
|
|
|
|
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
2019-03-27 18:39:59 +01:00
|
|
|
|
// © 2019 Clemens Fischer
|
2017-08-04 21:38:58 +02:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.Linq;
|
2019-11-07 22:34:25 +01:00
|
|
|
|
using System.Xml.Linq;
|
|
|
|
|
|
using System.Threading.Tasks;
|
2017-08-04 21:38:58 +02:00
|
|
|
|
#if WINDOWS_UWP
|
|
|
|
|
|
using Windows.UI.Xaml;
|
|
|
|
|
|
#else
|
|
|
|
|
|
using System.Windows;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
namespace MapControl
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Displays Bing Maps tiles. The static ApiKey property must be set to a Bing Maps API Key.
|
|
|
|
|
|
/// Tile image URLs and min/max zoom levels are retrieved from the Imagery Metadata Service
|
|
|
|
|
|
/// (see http://msdn.microsoft.com/en-us/library/ff701716.aspx).
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class BingMapsTileLayer : MapTileLayer
|
|
|
|
|
|
{
|
2019-11-07 22:34:25 +01:00
|
|
|
|
private static readonly XNamespace imageryMetadataNamespace = "http://schemas.microsoft.com/search/local/ws/rest/v1";
|
|
|
|
|
|
|
2017-08-04 21:38:58 +02:00
|
|
|
|
public enum MapMode
|
|
|
|
|
|
{
|
|
|
|
|
|
Road, Aerial, AerialWithLabels
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public BingMapsTileLayer()
|
|
|
|
|
|
: this(new TileImageLoader())
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public BingMapsTileLayer(ITileImageLoader tileImageLoader)
|
|
|
|
|
|
: base(tileImageLoader)
|
|
|
|
|
|
{
|
|
|
|
|
|
MinZoomLevel = 1;
|
|
|
|
|
|
MaxZoomLevel = 21;
|
|
|
|
|
|
Loaded += OnLoaded;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static string ApiKey { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public MapMode Mode { get; set; }
|
|
|
|
|
|
public string Culture { get; set; }
|
2017-10-07 17:43:28 +02:00
|
|
|
|
public Uri LogoImageUri { get; private set; }
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
2019-11-07 22:34:25 +01:00
|
|
|
|
private async void OnLoaded(object sender, RoutedEventArgs args)
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
|
|
|
|
|
Loaded -= OnLoaded;
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(ApiKey))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.WriteLine("BingMapsTileLayer requires a Bing Maps API Key");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-07 22:34:25 +01:00
|
|
|
|
var uri = "http://dev.virtualearth.net/REST/V1/Imagery/Metadata/" + Mode + "?output=xml&key=" + ApiKey;
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2019-11-07 22:34:25 +01:00
|
|
|
|
var document = await Task.Run(() => XDocument.Load(uri));
|
|
|
|
|
|
var imageryMetadata = document.Descendants(imageryMetadataNamespace + "ImageryMetadata").FirstOrDefault();
|
|
|
|
|
|
var brandLogoUri = document.Descendants(imageryMetadataNamespace + "BrandLogoUri").FirstOrDefault();
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
|
|
|
|
|
if (imageryMetadata != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ReadImageryMetadata(imageryMetadata);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (brandLogoUri != null)
|
|
|
|
|
|
{
|
2019-11-07 22:34:25 +01:00
|
|
|
|
LogoImageUri = new Uri(brandLogoUri.Value);
|
2017-08-04 21:38:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2019-11-07 22:34:25 +01:00
|
|
|
|
Debug.WriteLine("BingMapsTileLayer: {0}: {1}", uri, ex.Message);
|
2017-08-04 21:38:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-07 22:34:25 +01:00
|
|
|
|
private void ReadImageryMetadata(XElement imageryMetadata)
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
2019-11-07 22:34:25 +01:00
|
|
|
|
var imageUrl = imageryMetadata.Element(imageryMetadataNamespace + "ImageUrl")?.Value;
|
|
|
|
|
|
var imageUrlSubdomains = imageryMetadata.Element(imageryMetadataNamespace + "ImageUrlSubdomains")?
|
|
|
|
|
|
.Elements()
|
|
|
|
|
|
.Where(e => e.Name.LocalName == "string")
|
|
|
|
|
|
.Select(e => e.Value)
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(imageUrl) &&
|
|
|
|
|
|
imageUrlSubdomains != null &&
|
|
|
|
|
|
imageUrlSubdomains.Length > 0)
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
2019-11-07 22:34:25 +01:00
|
|
|
|
var zoomMin = imageryMetadata.Element(imageryMetadataNamespace + "ZoomMin")?.Value;
|
|
|
|
|
|
var zoomMax = imageryMetadata.Element(imageryMetadataNamespace + "ZoomMax")?.Value;
|
2019-11-07 22:40:46 +01:00
|
|
|
|
int zoomLevel;
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
2019-11-07 22:34:25 +01:00
|
|
|
|
if (!string.IsNullOrEmpty(zoomMin) &&
|
2019-11-07 22:40:46 +01:00
|
|
|
|
int.TryParse(zoomMin, out zoomLevel) &&
|
|
|
|
|
|
MinZoomLevel < zoomLevel)
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
2019-11-07 22:40:46 +01:00
|
|
|
|
MinZoomLevel = zoomLevel;
|
2017-08-04 21:38:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-07 22:34:25 +01:00
|
|
|
|
if (!string.IsNullOrEmpty(zoomMax) &&
|
2019-11-07 22:40:46 +01:00
|
|
|
|
int.TryParse(zoomMax, out zoomLevel) &&
|
|
|
|
|
|
MaxZoomLevel > zoomLevel)
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
2019-11-07 22:40:46 +01:00
|
|
|
|
MaxZoomLevel = zoomLevel;
|
2017-08-04 21:38:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(Culture))
|
|
|
|
|
|
{
|
|
|
|
|
|
Culture = CultureInfo.CurrentUICulture.Name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileSource = new BingMapsTileSource(imageUrl.Replace("{culture}", Culture), imageUrlSubdomains);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|