diff --git a/MBTiles/Shared/MBTileLayer.cs b/MBTiles/Shared/MBTileLayer.cs
index 4353ff71..394d5f93 100644
--- a/MBTiles/Shared/MBTileLayer.cs
+++ b/MBTiles/Shared/MBTileLayer.cs
@@ -16,7 +16,7 @@ namespace MapControl.MBTiles
///
/// MapTileLayer that uses an MBTiles SQLite Database. See https://wiki.openstreetmap.org/wiki/MBTiles.
///
- public class MBTileLayer : MapTileLayer
+ public partial class MBTileLayer : MapTileLayer
{
private static ILogger logger;
private static ILogger Logger => logger ??= ImageLoader.LoggerFactory?.CreateLogger();
diff --git a/MBTiles/Shared/MBTileSource.cs b/MBTiles/Shared/MBTileSource.cs
index 3984208c..92af2522 100644
--- a/MBTiles/Shared/MBTileSource.cs
+++ b/MBTiles/Shared/MBTileSource.cs
@@ -15,7 +15,7 @@ using ImageSource = Avalonia.Media.IImage;
namespace MapControl.MBTiles
{
- public sealed class MBTileSource : TileSource, IDisposable
+ public sealed partial class MBTileSource : TileSource, IDisposable
{
private static ILogger logger;
private static ILogger Logger => logger ??= ImageLoader.LoggerFactory?.CreateLogger();
diff --git a/MapControl/Avalonia/DependencyPropertyHelper.Avalonia.cs b/MapControl/Avalonia/DependencyPropertyHelper.Avalonia.cs
index a286d156..7804d31a 100644
--- a/MapControl/Avalonia/DependencyPropertyHelper.Avalonia.cs
+++ b/MapControl/Avalonia/DependencyPropertyHelper.Avalonia.cs
@@ -1,4 +1,5 @@
-using Avalonia;
+global using DependencyProperty = Avalonia.AvaloniaProperty;
+using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using System;
diff --git a/MapControl/Avalonia/MapBase.Avalonia.cs b/MapControl/Avalonia/MapBase.Avalonia.cs
index df48b817..1ee197fb 100644
--- a/MapControl/Avalonia/MapBase.Avalonia.cs
+++ b/MapControl/Avalonia/MapBase.Avalonia.cs
@@ -1,9 +1,4 @@
-global using DependencyProperty = Avalonia.AvaloniaProperty;
-global using FrameworkElement = Avalonia.Controls.Control;
-global using Brush = Avalonia.Media.IBrush;
-global using ImageSource = Avalonia.Media.IImage;
-global using PropertyPath = System.String;
-using Avalonia;
+using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
@@ -13,6 +8,7 @@ using Avalonia.Media;
using Avalonia.Styling;
using System.Threading;
using System.Threading.Tasks;
+using Brush = Avalonia.Media.IBrush;
namespace MapControl
{
diff --git a/MapControl/Avalonia/MapPanel.Avalonia.cs b/MapControl/Avalonia/MapPanel.Avalonia.cs
index b419beff..0f38ad2c 100644
--- a/MapControl/Avalonia/MapPanel.Avalonia.cs
+++ b/MapControl/Avalonia/MapPanel.Avalonia.cs
@@ -1,5 +1,5 @@
-using Avalonia;
-using Avalonia.Controls;
+global using FrameworkElement = Avalonia.Controls.Control;
+using Avalonia;
using Avalonia.Media;
namespace MapControl
@@ -20,18 +20,18 @@ namespace MapControl
AffectsParentArrange(LocationProperty, BoundingBoxProperty);
}
- public static MapBase GetParentMap(Control element)
+ public static MapBase GetParentMap(FrameworkElement element)
{
return (MapBase)element.GetValue(ParentMapProperty);
}
- public static void SetRenderTransform(Control element, Transform transform, double originX = 0d, double originY = 0d)
+ public static void SetRenderTransform(FrameworkElement element, Transform transform, double originX = 0d, double originY = 0d)
{
element.RenderTransform = transform;
element.RenderTransformOrigin = new RelativePoint(originX, originY, RelativeUnit.Relative);
}
- private static void SetVisible(Control element, bool visible)
+ private static void SetVisible(FrameworkElement element, bool visible)
{
element.IsVisible = visible;
}
diff --git a/MapControl/Shared/ImageLoader.cs b/MapControl/Shared/ImageLoader.cs
index dc051064..9522e11a 100644
--- a/MapControl/Shared/ImageLoader.cs
+++ b/MapControl/Shared/ImageLoader.cs
@@ -9,6 +9,8 @@ using System.Windows.Media;
using Windows.UI.Xaml.Media;
#elif WINUI
using Microsoft.UI.Xaml.Media;
+#elif AVALONIA
+using ImageSource = Avalonia.Media.IImage;
#endif
namespace MapControl
diff --git a/MapControl/Shared/MapBase.MapLayer.cs b/MapControl/Shared/MapBase.MapLayer.cs
index c336a200..70115306 100644
--- a/MapControl/Shared/MapBase.MapLayer.cs
+++ b/MapControl/Shared/MapBase.MapLayer.cs
@@ -14,6 +14,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#elif AVALONIA
using Avalonia.Controls;
+using Brush = Avalonia.Media.IBrush;
#endif
namespace MapControl
diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs
index 9d4daa6e..18925dbc 100644
--- a/MapControl/Shared/MapBase.cs
+++ b/MapControl/Shared/MapBase.cs
@@ -10,6 +10,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#elif AVALONIA
using Avalonia;
+using Brush = Avalonia.Media.IBrush;
#endif
namespace MapControl
diff --git a/MapControl/Shared/MapImageLayer.cs b/MapControl/Shared/MapImageLayer.cs
index 2871e598..2705930a 100644
--- a/MapControl/Shared/MapImageLayer.cs
+++ b/MapControl/Shared/MapImageLayer.cs
@@ -18,6 +18,8 @@ using Microsoft.UI.Xaml.Media;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
+using Brush = Avalonia.Media.IBrush;
+using ImageSource = Avalonia.Media.IImage;
#endif
namespace MapControl
diff --git a/MapControl/Shared/MapItemsControl.cs b/MapControl/Shared/MapItemsControl.cs
index d4842afe..d7e73ab5 100644
--- a/MapControl/Shared/MapItemsControl.cs
+++ b/MapControl/Shared/MapItemsControl.cs
@@ -15,6 +15,7 @@ using Microsoft.UI.Xaml.Data;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
+using PropertyPath = System.String;
#endif
namespace MapControl
diff --git a/MapControl/Shared/MapScale.cs b/MapControl/Shared/MapScale.cs
index 6ffac495..a5e3dcf0 100644
--- a/MapControl/Shared/MapScale.cs
+++ b/MapControl/Shared/MapScale.cs
@@ -27,6 +27,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Data;
using Avalonia.Layout;
using PointCollection = System.Collections.Generic.List;
+using PropertyPath = System.String;
#endif
namespace MapControl
diff --git a/MapControl/Shared/MapTileLayer.cs b/MapControl/Shared/MapTileLayer.cs
index 705fd4f2..f2986741 100644
--- a/MapControl/Shared/MapTileLayer.cs
+++ b/MapControl/Shared/MapTileLayer.cs
@@ -22,7 +22,7 @@ namespace MapControl
///
/// Displays a standard Web Mercator map tile pyramid, e.g. a OpenStreetMap tiles.
///
- public partial class MapTileLayer : MapTilePyramidLayer
+ public partial class MapTileLayer : TilePyramidLayer
{
public static readonly DependencyProperty MinZoomLevelProperty =
DependencyPropertyHelper.Register(nameof(MinZoomLevel), 0);
diff --git a/MapControl/Shared/Tile.cs b/MapControl/Shared/Tile.cs
index fdabcef1..411b4ee0 100644
--- a/MapControl/Shared/Tile.cs
+++ b/MapControl/Shared/Tile.cs
@@ -14,15 +14,14 @@ using Avalonia.Media;
namespace MapControl
{
- public partial class Tile(int zoomLevel, int x, int y, int columnCount)
+ public partial class Tile(int zoomLevel, int x, int y, int columnCount) : ITile
{
public int ZoomLevel { get; } = zoomLevel;
public int X { get; } = x;
public int Y { get; } = y;
public int Column { get; } = ((x % columnCount) + columnCount) % columnCount;
public int Row => Y;
-
- public Image Image { get; } = new Image { Stretch = Stretch.Fill };
public bool IsPending { get; set; } = true;
+ public Image Image { get; } = new Image { Stretch = Stretch.Fill };
}
}
diff --git a/MapControl/Shared/TileImageLoader.cs b/MapControl/Shared/TileImageLoader.cs
index 1525e1fc..c2559da4 100644
--- a/MapControl/Shared/TileImageLoader.cs
+++ b/MapControl/Shared/TileImageLoader.cs
@@ -7,19 +7,71 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+#if WPF
+using System.Windows.Media;
+#elif UWP
+using Windows.UI.Xaml.Media;
+#elif WINUI
+using Microsoft.UI.Xaml.Media;
+#elif AVALONIA
+using ImageSource = Avalonia.Media.IImage;
+#endif
namespace MapControl
{
- ///
- /// Loads and optionally caches map tile images for a MapTileLayer.
- ///
+ public interface ITile
+ {
+ int ZoomLevel { get; }
+ int Column { get; }
+ int Row { get; }
+ bool IsPending { get; set; }
+
+ ///
+ /// Runs a tile image download Task and marshals the result to the UI thread.
+ ///
+ Task LoadImageAsync(Func> loadImageFunc);
+ }
+
+ public interface ITileSource
+ {
+ ///
+ /// Indicates whether tile images from this source should be cached.
+ ///
+ bool Cacheable { get; }
+
+ ///
+ /// Gets the image Uri for the specified zoom level and tile indices.
+ ///
+ Uri GetUri(int zoomLevel, int column, int row);
+
+ ///
+ /// Loads a tile image whithout caching.
+ ///
+ Task LoadImageAsync(int zoomLevel, int column, int row);
+
+ ///
+ /// Loads a cacheable tile image from an encoded frame buffer.
+ ///
+ Task LoadImageAsync(byte[] buffer);
+ }
+
public interface ITileImageLoader
{
- void BeginLoadTiles(IEnumerable tiles, TileSource tileSource, string cacheName, IProgress progress);
+ ///
+ /// Loads all pending tiles from the tiles collection.
+ /// Tile image caching is enabled when tileSource.UriFormat starts with "http" and cacheName is a non-empty string.
+ ///
+ void BeginLoadTiles(IEnumerable tiles, ITileSource tileSource, string cacheName, IProgress progress);
+ ///
+ /// Cancels a potentially ongoing tile loading task.
+ ///
void CancelLoadTiles();
}
+ ///
+ /// Loads and optionally caches map tile images for a MapTilePyramidLayer.
+ ///
public class TileImageLoader : ITileImageLoader
{
private static ILogger logger;
@@ -63,17 +115,13 @@ namespace MapControl
///
public static int MaxLoadTasks { get; set; } = 4;
- private readonly Queue tileQueue = new();
+ private readonly Queue tileQueue = new();
private int tileCount;
private int taskCount;
- ///
- /// Loads all pending tiles from the tiles collection. Tile image caching is enabled when the Cache
- /// property is not null and tileSource.UriFormat starts with "http" and cacheName is a non-empty string.
- ///
- public void BeginLoadTiles(IEnumerable tiles, TileSource tileSource, string cacheName, IProgress progress)
+ public void BeginLoadTiles(IEnumerable tiles, ITileSource tileSource, string cacheName, IProgress progress)
{
- if (Cache == null || tileSource.UriTemplate == null || !tileSource.UriTemplate.StartsWith("http"))
+ if (Cache == null || !tileSource.Cacheable)
{
cacheName = null; // disable caching
}
@@ -110,9 +158,9 @@ namespace MapControl
}
}
- private async Task LoadTilesFromQueue(TileSource tileSource, string cacheName, IProgress progress)
+ private async Task LoadTilesFromQueue(ITileSource tileSource, string cacheName, IProgress progress)
{
- bool TryDequeueTile(out Tile tile)
+ bool TryDequeueTile(out ITile tile)
{
lock (tileQueue)
{
@@ -130,7 +178,7 @@ namespace MapControl
return false;
}
- while (TryDequeueTile(out Tile tile))
+ while (TryDequeueTile(out ITile tile))
{
tile.IsPending = false;
@@ -170,7 +218,7 @@ namespace MapControl
}
}
- private static async Task LoadCachedBuffer(Tile tile, Uri uri, string cacheName)
+ private static async Task LoadCachedBuffer(ITile tile, Uri uri, string cacheName)
{
var extension = Path.GetExtension(uri.LocalPath).ToLower();
diff --git a/MapControl/Shared/MapTilePyramidLayer.cs b/MapControl/Shared/TilePyramidLayer.cs
similarity index 86%
rename from MapControl/Shared/MapTilePyramidLayer.cs
rename to MapControl/Shared/TilePyramidLayer.cs
index a63a37f1..f8216a80 100644
--- a/MapControl/Shared/MapTilePyramidLayer.cs
+++ b/MapControl/Shared/TilePyramidLayer.cs
@@ -20,47 +20,48 @@ using Microsoft.UI.Xaml.Media;
#elif AVALONIA
using Avalonia.Controls;
using Avalonia.Media;
+using Brush = Avalonia.Media.IBrush;
#endif
namespace MapControl
{
- public abstract class MapTilePyramidLayer : Panel, IMapLayer
+ public abstract class TilePyramidLayer : Panel, IMapLayer
{
public static readonly DependencyProperty TileSourceProperty =
- DependencyPropertyHelper.Register(nameof(TileSource), null,
+ DependencyPropertyHelper.Register(nameof(TileSource), null,
(layer, oldValue, newValue) => layer.UpdateTiles(true));
public static readonly DependencyProperty SourceNameProperty =
- DependencyPropertyHelper.Register(nameof(SourceName));
+ DependencyPropertyHelper.Register(nameof(SourceName));
public static readonly DependencyProperty DescriptionProperty =
- DependencyPropertyHelper.Register(nameof(Description));
+ DependencyPropertyHelper.Register(nameof(Description));
public static readonly DependencyProperty MaxBackgroundLevelsProperty =
- DependencyPropertyHelper.Register(nameof(MaxBackgroundLevels), 5);
+ DependencyPropertyHelper.Register(nameof(MaxBackgroundLevels), 5);
public static readonly DependencyProperty UpdateIntervalProperty =
- DependencyPropertyHelper.Register(nameof(UpdateInterval), TimeSpan.FromSeconds(0.2),
+ DependencyPropertyHelper.Register(nameof(UpdateInterval), TimeSpan.FromSeconds(0.2),
(layer, oldValue, newValue) => layer.updateTimer.Interval = newValue);
public static readonly DependencyProperty UpdateWhileViewportChangingProperty =
- DependencyPropertyHelper.Register(nameof(UpdateWhileViewportChanging));
+ DependencyPropertyHelper.Register(nameof(UpdateWhileViewportChanging));
public static readonly DependencyProperty MapBackgroundProperty =
- DependencyPropertyHelper.Register(nameof(MapBackground));
+ DependencyPropertyHelper.Register(nameof(MapBackground));
public static readonly DependencyProperty MapForegroundProperty =
- DependencyPropertyHelper.Register(nameof(MapForeground));
+ DependencyPropertyHelper.Register(nameof(MapForeground));
public static readonly DependencyProperty LoadingProgressProperty =
- DependencyPropertyHelper.Register(nameof(LoadingProgress), 1d);
+ DependencyPropertyHelper.Register(nameof(LoadingProgress), 1d);
private readonly Progress loadingProgress;
private readonly UpdateTimer updateTimer;
private ITileImageLoader tileImageLoader;
private MapBase parentMap;
- protected MapTilePyramidLayer()
+ protected TilePyramidLayer()
{
IsHitTestVisible = false;
@@ -191,7 +192,7 @@ namespace MapControl
protected bool IsBaseMapLayer => parentMap != null && parentMap.Children.Count > 0 && parentMap.Children[0] == this;
- protected void BeginLoadTiles(IEnumerable tiles, string cacheName)
+ protected void BeginLoadTiles(IEnumerable tiles, string cacheName)
{
if (TileSource != null && tiles != null && tiles.Any(tile => tile.IsPending))
{
diff --git a/MapControl/Shared/TileSource.cs b/MapControl/Shared/TileSource.cs
index cc2b7844..4c73c62f 100644
--- a/MapControl/Shared/TileSource.cs
+++ b/MapControl/Shared/TileSource.cs
@@ -7,6 +7,8 @@ using System.Windows.Media;
using Windows.UI.Xaml.Media;
#elif WINUI
using Microsoft.UI.Xaml.Media;
+#elif AVALONIA
+using ImageSource = Avalonia.Media.IImage;
#endif
namespace MapControl
@@ -19,7 +21,7 @@ namespace MapControl
#else
[System.ComponentModel.TypeConverter(typeof(TileSourceConverter))]
#endif
- public class TileSource
+ public class TileSource : ITileSource
{
private string uriTemplate;
@@ -35,19 +37,15 @@ namespace MapControl
if (uriTemplate != null && uriTemplate.Contains("{s}") && Subdomains == null)
{
- Subdomains = new string[] { "a", "b", "c" }; // default OpenStreetMap subdomains
+ Subdomains = ["a", "b", "c"]; // default OpenStreetMap subdomains
}
}
}
- ///
- /// Gets or sets an array of request subdomain names that are replaced for the {s} format specifier.
- ///
public string[] Subdomains { get; set; }
- ///
- /// Gets the image Uri for the specified tile indices and zoom level.
- ///
+ public bool Cacheable => UriTemplate != null && UriTemplate.StartsWith("http");
+
public virtual Uri GetUri(int zoomLevel, int column, int row)
{
Uri uri = null;
@@ -71,10 +69,6 @@ namespace MapControl
return uri;
}
- ///
- /// Loads a tile ImageSource asynchronously from GetUri(zoomLevel, column, row).
- /// This method is called by TileImageLoader when caching is disabled.
- ///
public virtual Task LoadImageAsync(int zoomLevel, int column, int row)
{
var uri = GetUri(zoomLevel, column, row);
@@ -82,10 +76,6 @@ namespace MapControl
return uri != null ? ImageLoader.LoadImageAsync(uri) : Task.FromResult((ImageSource)null);
}
- ///
- /// Loads a tile ImageSource asynchronously from an encoded frame buffer in a byte array.
- /// This method is called by TileImageLoader when caching is enabled.
- ///
public virtual Task LoadImageAsync(byte[] buffer)
{
return ImageLoader.LoadImageAsync(buffer);
diff --git a/MapControl/Shared/TypeConverters.cs b/MapControl/Shared/TypeConverters.cs
index 010d241f..52e775d7 100644
--- a/MapControl/Shared/TypeConverters.cs
+++ b/MapControl/Shared/TypeConverters.cs
@@ -110,7 +110,7 @@ namespace MapControl
}
}
- public class MapProjectionConverter : TypeConverter, IValueConverter
+ public partial class MapProjectionConverter : TypeConverter, IValueConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
diff --git a/MapControl/Shared/WmsImageLayer.cs b/MapControl/Shared/WmsImageLayer.cs
index 36010aac..35484f4c 100644
--- a/MapControl/Shared/WmsImageLayer.cs
+++ b/MapControl/Shared/WmsImageLayer.cs
@@ -17,6 +17,7 @@ using Microsoft.UI.Xaml.Media;
#elif AVALONIA
using Avalonia;
using Avalonia.Interactivity;
+using ImageSource = Avalonia.Media.IImage;
#endif
namespace MapControl
diff --git a/MapControl/Shared/WmtsTileLayer.cs b/MapControl/Shared/WmtsTileLayer.cs
index 1550a701..8093af0d 100644
--- a/MapControl/Shared/WmtsTileLayer.cs
+++ b/MapControl/Shared/WmtsTileLayer.cs
@@ -20,7 +20,7 @@ namespace MapControl
///
/// Displays map tiles from a Web Map Tile Service (WMTS).
///
- public partial class WmtsTileLayer : MapTilePyramidLayer
+ public partial class WmtsTileLayer : TilePyramidLayer
{
private static ILogger logger;
private static ILogger Logger => logger ??= ImageLoader.LoggerFactory?.CreateLogger(typeof(WmtsTileLayer));