2025-03-31 21:33:52 +02:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using System;
|
2017-08-04 21:38:58 +02:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.Linq;
|
2019-11-07 22:34:25 +01:00
|
|
|
|
using System.Xml.Linq;
|
2024-05-22 11:25:32 +02:00
|
|
|
|
#if WPF
|
|
|
|
|
|
using System.Windows;
|
2021-11-17 23:17:11 +01:00
|
|
|
|
#elif UWP
|
2017-08-04 21:38:58 +02:00
|
|
|
|
using Windows.UI.Xaml;
|
2024-05-22 11:25:32 +02:00
|
|
|
|
#elif WINUI
|
|
|
|
|
|
using Microsoft.UI.Xaml;
|
2017-08-04 21:38:58 +02:00
|
|
|
|
#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
|
|
|
|
|
|
{
|
|
|
|
|
|
public enum MapMode
|
|
|
|
|
|
{
|
|
|
|
|
|
Road, Aerial, AerialWithLabels
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public BingMapsTileLayer()
|
|
|
|
|
|
{
|
|
|
|
|
|
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
|
|
|
|
|
2025-02-07 11:12:24 +01:00
|
|
|
|
private async void OnLoaded(object sender, RoutedEventArgs e)
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
|
|
|
|
|
Loaded -= OnLoaded;
|
|
|
|
|
|
|
2020-04-14 22:41:34 +02:00
|
|
|
|
if (!string.IsNullOrEmpty(ApiKey))
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
2022-08-18 22:30:22 +02:00
|
|
|
|
var metadataUri = $"https://dev.virtualearth.net/REST/V1/Imagery/Metadata/{Mode}?output=xml&key={ApiKey}";
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
2020-04-14 22:41:34 +02:00
|
|
|
|
try
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
2020-04-14 22:41:34 +02:00
|
|
|
|
using (var stream = await ImageLoader.HttpClient.GetStreamAsync(metadataUri))
|
|
|
|
|
|
{
|
|
|
|
|
|
ReadImageryMetadata(XDocument.Load(stream).Root);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2025-03-31 21:33:52 +02:00
|
|
|
|
ImageLoader.LoggerFactory?.CreateLogger<BingMapsTileLayer>()?.LogError(ex, "{uri}", metadataUri);
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
2017-08-04 21:38:58 +02:00
|
|
|
|
}
|
2020-04-14 22:41:34 +02:00
|
|
|
|
else
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
2025-03-31 21:33:52 +02:00
|
|
|
|
ImageLoader.LoggerFactory?.CreateLogger<BingMapsTileLayer>()?.LogError("Bing Maps API Key required");
|
2017-08-04 21:38:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-20 18:12:56 +01:00
|
|
|
|
private void ReadImageryMetadata(XElement metadataResponse)
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
2019-11-08 17:26:27 +01:00
|
|
|
|
var ns = metadataResponse.Name.Namespace;
|
|
|
|
|
|
var metadata = metadataResponse.Descendants(ns + "ImageryMetadata").FirstOrDefault();
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
2019-11-08 17:26:27 +01:00
|
|
|
|
if (metadata != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var imageUrl = metadata.Element(ns + "ImageUrl")?.Value;
|
|
|
|
|
|
var subdomains = metadata.Element(ns + "ImageUrlSubdomains")?.Elements(ns + "string").Select(e => e.Value).ToArray();
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
2019-11-08 17:26:27 +01:00
|
|
|
|
if (!string.IsNullOrEmpty(imageUrl) && subdomains != null && subdomains.Length > 0)
|
2017-08-04 21:38:58 +02:00
|
|
|
|
{
|
2019-11-08 17:26:27 +01:00
|
|
|
|
var zoomMin = metadata.Element(ns + "ZoomMin")?.Value;
|
|
|
|
|
|
var zoomMax = metadata.Element(ns + "ZoomMax")?.Value;
|
|
|
|
|
|
|
2020-04-16 19:24:38 +02:00
|
|
|
|
if (zoomMin != null && int.TryParse(zoomMin, out int zoomLevel) && MinZoomLevel < zoomLevel)
|
2019-11-08 17:26:27 +01:00
|
|
|
|
{
|
|
|
|
|
|
MinZoomLevel = zoomLevel;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (zoomMax != null && int.TryParse(zoomMax, out zoomLevel) && MaxZoomLevel > zoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
MaxZoomLevel = zoomLevel;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(Culture))
|
|
|
|
|
|
{
|
|
|
|
|
|
Culture = CultureInfo.CurrentUICulture.Name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-23 23:35:48 +02:00
|
|
|
|
TileSource = new BingMapsTileSource
|
|
|
|
|
|
{
|
2022-08-23 17:20:16 +02:00
|
|
|
|
UriTemplate = imageUrl.Replace("{culture}", Culture),
|
2020-10-23 23:35:48 +02:00
|
|
|
|
Subdomains = subdomains
|
|
|
|
|
|
};
|
2017-08-04 21:38:58 +02:00
|
|
|
|
}
|
2019-11-08 17:26:27 +01:00
|
|
|
|
}
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
2019-11-08 17:26:27 +01:00
|
|
|
|
var logoUri = metadataResponse.Element(ns + "BrandLogoUri");
|
2017-08-04 21:38:58 +02:00
|
|
|
|
|
2019-11-08 17:26:27 +01:00
|
|
|
|
if (logoUri != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogoImageUri = new Uri(logoUri.Value);
|
2017-08-04 21:38:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|