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
-
+
-
-
+
-
+