// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control // © 2020 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; #if WINDOWS_UWP using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; #else using System.Windows; using System.Windows.Media; #endif namespace MapControl { /// /// Displays a single map image from a Web Map Service (WMS). /// public partial class WmsImageLayer : MapImageLayer { public static readonly DependencyProperty ServiceUriProperty = DependencyProperty.Register( nameof(ServiceUri), typeof(Uri), typeof(WmsImageLayer), new PropertyMetadata(null, async (o, e) => await ((WmsImageLayer)o).UpdateImageAsync())); public static readonly DependencyProperty LayersProperty = DependencyProperty.Register( nameof(Layers), typeof(string), typeof(WmsImageLayer), new PropertyMetadata(null, async (o, e) => await ((WmsImageLayer)o).UpdateImageAsync())); public static readonly DependencyProperty StylesProperty = DependencyProperty.Register( nameof(Styles), typeof(string), typeof(WmsImageLayer), new PropertyMetadata(string.Empty, async (o, e) => await ((WmsImageLayer)o).UpdateImageAsync())); public WmsImageLayer() { foreach (FrameworkElement child in Children) { child.UseLayoutRounding = true; } } /// /// The base request URL. /// public Uri ServiceUri { get { return (Uri)GetValue(ServiceUriProperty); } set { SetValue(ServiceUriProperty, value); } } /// /// Comma-separated list of Layer names to be displayed. If not set, the first Layer is displayed. /// public string Layers { get { return (string)GetValue(LayersProperty); } set { SetValue(LayersProperty, value); } } /// /// Comma-separated list of requested styles. Default is an empty string. /// public string Styles { get { return (string)GetValue(StylesProperty); } set { SetValue(StylesProperty, value); } } /// /// Gets a list of all layer names returned by a GetCapabilities response. /// public async Task> GetLayerNamesAsync() { IEnumerable layerNames = null; var capabilities = await GetCapabilitiesAsync(); if (capabilities != null) { var ns = capabilities.Name.Namespace; layerNames = capabilities .Descendants(ns + "Layer") .Select(e => e.Element(ns + "Name")?.Value) .Where(n => !string.IsNullOrEmpty(n)); } return layerNames; } /// /// Loads an XElement from the URL returned by GetCapabilitiesRequestUri(). /// public async Task GetCapabilitiesAsync() { XElement element = null; if (ServiceUri != null) { var uri = GetCapabilitiesRequestUri(); try { using (var stream = await ImageLoader.HttpClient.GetStreamAsync(uri)) { element = XDocument.Load(stream).Root; } } catch (Exception ex) { Debug.WriteLine("WmsImageLayer: {0}: {1}", uri, ex.Message); } } return element; } /// /// Gets a response string from the URL returned by GetFeatureInfoRequestUri(). /// public async Task GetFeatureInfoTextAsync(Point position, string format = "text/plain") { string response = null; if (ServiceUri != null) { var uri = GetFeatureInfoRequestUri(position, format); try { response = await ImageLoader.HttpClient.GetStringAsync(uri); } catch (Exception ex) { Debug.WriteLine("WmsImageLayer: {0}: {1}", uri, ex.Message); } } return response; } /// /// Loads an XElement from the URL returned by GetFeatureInfoRequestUri(). /// public async Task GetFeatureInfoAsync(Point position) { XElement element = null; if (ServiceUri != null) { var uri = GetFeatureInfoRequestUri(position, "text/xml"); try { using (var stream = await ImageLoader.HttpClient.GetStreamAsync(uri)) { element = XDocument.Load(stream).Root; } } catch (Exception ex) { Debug.WriteLine("WmsImageLayer: {0}: {1}", uri, ex.Message); } } return element; } /// /// Loads an ImageSource from the URL returned by GetMapRequestUri(). /// protected override async Task GetImageAsync() { ImageSource image = null; if (ServiceUri != null) { if (Layers == null && ServiceUri.ToString().IndexOf("LAYERS=", StringComparison.OrdinalIgnoreCase) < 0) { Layers = (await GetLayerNamesAsync())?.FirstOrDefault() ?? ""; // get first Layer from Capabilities } var uri = GetMapRequestUri(); if (!string.IsNullOrEmpty(uri)) { image = await ImageLoader.LoadImageAsync(new Uri(uri)); } } return image; } /// /// Returns a GetCapabilities request URL string. /// protected virtual string GetCapabilitiesRequestUri() { return GetRequestUri("GetCapabilities").Replace(" ", "%20"); } /// /// Returns a GetMap request URL string. /// protected virtual string GetMapRequestUri() { string uri = null; var projection = ParentMap?.MapProjection; if (projection != null && !string.IsNullOrEmpty(projection.CrsId)) { uri = GetRequestUri("GetMap"); if (uri.IndexOf("LAYERS=", StringComparison.OrdinalIgnoreCase) < 0 && Layers != null) { uri += "&LAYERS=" + Layers; } if (uri.IndexOf("STYLES=", StringComparison.OrdinalIgnoreCase) < 0 && Styles != null) { uri += "&STYLES=" + Styles; } if (uri.IndexOf("FORMAT=", StringComparison.OrdinalIgnoreCase) < 0) { uri += "&FORMAT=image/png"; } var mapRect = projection.BoundingBoxToRect(BoundingBox); var viewScale = ParentMap.ViewTransform.Scale; uri += "&CRS=" + projection.GetCrsValue(); uri += "&BBOX=" + projection.GetBboxValue(mapRect); uri += "&WIDTH=" + (int)Math.Round(viewScale * mapRect.Width); uri += "&HEIGHT=" + (int)Math.Round(viewScale * mapRect.Height); } return uri.Replace(" ", "%20"); } /// /// Returns a GetFeatureInfo request URL string. /// protected virtual string GetFeatureInfoRequestUri(Point position, string format) { string uri = null; var projection = ParentMap?.MapProjection; if (projection != null && !string.IsNullOrEmpty(projection.CrsId)) { uri = GetRequestUri("GetFeatureInfo"); var i = uri.IndexOf("LAYERS=", StringComparison.OrdinalIgnoreCase); if (i >= 0) { i += 7; var j = uri.IndexOf('&', i); var layers = j >= i ? uri.Substring(i, j - i) : uri.Substring(i); uri += "&QUERY_LAYERS=" + layers; } else if (Layers != null) { uri += "&LAYERS=" + Layers; uri += "&QUERY_LAYERS=" + Layers; } var mapRect = projection.BoundingBoxToRect(BoundingBox); var viewRect = GetViewRectangle(mapRect); var viewSize = ParentMap.RenderSize; var transform = new Matrix(1, 0, 0, 1, -viewSize.Width / 2, -viewSize.Height / 2); transform.Rotate(-viewRect.Rotation); transform.Translate(viewRect.Width / 2, viewRect.Height / 2); var imagePos = transform.Transform(position); uri += "&CRS=" + projection.GetCrsValue(); uri += "&BBOX=" + projection.GetBboxValue(mapRect); uri += "&WIDTH=" + (int)Math.Round(viewRect.Width); uri += "&HEIGHT=" + (int)Math.Round(viewRect.Height); uri += "&I=" + (int)Math.Round(imagePos.X); uri += "&J=" + (int)Math.Round(imagePos.Y); uri += "&INFO_FORMAT=" + format; } return uri.Replace(" ", "%20"); } private string GetRequestUri(string request) { var uri = ServiceUri.ToString(); if (!uri.EndsWith("?") && !uri.EndsWith("&")) { uri += !uri.Contains("?") ? "?" : "&"; } if (uri.IndexOf("SERVICE=", StringComparison.OrdinalIgnoreCase) < 0) { uri += "SERVICE=WMS&"; } if (uri.IndexOf("VERSION=", StringComparison.OrdinalIgnoreCase) < 0) { uri += "VERSION=1.3.0&"; } return uri + "REQUEST=" + request; } } }