mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Reverted to not awaitable TileImageLoader
This commit is contained in:
parent
775d584df7
commit
178cec8e66
|
|
@ -28,7 +28,7 @@ namespace MapControl
|
||||||
|
|
||||||
static ImageLoader()
|
static ImageLoader()
|
||||||
{
|
{
|
||||||
HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
|
HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
|
||||||
HttpClient.DefaultRequestHeaders.Add("User-Agent", $"XAML-Map-Control/{typeof(ImageLoader).Assembly.GetName().Version}");
|
HttpClient.DefaultRequestHeaders.Add("User-Agent", $"XAML-Map-Control/{typeof(ImageLoader).Assembly.GetName().Version}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,13 +126,13 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException ex)
|
catch (OperationCanceledException ex)
|
||||||
{
|
{
|
||||||
if (ex.InnerException is TimeoutException timeout)
|
if (ex.CancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Logger?.LogError(timeout, "Failed loading image from {uri}", uri);
|
Logger?.LogTrace("Cancelled loading image from {uri}", uri);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger?.LogTrace("Cancelled loading image from {uri}", uri);
|
Logger?.LogError(ex, "Failed loading image from {uri}", uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
@ -112,7 +111,7 @@ namespace MapControl
|
||||||
return finalSize;
|
return finalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task UpdateTileLayerAsync(bool resetTiles)
|
protected override void UpdateTileLayerAsync(bool resetTiles)
|
||||||
{
|
{
|
||||||
if (ParentMap == null || ParentMap.MapProjection.Type != MapProjectionType.WebMercator)
|
if (ParentMap == null || ParentMap.MapProjection.Type != MapProjectionType.WebMercator)
|
||||||
{
|
{
|
||||||
|
|
@ -131,8 +130,7 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateTiles();
|
UpdateTiles();
|
||||||
|
LoadTiles(Tiles, SourceName);
|
||||||
await LoadTilesAsync(Tiles, SourceName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
@ -32,7 +30,7 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
public static readonly DependencyProperty TileSourceProperty =
|
public static readonly DependencyProperty TileSourceProperty =
|
||||||
DependencyPropertyHelper.Register<MapTileLayerBase, TileSource>(nameof(TileSource), null,
|
DependencyPropertyHelper.Register<MapTileLayerBase, TileSource>(nameof(TileSource), null,
|
||||||
async (layer, oldValue, newValue) => await layer.UpdateTileLayer(true));
|
(layer, oldValue, newValue) => layer.UpdateTileLayer(true));
|
||||||
|
|
||||||
public static readonly DependencyProperty SourceNameProperty =
|
public static readonly DependencyProperty SourceNameProperty =
|
||||||
DependencyPropertyHelper.Register<MapTileLayerBase, string>(nameof(SourceName));
|
DependencyPropertyHelper.Register<MapTileLayerBase, string>(nameof(SourceName));
|
||||||
|
|
@ -61,7 +59,6 @@ namespace MapControl
|
||||||
|
|
||||||
private readonly Progress<double> loadingProgress;
|
private readonly Progress<double> loadingProgress;
|
||||||
private readonly DispatcherTimer updateTimer;
|
private readonly DispatcherTimer updateTimer;
|
||||||
private CancellationTokenSource cancellationTokenSource;
|
|
||||||
private ITileImageLoader tileImageLoader;
|
private ITileImageLoader tileImageLoader;
|
||||||
private MapBase parentMap;
|
private MapBase parentMap;
|
||||||
|
|
||||||
|
|
@ -72,7 +69,7 @@ namespace MapControl
|
||||||
loadingProgress = new Progress<double>(p => SetValue(LoadingProgressProperty, p));
|
loadingProgress = new Progress<double>(p => SetValue(LoadingProgressProperty, p));
|
||||||
|
|
||||||
updateTimer = this.CreateTimer(UpdateInterval);
|
updateTimer = this.CreateTimer(UpdateInterval);
|
||||||
updateTimer.Tick += async (s, e) => await UpdateTileLayer(false);
|
updateTimer.Tick += (s, e) => UpdateTileLayer(false);
|
||||||
|
|
||||||
MapPanel.SetRenderTransform(this, new MatrixTransform());
|
MapPanel.SetRenderTransform(this, new MatrixTransform());
|
||||||
#if WPF
|
#if WPF
|
||||||
|
|
@ -194,44 +191,37 @@ namespace MapControl
|
||||||
|
|
||||||
protected bool IsBaseMapLayer => parentMap != null && parentMap.Children.Count > 0 && parentMap.Children[0] == this;
|
protected bool IsBaseMapLayer => parentMap != null && parentMap.Children.Count > 0 && parentMap.Children[0] == this;
|
||||||
|
|
||||||
protected async Task LoadTilesAsync(IEnumerable<Tile> tiles, string cacheName)
|
protected void LoadTiles(IEnumerable<Tile> tiles, string cacheName)
|
||||||
{
|
{
|
||||||
cancellationTokenSource?.Cancel();
|
|
||||||
|
|
||||||
if (TileSource != null && tiles != null && tiles.Any(tile => tile.IsPending))
|
if (TileSource != null && tiles != null && tiles.Any(tile => tile.IsPending))
|
||||||
{
|
{
|
||||||
using (cancellationTokenSource = new CancellationTokenSource())
|
TileImageLoader.LoadTiles(tiles, TileSource, cacheName, loadingProgress);
|
||||||
{
|
|
||||||
await TileImageLoader.LoadTilesAsync(tiles, TileSource, cacheName, loadingProgress, cancellationTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
cancellationTokenSource = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void CancelLoadTiles()
|
protected void CancelLoadTiles()
|
||||||
{
|
{
|
||||||
cancellationTokenSource?.Cancel();
|
TileImageLoader.CancelLoadTiles();
|
||||||
|
|
||||||
ClearValue(LoadingProgressProperty);
|
ClearValue(LoadingProgressProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void SetRenderTransform();
|
protected abstract void SetRenderTransform();
|
||||||
|
|
||||||
protected abstract Task UpdateTileLayerAsync(bool resetTiles);
|
protected abstract void UpdateTileLayerAsync(bool resetTiles);
|
||||||
|
|
||||||
private Task UpdateTileLayer(bool resetTiles)
|
private void UpdateTileLayer(bool resetTiles)
|
||||||
{
|
{
|
||||||
updateTimer.Stop();
|
updateTimer.Stop();
|
||||||
|
|
||||||
return UpdateTileLayerAsync(resetTiles);
|
UpdateTileLayerAsync(resetTiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnViewportChanged(object sender, ViewportChangedEventArgs e)
|
private void OnViewportChanged(object sender, ViewportChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.TransformCenterChanged || e.ProjectionChanged || Children.Count == 0)
|
if (e.TransformCenterChanged || e.ProjectionChanged || Children.Count == 0)
|
||||||
{
|
{
|
||||||
await UpdateTileLayer(false); // update immediately
|
UpdateTileLayer(false); // update immediately
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ namespace MapControl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITileImageLoader
|
public interface ITileImageLoader
|
||||||
{
|
{
|
||||||
Task LoadTilesAsync(IEnumerable<Tile> tiles, TileSource tileSource, string cacheName, IProgress<double> progress, CancellationToken cancellationToken);
|
void LoadTiles(IEnumerable<Tile> tiles, TileSource tileSource, string cacheName, IProgress<double> progress);
|
||||||
|
|
||||||
|
void CancelLoadTiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class TileImageLoader : ITileImageLoader
|
public partial class TileImageLoader : ITileImageLoader
|
||||||
|
|
@ -70,56 +72,69 @@ namespace MapControl
|
||||||
private static ILogger logger;
|
private static ILogger logger;
|
||||||
private static ILogger Logger => logger ?? (logger = ImageLoader.LoggerFactory?.CreateLogger<TileImageLoader>());
|
private static ILogger Logger => logger ?? (logger = ImageLoader.LoggerFactory?.CreateLogger<TileImageLoader>());
|
||||||
|
|
||||||
|
private readonly ConcurrentStack<Tile> tileStack = new ConcurrentStack<Tile>();
|
||||||
|
private int tileCount;
|
||||||
|
private int taskCount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads all pending tiles from the tiles collection. Tile image caching is enabled when the Cache
|
/// 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.
|
/// property is not null and tileSource.UriFormat starts with "http" and cacheName is a non-empty string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task LoadTilesAsync(IEnumerable<Tile> tiles, TileSource tileSource, string cacheName, IProgress<double> progress, CancellationToken cancellationToken)
|
public void LoadTiles(IEnumerable<Tile> tiles, TileSource tileSource, string cacheName, IProgress<double> progress)
|
||||||
{
|
{
|
||||||
var pendingTiles = new ConcurrentStack<Tile>(tiles.Where(tile => tile.IsPending).Reverse());
|
if (Cache == null || tileSource.UriTemplate == null || !tileSource.UriTemplate.StartsWith("http"))
|
||||||
var tileCount = pendingTiles.Count;
|
|
||||||
var taskCount = Math.Min(tileCount, MaxLoadTasks);
|
|
||||||
|
|
||||||
if (taskCount > 0)
|
|
||||||
{
|
{
|
||||||
if (Cache == null || tileSource.UriTemplate == null || !tileSource.UriTemplate.StartsWith("http"))
|
cacheName = null; // disable caching
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentTiles = tiles.Where(tile => tile.IsPending).Reverse().ToArray();
|
||||||
|
|
||||||
|
tileStack.Clear();
|
||||||
|
tileStack.PushRange(currentTiles);
|
||||||
|
tileCount = currentTiles.Length;
|
||||||
|
|
||||||
|
var maxTasks = Math.Min(tileCount, MaxLoadTasks);
|
||||||
|
|
||||||
|
while (taskCount < maxTasks)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref taskCount);
|
||||||
|
Logger?.LogTrace("Task count: {count}", taskCount);
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
cacheName = null; // disable caching
|
await LoadTilesFromStack(tileSource, cacheName, progress).ConfigureAwait(false);
|
||||||
}
|
|
||||||
|
|
||||||
async Task LoadTilesFromQueue()
|
Interlocked.Decrement(ref taskCount);
|
||||||
{
|
Logger?.LogTrace("Task count: {count}", taskCount);
|
||||||
while (!cancellationToken.IsCancellationRequested && pendingTiles.TryPop(out var tile))
|
});
|
||||||
{
|
}
|
||||||
tile.IsPending = false;
|
}
|
||||||
|
|
||||||
progress?.Report((double)(tileCount - pendingTiles.Count) / tileCount);
|
public void CancelLoadTiles()
|
||||||
|
{
|
||||||
|
tileStack.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
Logger?.LogTrace("[{thread}] Loading {zoom}/{column}/{row}", Environment.CurrentManagedThreadId, tile.ZoomLevel, tile.Column, tile.Row);
|
private async Task LoadTilesFromStack(TileSource tileSource, string cacheName, IProgress<double> progress)
|
||||||
|
{
|
||||||
|
while (tileStack.TryPop(out var tile))
|
||||||
|
{
|
||||||
|
tile.IsPending = false;
|
||||||
|
|
||||||
try
|
var tilesLoaded = tileCount - tileStack.Count;
|
||||||
{
|
|
||||||
await LoadTileImage(tile, tileSource, cacheName).ConfigureAwait(false);
|
progress?.Report((double)tilesLoaded / tileCount);
|
||||||
}
|
|
||||||
catch (Exception ex)
|
Logger?.LogTrace("[{thread}] Loading tile {loaded} of {count}: {zoom}/{column}/{row}",
|
||||||
{
|
Environment.CurrentManagedThreadId, tilesLoaded, tileCount, tile.ZoomLevel, tile.Column, tile.Row);
|
||||||
Logger?.LogError(ex, "Failed loading {zoom}/{column}/{row}", tile.ZoomLevel, tile.Column, tile.Row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.WhenAll(Enumerable.Range(0, taskCount).Select(_ => Task.Run(LoadTilesFromQueue, cancellationToken)));
|
await LoadTileImage(tile, tileSource, cacheName).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// no action
|
Logger?.LogError(ex, "Failed loading tile {zoom}/{column}/{row}", tile.ZoomLevel, tile.Column, tile.Row);
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
Logger?.LogTrace("Cancelled LoadTilesAsync with {count} pending tiles", pendingTiles.Count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
#elif UWP
|
#elif UWP
|
||||||
|
|
@ -92,7 +91,7 @@ namespace MapControl
|
||||||
return finalSize;
|
return finalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task UpdateTileLayerAsync(bool resetTiles)
|
protected override void UpdateTileLayerAsync(bool resetTiles)
|
||||||
{
|
{
|
||||||
// resetTiles is ignored here because it is always false.
|
// resetTiles is ignored here because it is always false.
|
||||||
|
|
||||||
|
|
@ -121,7 +120,7 @@ namespace MapControl
|
||||||
|
|
||||||
var tiles = ChildLayers.SelectMany(layer => layer.Tiles);
|
var tiles = ChildLayers.SelectMany(layer => layer.Tiles);
|
||||||
|
|
||||||
await LoadTilesAsync(tiles, cacheName);
|
LoadTiles(tiles, cacheName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue