From 209d06adf5646408f6c5b25aaca31dce5f660f06 Mon Sep 17 00:00:00 2001 From: ClemensF Date: Wed, 15 Apr 2020 20:46:16 +0200 Subject: [PATCH] Updated WMTS implementation --- MapControl/Shared/WmtsCapabilities.cs | 231 +++++++++++++++++++++++++ MapControl/Shared/WmtsTileLayer.cs | 109 +----------- MapControl/Shared/WmtsTileMatrix.cs | 69 -------- MapControl/Shared/WmtsTileMatrixSet.cs | 35 ---- MapControl/Shared/WmtsTileSource.cs | 1 - MapControl/UWP/MapControl.UWP.csproj | 3 + 6 files changed, 243 insertions(+), 205 deletions(-) create mode 100644 MapControl/Shared/WmtsCapabilities.cs diff --git a/MapControl/Shared/WmtsCapabilities.cs b/MapControl/Shared/WmtsCapabilities.cs new file mode 100644 index 00000000..9e0be3fc --- /dev/null +++ b/MapControl/Shared/WmtsCapabilities.cs @@ -0,0 +1,231 @@ +// 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.Globalization; +using System.Linq; +using System.Xml.Linq; +using System.Threading.Tasks; +#if !WINDOWS_UWP +using System.Windows; +#endif + +namespace MapControl +{ + public class WmtsCapabilities + { + public string LayerIdentifier { get; private set; } + public WmtsTileSource TileSource { get; private set; } + public List TileMatrixSets { get; private set; } + + public static async Task ReadCapabilities(Uri capabilitiesUri, string layerIdentifier) + { + WmtsCapabilities capabilities; + + if (capabilitiesUri.IsAbsoluteUri && (capabilitiesUri.Scheme == "http" || capabilitiesUri.Scheme == "https")) + { + using (var stream = await ImageLoader.HttpClient.GetStreamAsync(capabilitiesUri)) + { + capabilities = ReadCapabilities(XDocument.Load(stream).Root, layerIdentifier); + } + } + else + { + capabilities = ReadCapabilities(XDocument.Load(capabilitiesUri.ToString()).Root, layerIdentifier); + } + + return capabilities; + } + + public static WmtsCapabilities ReadCapabilities(XElement capabilitiesElement, string layerIdentifier) + { + XNamespace ns = capabilitiesElement.Name.Namespace; + XNamespace ows = "http://www.opengis.net/ows/1.1"; + + var contentsElement = capabilitiesElement.Element(ns + "Contents"); + + if (contentsElement == null) + { + throw new ArgumentException("Contents element not found."); + } + + XElement layerElement; + + if (!string.IsNullOrEmpty(layerIdentifier)) + { + layerElement = contentsElement.Descendants(ns + "Layer") + .FirstOrDefault(e => e.Element(ows + "Identifier")?.Value == layerIdentifier); + + if (layerElement == null) + { + throw new ArgumentException("Layer element \"" + layerIdentifier + "\" not found."); + } + } + else + { + layerElement = capabilitiesElement.Descendants(ns + "Layer").FirstOrDefault(); + + if (layerElement == null) + { + throw new ArgumentException("No Layer element found."); + } + + layerIdentifier = layerElement.Element(ows + "Identifier")?.Value ?? ""; + } + + var urlTemplate = layerElement.Element(ns + "ResourceURL")?.Attribute("template")?.Value; + + if (string.IsNullOrEmpty(urlTemplate)) + { + throw new ArgumentException("No valid ResourceURL element found in Layer \"" + layerIdentifier + "\"."); + } + + var styleElement = layerElement.Descendants(ns + "Style") + .FirstOrDefault(e => e.Attribute("isDefault")?.Value == "true"); + + if (styleElement == null) + { + styleElement = layerElement.Descendants(ns + "Style").FirstOrDefault(); + } + + var style = styleElement?.Element(ows + "Identifier")?.Value; + + if (string.IsNullOrEmpty(style)) + { + throw new ArgumentException("No valid Style element found in Layer \"" + layerIdentifier + "\"."); + } + + var tileMatrixSetIds = layerElement + .Descendants(ns + "TileMatrixSetLink") + .Select(e => e.Element(ns + "TileMatrixSet")?.Value) + .Where(id => !string.IsNullOrEmpty(id)) + .ToList(); + + var tileMatrixSets = new List(); + + foreach (var tileMatrixSetId in tileMatrixSetIds) + { + var tileMatrixSetElement = capabilitiesElement.Descendants(ns + "TileMatrixSet") + .FirstOrDefault(e => e.Element(ows + "Identifier")?.Value == tileMatrixSetId); + + if (tileMatrixSetElement == null) + { + throw new ArgumentException("Linked TileMatrixSet element not found in Layer \"" + layerIdentifier + "\"."); + } + + var tileMatrixSet = ReadTileMatrixSet(tileMatrixSetElement); + + tileMatrixSets.Add(tileMatrixSet); + } + + return new WmtsCapabilities + { + LayerIdentifier = layerIdentifier, + TileSource = new WmtsTileSource(urlTemplate.Replace("{Style}", style)), + TileMatrixSets = tileMatrixSets + }; + } + + public static WmtsTileMatrixSet ReadTileMatrixSet(XElement tileMatrixSetElement) + { + XNamespace ns = tileMatrixSetElement.Name.Namespace; + XNamespace ows = "http://www.opengis.net/ows/1.1"; + + var identifier = tileMatrixSetElement.Element(ows + "Identifier")?.Value; + + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("No ows:Identifier element found in TileMatrixSet."); + } + + var supportedCrs = tileMatrixSetElement.Element(ows + "SupportedCRS")?.Value; + + if (string.IsNullOrEmpty(supportedCrs)) + { + throw new ArgumentException("No ows:SupportedCRS element found in TileMatrixSet \"" + identifier + "\"."); + } + + var tileMatrixes = new List(); + + foreach (var tileMatrixElement in tileMatrixSetElement.Descendants(ns + "TileMatrix")) + { + tileMatrixes.Add(ReadTileMatrix(tileMatrixElement)); + } + + if (tileMatrixes.Count <= 0) + { + throw new ArgumentException("No TileMatrix elements found in TileMatrixSet \"" + identifier + "\"."); + } + + return new WmtsTileMatrixSet(identifier, supportedCrs, tileMatrixes); + } + + public static WmtsTileMatrix ReadTileMatrix(XElement tileMatrixElement) + { + XNamespace ns = tileMatrixElement.Name.Namespace; + XNamespace ows = "http://www.opengis.net/ows/1.1"; + + var identifier = tileMatrixElement.Element(ows + "Identifier")?.Value; + + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("No ows:Identifier element found in TileMatrix."); + } + + string[] topLeftCornerStrings; + double scaleDenominator, top, left; + int tileWidth, tileHeight, matrixWidth, matrixHeight; + + var valueString = tileMatrixElement.Element(ns + "ScaleDenominator")?.Value; + + if (string.IsNullOrEmpty(valueString) || + !double.TryParse(valueString, NumberStyles.Float, CultureInfo.InvariantCulture, out scaleDenominator)) + { + throw new ArgumentException("No ScaleDenominator element found in TileMatrix \"" + identifier + "\"."); + } + + valueString = tileMatrixElement.Element(ns + "TopLeftCorner")?.Value; + + if (string.IsNullOrEmpty(valueString) || + (topLeftCornerStrings = valueString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)).Length < 2 || + !double.TryParse(topLeftCornerStrings[0], NumberStyles.Float, CultureInfo.InvariantCulture, out left) || + !double.TryParse(topLeftCornerStrings[1], NumberStyles.Float, CultureInfo.InvariantCulture, out top)) + { + throw new ArgumentException("No TopLeftCorner element found in TileMatrix \"" + identifier + "\"."); + } + + valueString = tileMatrixElement.Element(ns + "TileWidth")?.Value; + + if (string.IsNullOrEmpty(valueString) || !int.TryParse(valueString, out tileWidth)) + { + throw new ArgumentException("No TileWidth element found in TileMatrix \"" + identifier + "\"."); + } + + valueString = tileMatrixElement.Element(ns + "TileHeight")?.Value; + + if (string.IsNullOrEmpty(valueString) || !int.TryParse(valueString, out tileHeight)) + { + throw new ArgumentException("No TileHeight element found in TileMatrix \"" + identifier + "\"."); + } + + valueString = tileMatrixElement.Element(ns + "MatrixWidth")?.Value; + + if (string.IsNullOrEmpty(valueString) || !int.TryParse(valueString, out matrixWidth)) + { + throw new ArgumentException("No MatrixWidth element found in TileMatrix \"" + identifier + "\"."); + } + + valueString = tileMatrixElement.Element(ns + "MatrixHeight")?.Value; + + if (string.IsNullOrEmpty(valueString) || !int.TryParse(valueString, out matrixHeight)) + { + throw new ArgumentException("No MatrixHeight element found in TileMatrix \"" + identifier + "\"."); + } + + return new WmtsTileMatrix( + identifier, scaleDenominator, new Point(left, top), tileWidth, tileHeight, matrixWidth, matrixHeight); + } + } +} diff --git a/MapControl/Shared/WmtsTileLayer.cs b/MapControl/Shared/WmtsTileLayer.cs index 464a05fd..91784696 100644 --- a/MapControl/Shared/WmtsTileLayer.cs +++ b/MapControl/Shared/WmtsTileLayer.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Xml.Linq; #if WINDOWS_UWP using Windows.Foundation; using Windows.UI.Xaml; @@ -79,6 +78,7 @@ namespace MapControl protected override void TileSourcePropertyChanged() { + UpdateTileLayer(); } protected override void UpdateTileLayer() @@ -160,7 +160,6 @@ namespace MapControl foreach (var layer in ChildLayers) { layer.UpdateTiles(); - tiles.AddRange(layer.Tiles); } @@ -177,7 +176,7 @@ namespace MapControl } } - TileImageLoader.LoadTilesAsync(tiles, TileSource, sourceName); + TileImageLoader.LoadTilesAsync(tiles, tileSource, sourceName); } private async void OnLoaded(object sender, RoutedEventArgs e) @@ -186,17 +185,13 @@ namespace MapControl { try { - if (CapabilitiesUri.IsAbsoluteUri && (CapabilitiesUri.Scheme == "http" || CapabilitiesUri.Scheme == "https")) - { - using (var stream = await ImageLoader.HttpClient.GetStreamAsync(CapabilitiesUri)) - { - ReadCapabilities(XDocument.Load(stream).Root); - } - } - else - { - ReadCapabilities(XDocument.Load(CapabilitiesUri.ToString()).Root); - } + var capabilities = await WmtsCapabilities.ReadCapabilities(CapabilitiesUri, LayerIdentifier); + + TileMatrixSets.Clear(); + capabilities.TileMatrixSets.ForEach(s => TileMatrixSets.Add(s.SupportedCrs, s)); + + LayerIdentifier = capabilities.LayerIdentifier; + TileSource = capabilities.TileSource; } catch (Exception ex) { @@ -204,91 +199,5 @@ namespace MapControl } } } - - private void ReadCapabilities(XElement capabilitiesElement) - { - TileMatrixSets.Clear(); - - XNamespace ns = capabilitiesElement.Name.Namespace; - XNamespace ows = "http://www.opengis.net/ows/1.1"; - - var contentsElement = capabilitiesElement.Element(ns + "Contents"); - - if (contentsElement == null) - { - throw new ArgumentException("Contents element not found."); - } - - XElement layerElement; - - if (!string.IsNullOrEmpty(LayerIdentifier)) - { - layerElement = contentsElement.Descendants(ns + "Layer") - .FirstOrDefault(e => e.Element(ows + "Identifier")?.Value == LayerIdentifier); - - if (layerElement == null) - { - throw new ArgumentException("Layer element \"" + LayerIdentifier + "\" not found."); - } - } - else - { - layerElement = capabilitiesElement.Descendants(ns + "Layer").FirstOrDefault(); - - if (layerElement == null) - { - throw new ArgumentException("No Layer element found."); - } - - LayerIdentifier = layerElement.Element(ows + "Identifier")?.Value ?? ""; - } - - var urlTemplate = layerElement.Element(ns + "ResourceURL")?.Attribute("template")?.Value; - - if (string.IsNullOrEmpty(urlTemplate)) - { - throw new ArgumentException("No valid ResourceURL element found in Layer \"" + LayerIdentifier + "\"."); - } - - var styleElement = layerElement.Descendants(ns + "Style") - .FirstOrDefault(e => e.Attribute("isDefault")?.Value == "true"); - - if (styleElement == null) - { - styleElement = layerElement.Descendants(ns + "Style").FirstOrDefault(); - } - - var style = styleElement?.Element(ows + "Identifier")?.Value; - - if (string.IsNullOrEmpty(style)) - { - throw new ArgumentException("No valid Style element found in Layer \"" + LayerIdentifier + "\"."); - } - - var tileMatrixSetIds = layerElement - .Descendants(ns + "TileMatrixSetLink") - .Select(e => e.Element(ns + "TileMatrixSet")?.Value) - .Where(id => !string.IsNullOrEmpty(id)) - .ToList(); - - foreach (var tileMatrixSetId in tileMatrixSetIds) - { - var tileMatrixSetElement = capabilitiesElement.Descendants(ns + "TileMatrixSet") - .FirstOrDefault(e => e.Element(ows + "Identifier")?.Value == tileMatrixSetId); - - if (tileMatrixSetElement == null) - { - throw new ArgumentException("Linked TileMatrixSet element not found in Layer \"" + LayerIdentifier + "\"."); - } - - var tileMatrixSet = WmtsTileMatrixSet.Create(tileMatrixSetElement); - - TileMatrixSets.Add(tileMatrixSet.SupportedCrs, tileMatrixSet); - } - - TileSource = new WmtsTileSource(urlTemplate.Replace("{Style}", style)); - - UpdateTileLayer(); - } } } diff --git a/MapControl/Shared/WmtsTileMatrix.cs b/MapControl/Shared/WmtsTileMatrix.cs index 76061a05..188a93b6 100644 --- a/MapControl/Shared/WmtsTileMatrix.cs +++ b/MapControl/Shared/WmtsTileMatrix.cs @@ -2,9 +2,6 @@ // © 2020 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System; -using System.Globalization; -using System.Xml.Linq; #if !WINDOWS_UWP using System.Windows; #endif @@ -32,71 +29,5 @@ namespace MapControl MatrixWidth = matrixWidth; MatrixHeight = matrixHeight; } - - public static WmtsTileMatrix Create(XElement tileMatrixElement) - { - XNamespace ns = tileMatrixElement.Name.Namespace; - XNamespace ows = "http://www.opengis.net/ows/1.1"; - - var identifier = tileMatrixElement.Element(ows + "Identifier")?.Value; - - if (string.IsNullOrEmpty(identifier)) - { - throw new ArgumentException("ows:Identifier element not found in TileMatrix."); - } - - string[] topLeftCornerStrings; - double scaleDenominator, top, left; - int tileWidth, tileHeight, matrixWidth, matrixHeight; - - var valueString = tileMatrixElement.Element(ns + "ScaleDenominator")?.Value; - - if (string.IsNullOrEmpty(valueString) || - !double.TryParse(valueString, NumberStyles.Float, CultureInfo.InvariantCulture, out scaleDenominator)) - { - throw new ArgumentException("ScaleDenominator element not found in TileMatrix \"" + identifier + "\"."); - } - - valueString = tileMatrixElement.Element(ns + "TopLeftCorner")?.Value; - - if (string.IsNullOrEmpty(valueString) || - (topLeftCornerStrings = valueString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)).Length < 2 || - !double.TryParse(topLeftCornerStrings[0], NumberStyles.Float, CultureInfo.InvariantCulture, out left) || - !double.TryParse(topLeftCornerStrings[1], NumberStyles.Float, CultureInfo.InvariantCulture, out top)) - { - throw new ArgumentException("TopLeftCorner element not found in TileMatrix \"" + identifier + "\"."); - } - - valueString = tileMatrixElement.Element(ns + "TileWidth")?.Value; - - if (string.IsNullOrEmpty(valueString) || !int.TryParse(valueString, out tileWidth)) - { - throw new ArgumentException("TileWidth element not found in TileMatrix \"" + identifier + "\"."); - } - - valueString = tileMatrixElement.Element(ns + "TileHeight")?.Value; - - if (string.IsNullOrEmpty(valueString) || !int.TryParse(valueString, out tileHeight)) - { - throw new ArgumentException("TileHeight element not found in TileMatrix \"" + identifier + "\"."); - } - - valueString = tileMatrixElement.Element(ns + "MatrixWidth")?.Value; - - if (string.IsNullOrEmpty(valueString) || !int.TryParse(valueString, out matrixWidth)) - { - throw new ArgumentException("MatrixWidth element not found in TileMatrix \"" + identifier + "\"."); - } - - valueString = tileMatrixElement.Element(ns + "MatrixHeight")?.Value; - - if (string.IsNullOrEmpty(valueString) || !int.TryParse(valueString, out matrixHeight)) - { - throw new ArgumentException("MatrixHeight element not found in TileMatrix \"" + identifier + "\"."); - } - - return new WmtsTileMatrix( - identifier, scaleDenominator, new Point(left, top), tileWidth, tileHeight, matrixWidth, matrixHeight); - } } } diff --git a/MapControl/Shared/WmtsTileMatrixSet.cs b/MapControl/Shared/WmtsTileMatrixSet.cs index 144c1553..3e8137e3 100644 --- a/MapControl/Shared/WmtsTileMatrixSet.cs +++ b/MapControl/Shared/WmtsTileMatrixSet.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Xml.Linq; namespace MapControl { @@ -36,39 +35,5 @@ namespace MapControl SupportedCrs = supportedCrs; TileMatrixes = tileMatrixes.OrderBy(m => m.Scale).ToList(); } - - public static WmtsTileMatrixSet Create(XElement tileMatrixSetElement) - { - XNamespace ns = tileMatrixSetElement.Name.Namespace; - XNamespace ows = "http://www.opengis.net/ows/1.1"; - - var identifier = tileMatrixSetElement.Element(ows + "Identifier")?.Value; - - if (string.IsNullOrEmpty(identifier)) - { - throw new ArgumentException("ows:Identifier element not found in TileMatrixSet."); - } - - var supportedCrs = tileMatrixSetElement.Element(ows + "SupportedCRS")?.Value; - - if (string.IsNullOrEmpty(supportedCrs)) - { - throw new ArgumentException("ows:SupportedCRS element not found in TileMatrixSet \"" + identifier + "\"."); - } - - var tileMatrixes = new List(); - - foreach (var tileMatrixElement in tileMatrixSetElement.Descendants(ns + "TileMatrix")) - { - tileMatrixes.Add(WmtsTileMatrix.Create(tileMatrixElement)); - } - - if (tileMatrixes.Count <= 0) - { - throw new ArgumentException("No TileMatrix elements found in TileMatrixSet \"" + identifier + "\"."); - } - - return new WmtsTileMatrixSet(identifier, supportedCrs, tileMatrixes); - } } } diff --git a/MapControl/Shared/WmtsTileSource.cs b/MapControl/Shared/WmtsTileSource.cs index 401fd925..05a7a8b2 100644 --- a/MapControl/Shared/WmtsTileSource.cs +++ b/MapControl/Shared/WmtsTileSource.cs @@ -3,7 +3,6 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -using System.Collections.Generic; namespace MapControl { diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj index 8858827e..59899415 100644 --- a/MapControl/UWP/MapControl.UWP.csproj +++ b/MapControl/UWP/MapControl.UWP.csproj @@ -161,6 +161,9 @@ WmsImageLayer.cs + + WmtsCapabilities.cs + WmtsTileLayer.cs