From ddf77441f297668681397d1cfba3a3ecae824325 Mon Sep 17 00:00:00 2001 From: ClemensF Date: Sun, 25 Oct 2020 16:09:09 +0100 Subject: [PATCH] Minor tile layer updates --- MapControl/Shared/BoundingBoxTileSource.cs | 42 +++++++++ MapControl/Shared/MapImageLayer.cs | 2 +- MapControl/Shared/MapTileLayer.cs | 9 +- MapControl/Shared/MapTileLayerBase.cs | 25 ++++-- MapControl/Shared/TileSource.cs | 26 ------ MapControl/Shared/WmtsTileLayer.cs | 2 - MapControl/UWP/MapControl.UWP.csproj | 3 + SampleApps/Shared/MapLayers.cs | 88 +++++++++++-------- SampleApps/WpfApplication/MainWindow.xaml | 4 +- .../WpfApplication/WpfApplication.csproj | 9 +- 10 files changed, 124 insertions(+), 86 deletions(-) create mode 100644 MapControl/Shared/BoundingBoxTileSource.cs diff --git a/MapControl/Shared/BoundingBoxTileSource.cs b/MapControl/Shared/BoundingBoxTileSource.cs new file mode 100644 index 00000000..a4c05370 --- /dev/null +++ b/MapControl/Shared/BoundingBoxTileSource.cs @@ -0,0 +1,42 @@ +// 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.Globalization; + +namespace MapControl +{ + public class BoundingBoxTileSource : TileSource + { + public override Uri GetUri(int x, int y, int zoomLevel) + { + Uri uri = null; + + if (UriFormat != null) + { + var tileSize = 360d / (1 << zoomLevel); // tile width in degrees + var west = MapProjection.Wgs84MetersPerDegree * (x * tileSize - 180d); + var east = MapProjection.Wgs84MetersPerDegree * ((x + 1) * tileSize - 180d); + var south = MapProjection.Wgs84MetersPerDegree * (180d - (y + 1) * tileSize); + var north = MapProjection.Wgs84MetersPerDegree * (180d - y * tileSize); + + if (UriFormat.Contains("{bbox}")) + { + uri = new Uri(UriFormat.Replace("{bbox}", string.Format(CultureInfo.InvariantCulture, + "{0:F2},{1:F2},{2:F2},{3:F2}", west, south, east, north))); + } + else + { + uri = new Uri(UriFormat + .Replace("{west}", west.ToString("F2", CultureInfo.InvariantCulture)) + .Replace("{south}", south.ToString("F2", CultureInfo.InvariantCulture)) + .Replace("{east}", east.ToString("F2", CultureInfo.InvariantCulture)) + .Replace("{north}", north.ToString("F2", CultureInfo.InvariantCulture))); + } + } + + return uri; + } + } +} diff --git a/MapControl/Shared/MapImageLayer.cs b/MapControl/Shared/MapImageLayer.cs index 7e9eb2f1..8b5ab5c1 100644 --- a/MapControl/Shared/MapImageLayer.cs +++ b/MapControl/Shared/MapImageLayer.cs @@ -216,7 +216,7 @@ namespace MapControl } } - protected virtual async Task UpdateImageAsync() + protected async Task UpdateImageAsync() { updateTimer.Stop(); diff --git a/MapControl/Shared/MapTileLayer.cs b/MapControl/Shared/MapTileLayer.cs index a86d241e..1a851743 100644 --- a/MapControl/Shared/MapTileLayer.cs +++ b/MapControl/Shared/MapTileLayer.cs @@ -37,8 +37,7 @@ namespace MapControl { TileSource = new TileSource { UriFormat = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" }, SourceName = "OpenStreetMap", - Description = "© [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)", - MaxZoomLevel = 19 + Description = "© [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)" }; } } @@ -47,7 +46,7 @@ namespace MapControl nameof(MinZoomLevel), typeof(int), typeof(MapTileLayer), new PropertyMetadata(0)); public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register( - nameof(MaxZoomLevel), typeof(int), typeof(MapTileLayer), new PropertyMetadata(18)); + nameof(MaxZoomLevel), typeof(int), typeof(MapTileLayer), new PropertyMetadata(19)); public MapTileLayer() : this(new TileImageLoader()) @@ -73,7 +72,7 @@ namespace MapControl } /// - /// Maximum zoom level supported by the MapTileLayer. Default value is 18. + /// Maximum zoom level supported by the MapTileLayer. Default value is 19. /// public int MaxZoomLevel { @@ -92,8 +91,6 @@ namespace MapControl protected override void UpdateTileLayer() { - UpdateTimer.Stop(); - if (ParentMap == null || !ParentMap.MapProjection.IsWebMercator) { TileMatrix = null; diff --git a/MapControl/Shared/MapTileLayerBase.cs b/MapControl/Shared/MapTileLayerBase.cs index 5eeb1cb3..df3a378c 100644 --- a/MapControl/Shared/MapTileLayerBase.cs +++ b/MapControl/Shared/MapTileLayerBase.cs @@ -39,7 +39,7 @@ namespace MapControl public static readonly DependencyProperty UpdateIntervalProperty = DependencyProperty.Register( nameof(UpdateInterval), typeof(TimeSpan), typeof(MapTileLayerBase), - new PropertyMetadata(TimeSpan.FromSeconds(0.2), (o, e) => ((MapTileLayerBase)o).UpdateTimer.Interval = (TimeSpan)e.NewValue)); + new PropertyMetadata(TimeSpan.FromSeconds(0.2), (o, e) => ((MapTileLayerBase)o).updateTimer.Interval = (TimeSpan)e.NewValue)); public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register( nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapTileLayerBase), new PropertyMetadata(false)); @@ -50,6 +50,7 @@ namespace MapControl public static readonly DependencyProperty MapForegroundProperty = DependencyProperty.Register( nameof(MapForeground), typeof(Brush), typeof(MapTileLayerBase), new PropertyMetadata(null)); + private readonly DispatcherTimer updateTimer; private MapBase parentMap; protected MapTileLayerBase(ITileImageLoader tileImageLoader) @@ -57,16 +58,19 @@ namespace MapControl RenderTransform = new MatrixTransform(); TileImageLoader = tileImageLoader; - UpdateTimer = new DispatcherTimer { Interval = UpdateInterval }; - UpdateTimer.Tick += (s, e) => UpdateTileLayer(); + updateTimer = new DispatcherTimer { Interval = UpdateInterval }; + + updateTimer.Tick += (s, e) => + { + updateTimer.Stop(); + UpdateTileLayer(); + }; MapPanel.InitMapElement(this); } public ITileImageLoader TileImageLoader { get; } - public DispatcherTimer UpdateTimer { get; } - /// /// Provides map tile URIs or images. /// @@ -145,6 +149,8 @@ namespace MapControl get { return parentMap; } set { + updateTimer.Stop(); + if (parentMap != null) { parentMap.ViewportChanged -= OnViewportChanged; @@ -165,20 +171,21 @@ namespace MapControl { if (Children.Count == 0 || e.ProjectionChanged || Math.Abs(e.LongitudeOffset) > 180d) { + updateTimer.Stop(); UpdateTileLayer(); // update immediately when projection has changed or center has moved across 180° longitude } else { SetRenderTransform(); - if (UpdateTimer.IsEnabled && !UpdateWhileViewportChanging) + if (updateTimer.IsEnabled && !UpdateWhileViewportChanging) { - UpdateTimer.Stop(); // restart + updateTimer.Stop(); // restart } - if (!UpdateTimer.IsEnabled) + if (!updateTimer.IsEnabled) { - UpdateTimer.Start(); + updateTimer.Start(); } } } diff --git a/MapControl/Shared/TileSource.cs b/MapControl/Shared/TileSource.cs index dc61c79c..c6ba47d3 100644 --- a/MapControl/Shared/TileSource.cs +++ b/MapControl/Shared/TileSource.cs @@ -3,7 +3,6 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -using System.Globalization; using System.Threading.Tasks; #if WINDOWS_UWP using Windows.UI.Xaml.Media; @@ -89,29 +88,4 @@ namespace MapControl return base.GetUri(x, (1 << zoomLevel) - 1 - y, zoomLevel); } } - - public class BoundingBoxTileSource : TileSource - { - public override Uri GetUri(int x, int y, int zoomLevel) - { - Uri uri = null; - - if (UriFormat != null) - { - var tileSize = 360d / (1 << zoomLevel); // tile width in degrees - var west = MapProjection.Wgs84MetersPerDegree * (x * tileSize - 180d); - var east = MapProjection.Wgs84MetersPerDegree * ((x + 1) * tileSize - 180d); - var south = MapProjection.Wgs84MetersPerDegree * (180d - (y + 1) * tileSize); - var north = MapProjection.Wgs84MetersPerDegree * (180d - y * tileSize); - - uri = new Uri(UriFormat - .Replace("{west}", west.ToString(CultureInfo.InvariantCulture)) - .Replace("{south}", south.ToString(CultureInfo.InvariantCulture)) - .Replace("{east}", east.ToString(CultureInfo.InvariantCulture)) - .Replace("{north}", north.ToString(CultureInfo.InvariantCulture))); - } - - return uri; - } - } } diff --git a/MapControl/Shared/WmtsTileLayer.cs b/MapControl/Shared/WmtsTileLayer.cs index 837a92c1..d7df2c6d 100644 --- a/MapControl/Shared/WmtsTileLayer.cs +++ b/MapControl/Shared/WmtsTileLayer.cs @@ -90,8 +90,6 @@ namespace MapControl protected override void UpdateTileLayer() { - UpdateTimer.Stop(); - if (ParentMap == null || !TileMatrixSets.TryGetValue(ParentMap.MapProjection.CrsId, out WmtsTileMatrixSet tileMatrixSet)) { diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj index ec20050a..072b73e9 100644 --- a/MapControl/UWP/MapControl.UWP.csproj +++ b/MapControl/UWP/MapControl.UWP.csproj @@ -59,6 +59,9 @@ BoundingBox.cs + + BoundingBoxTileSource.cs + CenteredBoundingBox.cs diff --git a/SampleApps/Shared/MapLayers.cs b/SampleApps/Shared/MapLayers.cs index 9a4dfbee..5564bf9f 100644 --- a/SampleApps/Shared/MapLayers.cs +++ b/SampleApps/Shared/MapLayers.cs @@ -26,18 +26,25 @@ namespace ViewModel { TileSource = new TileSource { UriFormat = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" }, SourceName = "OpenStreetMap", - Description = "© [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)", - MaxZoomLevel = 19 + Description = "© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)" } }, { "OpenStreetMap German", new MapTileLayer { - TileSource = new TileSource { UriFormat = "https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png" }, + TileSource = new TileSource { UriFormat = "https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png" }, SourceName = "OpenStreetMap German", - Description = "© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)", - MaxZoomLevel = 19 + Description = "© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)" + } + }, + { + "OpenStreetMap French", + new MapTileLayer + { + TileSource = new TileSource { UriFormat = "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png" }, + SourceName = "OpenStreetMap French", + Description = "© [OpenStreetMap France](https://www.openstreetmap.fr/mentions-legales/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)" } }, { @@ -46,10 +53,19 @@ namespace ViewModel { TileSource = new TileSource { UriFormat = "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png" }, SourceName = "OpenTopoMap", - Description = "© [OpenTopoMap](https://opentopomap.org/)\n© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)", + Description = "© [OpenTopoMap](https://opentopomap.org/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)", MaxZoomLevel = 17 } }, + { + "Hike & Bike", + new MapTileLayer + { + TileSource = new TileSource { UriFormat = "https://tiles.wmflabs.org/hikebike/{z}/{x}/{y}.png" }, + SourceName = "HikeBike", + Description = "© [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)" + } + }, { "Seamarks", new MapTileLayer @@ -60,35 +76,6 @@ namespace ViewModel MaxZoomLevel = 18 } }, - { - "TopPlusOpen WMTS", - new WmtsTileLayer - { - SourceName = "TopPlusOpen", - Description = "© [BKG](https://gdz.bkg.bund.de/index.php/default/webdienste/topplus-produkte/wmts-topplusopen-wmts-topplus-open.html)", - CapabilitiesUri = new Uri("https://sgx.geodatenzentrum.de/wmts_topplus_open/1.0.0/WMTSCapabilities.xml") - } - }, - { - "TopPlusOpen WMS", - new WmsImageLayer - { - Description = "© [BKG](https://gdz.bkg.bund.de/index.php/default/webdienste/topplus-produkte/wms-topplusopen-mit-layer-fur-normalausgabe-und-druck-wms-topplus-open.html)", - ServiceUri = new Uri("https://sgx.geodatenzentrum.de/wms_topplus_open") - } - }, - { - "OpenStreetMap WMS", - new WmsImageLayer - { - Description = "© [terrestris GmbH & Co. KG](http://ows.terrestris.de/)\nData © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)", - ServiceUri = new Uri("http://ows.terrestris.de/osm/service") - } - }, - { - "SevenCs ChartServer WMS", - new ChartServerLayer() - }, { "Bing Maps Road", new BingMapsTileLayer @@ -120,6 +107,35 @@ namespace ViewModel MapBackground = new SolidColorBrush(Colors.Black) } }, + { + "TopPlusOpen WMTS", + new WmtsTileLayer + { + SourceName = "TopPlusOpen", + Description = "© [BKG](https://gdz.bkg.bund.de/index.php/default/webdienste/topplus-produkte/wmts-topplusopen-wmts-topplus-open.html)", + CapabilitiesUri = new Uri("https://sgx.geodatenzentrum.de/wmts_topplus_open/1.0.0/WMTSCapabilities.xml") + } + }, + { + "TopPlusOpen WMS", + new WmsImageLayer + { + Description = "© [BKG](https://gdz.bkg.bund.de/index.php/default/webdienste/topplus-produkte/wms-topplusopen-mit-layer-fur-normalausgabe-und-druck-wms-topplus-open.html)", + ServiceUri = new Uri("https://sgx.geodatenzentrum.de/wms_topplus_open") + } + }, + { + "OpenStreetMap WMS", + new WmsImageLayer + { + Description = "© [terrestris GmbH & Co. KG](http://ows.terrestris.de/) © [OpenStreetMap contributors](http://www.openstreetmap.org/copyright)", + ServiceUri = new Uri("http://ows.terrestris.de/osm/service") + } + }, + { + "SevenCs ChartServer WMS", + new ChartServerLayer() + }, }; private string currentMapLayerName = "OpenStreetMap"; @@ -149,7 +165,9 @@ namespace ViewModel { "OpenStreetMap", "OpenStreetMap German", + "OpenStreetMap French", "OpenTopoMap", + "Hike & Bike", "TopPlusOpen WMTS", "TopPlusOpen WMS", "OpenStreetMap WMS", diff --git a/SampleApps/WpfApplication/MainWindow.xaml b/SampleApps/WpfApplication/MainWindow.xaml index 2e5f9592..ea7ca7d9 100644 --- a/SampleApps/WpfApplication/MainWindow.xaml +++ b/SampleApps/WpfApplication/MainWindow.xaml @@ -174,8 +174,8 @@ - - + + diff --git a/SampleApps/WpfApplication/WpfApplication.csproj b/SampleApps/WpfApplication/WpfApplication.csproj index f3c4a5b1..38ff9037 100644 --- a/SampleApps/WpfApplication/WpfApplication.csproj +++ b/SampleApps/WpfApplication/WpfApplication.csproj @@ -13,20 +13,19 @@ - DEBUG + DEBUG - + - - + - +