2020-03-20 18:12:56 +01:00
|
|
|
|
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
2021-01-13 21:19:27 +01:00
|
|
|
|
// © 2021 Clemens Fischer
|
2020-03-20 18:12:56 +01:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.Linq;
|
2021-07-05 00:03:44 +02:00
|
|
|
|
using System.Threading.Tasks;
|
2021-06-14 21:41:37 +02:00
|
|
|
|
#if WINUI
|
|
|
|
|
|
using Windows.Foundation;
|
|
|
|
|
|
using Microsoft.UI.Xaml;
|
|
|
|
|
|
#elif WINDOWS_UWP
|
2020-03-20 18:12:56 +01:00
|
|
|
|
using Windows.Foundation;
|
|
|
|
|
|
using Windows.UI.Xaml;
|
|
|
|
|
|
#else
|
|
|
|
|
|
using System.Windows;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
namespace MapControl
|
|
|
|
|
|
{
|
2020-04-19 10:53:25 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Displays map tiles from a Web Map Tile Service (WMTS).
|
|
|
|
|
|
/// </summary>
|
2020-03-20 18:12:56 +01:00
|
|
|
|
public class WmtsTileLayer : MapTileLayerBase
|
|
|
|
|
|
{
|
2020-03-20 20:40:13 +01:00
|
|
|
|
public static readonly DependencyProperty CapabilitiesUriProperty = DependencyProperty.Register(
|
2020-03-20 21:09:19 +01:00
|
|
|
|
nameof(CapabilitiesUri), typeof(Uri), typeof(WmtsTileLayer),
|
2020-03-21 01:52:17 +01:00
|
|
|
|
new PropertyMetadata(null, (o, e) => ((WmtsTileLayer)o).TileMatrixSets.Clear()));
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty LayerIdentifierProperty = DependencyProperty.Register(
|
|
|
|
|
|
nameof(LayerIdentifier), typeof(string), typeof(WmtsTileLayer), new PropertyMetadata(null));
|
|
|
|
|
|
|
|
|
|
|
|
public WmtsTileLayer()
|
|
|
|
|
|
: this(new TileImageLoader())
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public WmtsTileLayer(ITileImageLoader tileImageLoader)
|
|
|
|
|
|
: base(tileImageLoader)
|
|
|
|
|
|
{
|
|
|
|
|
|
Loaded += OnLoaded;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-04-19 10:53:25 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The Uri of a XML file or web response that contains the service capabilities.
|
|
|
|
|
|
/// </summary>
|
2020-03-20 20:40:13 +01:00
|
|
|
|
public Uri CapabilitiesUri
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
2020-03-20 20:40:13 +01:00
|
|
|
|
get { return (Uri)GetValue(CapabilitiesUriProperty); }
|
|
|
|
|
|
set { SetValue(CapabilitiesUriProperty, value); }
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-04-19 10:53:25 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The ows:Identifier of the Layer that should be displayed. If not set, the first Layer is displayed.
|
|
|
|
|
|
/// </summary>
|
2020-03-20 18:12:56 +01:00
|
|
|
|
public string LayerIdentifier
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (string)GetValue(LayerIdentifierProperty); }
|
|
|
|
|
|
set { SetValue(LayerIdentifierProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-23 17:13:50 +01:00
|
|
|
|
public IEnumerable<WmtsTileMatrixLayer> ChildLayers
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return Children.Cast<WmtsTileMatrixLayer>(); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-21 01:52:17 +01:00
|
|
|
|
public Dictionary<string, WmtsTileMatrixSet> TileMatrixSets { get; } = new Dictionary<string, WmtsTileMatrixSet>();
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
|
|
|
|
|
protected override Size MeasureOverride(Size availableSize)
|
|
|
|
|
|
{
|
2020-03-23 17:13:50 +01:00
|
|
|
|
foreach (var layer in ChildLayers)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
layer.Measure(availableSize);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new Size();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override Size ArrangeOverride(Size finalSize)
|
|
|
|
|
|
{
|
2020-03-23 17:13:50 +01:00
|
|
|
|
foreach (var layer in ChildLayers)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
layer.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return finalSize;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-05 00:03:44 +02:00
|
|
|
|
protected override Task UpdateTileLayer()
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (ParentMap == null ||
|
2020-04-16 19:24:38 +02:00
|
|
|
|
!TileMatrixSets.TryGetValue(ParentMap.MapProjection.CrsId, out WmtsTileMatrixSet tileMatrixSet))
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
Children.Clear();
|
2021-07-05 00:03:44 +02:00
|
|
|
|
|
|
|
|
|
|
return UpdateTiles(null);
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
2021-07-05 00:03:44 +02:00
|
|
|
|
|
|
|
|
|
|
if (UpdateChildLayers(tileMatrixSet))
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
SetRenderTransform();
|
2021-07-05 00:03:44 +02:00
|
|
|
|
|
|
|
|
|
|
return UpdateTiles(tileMatrixSet);
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
2021-07-05 00:03:44 +02:00
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void SetRenderTransform()
|
|
|
|
|
|
{
|
2020-03-23 17:13:50 +01:00
|
|
|
|
foreach (var layer in ChildLayers)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
2020-03-26 19:08:20 +01:00
|
|
|
|
layer.SetRenderTransform(ParentMap.ViewTransform);
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-21 01:52:17 +01:00
|
|
|
|
private bool UpdateChildLayers(WmtsTileMatrixSet tileMatrixSet)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
2020-03-22 18:33:34 +01:00
|
|
|
|
var layersChanged = false;
|
2020-03-26 19:08:20 +01:00
|
|
|
|
var maxScale = 1.001 * ParentMap.ViewTransform.Scale; // avoid rounding issues
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
2020-03-23 17:13:50 +01:00
|
|
|
|
// show all TileMatrix layers with Scale <= maxScale, at least the first layer
|
2020-03-21 01:52:17 +01:00
|
|
|
|
//
|
|
|
|
|
|
var currentMatrixes = tileMatrixSet.TileMatrixes
|
2020-03-22 18:33:34 +01:00
|
|
|
|
.Where((matrix, i) => i == 0 || matrix.Scale <= maxScale)
|
2020-03-21 01:52:17 +01:00
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
if (this != ParentMap.MapLayer) // do not load background tiles
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
2020-03-21 01:52:17 +01:00
|
|
|
|
currentMatrixes = currentMatrixes.Skip(currentMatrixes.Count - 1).ToList(); // last element only
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (currentMatrixes.Count > MaxBackgroundLevels + 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
currentMatrixes = currentMatrixes.Skip(currentMatrixes.Count - MaxBackgroundLevels - 1).ToList();
|
|
|
|
|
|
}
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
2020-03-23 17:13:50 +01:00
|
|
|
|
var currentLayers = ChildLayers.Where(layer => currentMatrixes.Contains(layer.TileMatrix)).ToList();
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
2020-03-21 01:52:17 +01:00
|
|
|
|
Children.Clear();
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
2020-03-21 01:52:17 +01:00
|
|
|
|
foreach (var tileMatrix in currentMatrixes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var layer = currentLayers.FirstOrDefault(l => l.TileMatrix == tileMatrix);
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
2020-03-21 01:52:17 +01:00
|
|
|
|
if (layer == null)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
2020-03-21 01:52:17 +01:00
|
|
|
|
layer = new WmtsTileMatrixLayer(tileMatrix, tileMatrixSet.TileMatrixes.IndexOf(tileMatrix));
|
|
|
|
|
|
layersChanged = true;
|
|
|
|
|
|
}
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
2020-03-26 19:08:20 +01:00
|
|
|
|
if (layer.SetBounds(ParentMap.ViewTransform, ParentMap.RenderSize))
|
2020-03-21 01:52:17 +01:00
|
|
|
|
{
|
|
|
|
|
|
layersChanged = true;
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
2020-03-21 01:52:17 +01:00
|
|
|
|
|
|
|
|
|
|
Children.Add(layer);
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return layersChanged;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-05 00:03:44 +02:00
|
|
|
|
private Task UpdateTiles(WmtsTileMatrixSet tileMatrixSet)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
var tiles = new List<Tile>();
|
2021-01-23 09:52:53 +01:00
|
|
|
|
var cacheName = SourceName;
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
2020-03-23 17:13:50 +01:00
|
|
|
|
foreach (var layer in ChildLayers)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
layer.UpdateTiles();
|
|
|
|
|
|
tiles.AddRange(layer.Tiles);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-23 09:52:53 +01:00
|
|
|
|
if (tileMatrixSet != null && TileSource is WmtsTileSource tileSource)
|
2020-03-21 01:52:17 +01:00
|
|
|
|
{
|
|
|
|
|
|
tileSource.TileMatrixSet = tileMatrixSet;
|
|
|
|
|
|
|
2021-01-23 09:52:53 +01:00
|
|
|
|
if (!string.IsNullOrEmpty(cacheName))
|
2020-03-21 01:52:17 +01:00
|
|
|
|
{
|
2020-11-19 18:53:20 +01:00
|
|
|
|
cacheName += "/" + tileMatrixSet.Identifier
|
2020-06-20 13:39:05 +02:00
|
|
|
|
.Replace(':', '_')
|
|
|
|
|
|
.Replace(';', '_')
|
|
|
|
|
|
.Replace(',', '_')
|
|
|
|
|
|
.Replace('/', '_')
|
|
|
|
|
|
.Replace('\\', '_');
|
2020-03-21 01:52:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-05 00:03:44 +02:00
|
|
|
|
return TileImageLoader.LoadTiles(tiles, TileSource, cacheName);
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async void OnLoaded(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
2020-03-21 01:52:17 +01:00
|
|
|
|
if (TileMatrixSets.Count == 0 && CapabilitiesUri != null)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2020-04-15 20:46:16 +02:00
|
|
|
|
var capabilities = await WmtsCapabilities.ReadCapabilities(CapabilitiesUri, LayerIdentifier);
|
2020-03-21 08:17:15 +01:00
|
|
|
|
|
2020-06-18 18:30:57 +02:00
|
|
|
|
foreach (var tileMatrixSet in capabilities.TileMatrixSets
|
|
|
|
|
|
.Where(s => !TileMatrixSets.ContainsKey(s.SupportedCrs)))
|
|
|
|
|
|
{
|
|
|
|
|
|
TileMatrixSets.Add(tileMatrixSet.SupportedCrs, tileMatrixSet);
|
|
|
|
|
|
}
|
2020-03-20 18:12:56 +01:00
|
|
|
|
|
2020-04-15 20:46:16 +02:00
|
|
|
|
LayerIdentifier = capabilities.LayerIdentifier;
|
|
|
|
|
|
TileSource = capabilities.TileSource;
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
2020-04-15 20:46:16 +02:00
|
|
|
|
catch (Exception ex)
|
2020-03-20 18:12:56 +01:00
|
|
|
|
{
|
2020-04-15 20:46:16 +02:00
|
|
|
|
Debug.WriteLine("WmtsTileLayer: {0}: {1}", CapabilitiesUri, ex.Message);
|
2020-03-20 18:12:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|