mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Image loading with cancellation
This commit is contained in:
parent
81eabef257
commit
69bba213f0
|
|
@ -4,6 +4,7 @@ using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
|
|
@ -38,17 +39,18 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task<IImage> LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress<double> progress)
|
internal static async Task<IImage> LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
WriteableBitmap mergedBitmap = null;
|
WriteableBitmap mergedBitmap = null;
|
||||||
var p1 = 0d;
|
var p1 = 0d;
|
||||||
var p2 = 0d;
|
var p2 = 0d;
|
||||||
|
|
||||||
var images = await Task.WhenAll(
|
var images = await Task.WhenAll(
|
||||||
LoadImageAsync(uri1, new Progress<double>(p => { p1 = p; progress.Report((p1 + p2) / 2d); })),
|
LoadImageAsync(uri1, new Progress<double>(p => { p1 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken),
|
||||||
LoadImageAsync(uri2, new Progress<double>(p => { p2 = p; progress.Report((p1 + p2) / 2d); })));
|
LoadImageAsync(uri2, new Progress<double>(p => { p2 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken));
|
||||||
|
|
||||||
if (images.Length == 2 &&
|
if (!cancellationToken.IsCancellationRequested &&
|
||||||
|
images.Length == 2 &&
|
||||||
images[0] is Bitmap bitmap1 &&
|
images[0] is Bitmap bitmap1 &&
|
||||||
images[1] is Bitmap bitmap2 &&
|
images[1] is Bitmap bitmap2 &&
|
||||||
bitmap1.PixelSize.Height == bitmap2.PixelSize.Height &&
|
bitmap1.PixelSize.Height == bitmap2.PixelSize.Height &&
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading.Tasks;
|
|
||||||
#if WPF
|
|
||||||
using System.Windows.Media;
|
|
||||||
#elif UWP
|
|
||||||
using Windows.UI.Xaml.Media;
|
|
||||||
#elif WINUI
|
|
||||||
using Microsoft.UI.Xaml.Media;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
{
|
{
|
||||||
|
|
@ -20,13 +12,6 @@ namespace MapControl
|
||||||
return GetUri(west, south, east, north);
|
return GetUri(west, south, east, north);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<ImageSource> LoadImageAsync(int column, int row, int zoomLevel)
|
|
||||||
{
|
|
||||||
GetTileBounds(column, row, zoomLevel, out double west, out double south, out double east, out double north);
|
|
||||||
|
|
||||||
return LoadImageAsync(west, south, east, north);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual Uri GetUri(double west, double south, double east, double north)
|
protected virtual Uri GetUri(double west, double south, double east, double north)
|
||||||
{
|
{
|
||||||
Uri uri = null;
|
Uri uri = null;
|
||||||
|
|
@ -46,13 +31,6 @@ namespace MapControl
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Task<ImageSource> LoadImageAsync(double west, double south, double east, double north)
|
|
||||||
{
|
|
||||||
var uri = GetUri(west, south, east, north);
|
|
||||||
|
|
||||||
return uri != null ? ImageLoader.LoadImageAsync(uri) : Task.FromResult((ImageSource)null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the bounding box in meters of a standard Web Mercator tile,
|
/// Gets the bounding box in meters of a standard Web Mercator tile,
|
||||||
/// specified by grid column and row indices and zoom level.
|
/// specified by grid column and row indices and zoom level.
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using System.Threading;
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
@ -164,7 +165,7 @@ namespace MapControl
|
||||||
|
|
||||||
foreach (var imageOverlay in imageOverlays)
|
foreach (var imageOverlay in imageOverlays)
|
||||||
{
|
{
|
||||||
imageOverlay.ImageSource = await ImageLoader.LoadImageAsync(new Uri(docUri, imageOverlay.ImagePath));
|
imageOverlay.ImageSource = await ImageLoader.LoadImageAsync(new Uri(docUri, imageOverlay.ImagePath), null, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageOverlays;
|
return imageOverlays;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
#elif UWP
|
#elif UWP
|
||||||
|
|
@ -31,7 +33,7 @@ namespace MapControl
|
||||||
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}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<ImageSource> LoadImageAsync(Uri uri, IProgress<double> progress = null)
|
public static async Task<ImageSource> LoadImageAsync(Uri uri, IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ImageSource image = null;
|
ImageSource image = null;
|
||||||
|
|
||||||
|
|
@ -41,7 +43,7 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||||
{
|
{
|
||||||
var response = await GetHttpResponseAsync(uri, progress);
|
var response = await GetHttpResponseAsync(uri, progress, cancellationToken);
|
||||||
|
|
||||||
if (response?.Buffer != null)
|
if (response?.Buffer != null)
|
||||||
{
|
{
|
||||||
|
|
@ -87,13 +89,13 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task<HttpResponse> GetHttpResponseAsync(Uri uri, IProgress<double> progress = null)
|
internal static async Task<HttpResponse> GetHttpResponseAsync(Uri uri, IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
HttpResponse response = null;
|
HttpResponse response = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var responseMessage = await HttpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
|
using (var responseMessage = await HttpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (responseMessage.IsSuccessStatusCode)
|
if (responseMessage.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
|
|
@ -116,6 +118,10 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Logger?.LogTrace("Cancelled loading image from {uri}", uri);
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger?.LogError(ex, "Failed loading image from {uri}", uri);
|
Logger?.LogError(ex, "Failed loading image from {uri}", uri);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
@ -53,7 +54,7 @@ namespace MapControl
|
||||||
|
|
||||||
private readonly Progress<double> loadingProgress;
|
private readonly Progress<double> loadingProgress;
|
||||||
private readonly DispatcherTimer updateTimer;
|
private readonly DispatcherTimer updateTimer;
|
||||||
private bool updateInProgress;
|
private CancellationTokenSource cancellationTokenSource;
|
||||||
|
|
||||||
public MapImageLayer()
|
public MapImageLayer()
|
||||||
{
|
{
|
||||||
|
|
@ -165,39 +166,37 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Task<ImageSource> GetImageAsync(BoundingBox boundingBox, IProgress<double> progress);
|
protected abstract Task<ImageSource> GetImageAsync(BoundingBox boundingBox, IProgress<double> progress, CancellationToken cancellationToken);
|
||||||
|
|
||||||
protected async Task UpdateImageAsync()
|
protected async Task UpdateImageAsync()
|
||||||
{
|
{
|
||||||
if (updateInProgress)
|
updateTimer.Stop();
|
||||||
|
|
||||||
|
if (cancellationTokenSource != null)
|
||||||
{
|
{
|
||||||
// Update image on next tick, start timer if not running.
|
cancellationTokenSource.Cancel();
|
||||||
//
|
cancellationTokenSource = null;
|
||||||
updateTimer.Run();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (ParentMap != null && ParentMap.ActualWidth > 0d && ParentMap.ActualHeight > 0d)
|
||||||
{
|
{
|
||||||
updateInProgress = true;
|
var width = ParentMap.ActualWidth * RelativeImageSize;
|
||||||
updateTimer.Stop();
|
var height = ParentMap.ActualHeight * RelativeImageSize;
|
||||||
|
var x = (ParentMap.ActualWidth - width) / 2d;
|
||||||
|
var y = (ParentMap.ActualHeight - height) / 2d;
|
||||||
|
|
||||||
ImageSource image = null;
|
var boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(x, y, width, height));
|
||||||
BoundingBox boundingBox = null;
|
|
||||||
|
|
||||||
if (ParentMap != null && ParentMap.ActualWidth > 0d && ParentMap.ActualHeight > 0d)
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var image = await GetImageAsync(boundingBox, loadingProgress, cancellationTokenSource.Token);
|
||||||
|
|
||||||
|
cancellationTokenSource = null;
|
||||||
|
|
||||||
|
if (image != null)
|
||||||
{
|
{
|
||||||
var width = ParentMap.ActualWidth * RelativeImageSize;
|
SwapImages(image, boundingBox);
|
||||||
var height = ParentMap.ActualHeight * RelativeImageSize;
|
|
||||||
var x = (ParentMap.ActualWidth - width) / 2d;
|
|
||||||
var y = (ParentMap.ActualHeight - height) / 2d;
|
|
||||||
|
|
||||||
boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(x, y, width, height));
|
|
||||||
|
|
||||||
image = await GetImageAsync(boundingBox, loadingProgress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SwapImages(image, boundingBox);
|
|
||||||
|
|
||||||
updateInProgress = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ namespace MapControl
|
||||||
TileMatrix = null;
|
TileMatrix = null;
|
||||||
Children.Clear();
|
Children.Clear();
|
||||||
|
|
||||||
await LoadTilesAsync(null, null); // stop TileImageLoader
|
CancelLoadTilesAsync();
|
||||||
}
|
}
|
||||||
else if (SetTileMatrix() || resetTiles)
|
else if (SetTileMatrix() || resetTiles)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,13 @@ namespace MapControl
|
||||||
return TileImageLoader.LoadTilesAsync(tiles, TileSource, cacheName, loadingProgress);
|
return TileImageLoader.LoadTilesAsync(tiles, TileSource, cacheName, loadingProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void CancelLoadTilesAsync()
|
||||||
|
{
|
||||||
|
TileImageLoader.CancelLoadTiles();
|
||||||
|
|
||||||
|
ClearValue(LoadingProgressProperty);
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract void SetRenderTransform();
|
protected abstract void SetRenderTransform();
|
||||||
|
|
||||||
protected abstract Task UpdateTileLayerAsync(bool resetTiles);
|
protected abstract Task UpdateTileLayerAsync(bool resetTiles);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
#elif UWP
|
#elif UWP
|
||||||
|
|
@ -24,6 +26,8 @@ namespace MapControl
|
||||||
public interface ITileImageLoader
|
public interface ITileImageLoader
|
||||||
{
|
{
|
||||||
Task LoadTilesAsync(IEnumerable<Tile> tiles, TileSource tileSource, string cacheName, IProgress<double> progress);
|
Task LoadTilesAsync(IEnumerable<Tile> tiles, TileSource tileSource, string cacheName, IProgress<double> progress);
|
||||||
|
|
||||||
|
void CancelLoadTiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class TileImageLoader : ITileImageLoader
|
public partial class TileImageLoader : ITileImageLoader
|
||||||
|
|
@ -71,17 +75,26 @@ namespace MapControl
|
||||||
|
|
||||||
private ConcurrentStack<Tile> pendingTiles = new ConcurrentStack<Tile>();
|
private ConcurrentStack<Tile> pendingTiles = new ConcurrentStack<Tile>();
|
||||||
|
|
||||||
|
private CancellationTokenSource cancellationTokenSource;
|
||||||
|
|
||||||
|
public void CancelLoadTiles()
|
||||||
|
{
|
||||||
|
pendingTiles.Clear();
|
||||||
|
|
||||||
|
if (cancellationTokenSource != null)
|
||||||
|
{
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
cancellationTokenSource = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <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 non-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)
|
public async Task LoadTilesAsync(IEnumerable<Tile> tiles, TileSource tileSource, string cacheName, IProgress<double> progress)
|
||||||
{
|
{
|
||||||
if (!pendingTiles.IsEmpty)
|
CancelLoadTiles();
|
||||||
{
|
|
||||||
pendingTiles.Clear();
|
|
||||||
progress?.Report(1d);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tileSource != null && tiles != null && (tiles = tiles.Where(tile => tile.IsPending)).Any())
|
if (tileSource != null && tiles != null && (tiles = tiles.Where(tile => tile.IsPending)).Any())
|
||||||
{
|
{
|
||||||
|
|
@ -102,22 +115,24 @@ namespace MapControl
|
||||||
var tasks = new Task[taskCount];
|
var tasks = new Task[taskCount];
|
||||||
var tileStack = pendingTiles; // pendingTiles member may change while tasks are running
|
var tileStack = pendingTiles; // pendingTiles member may change while tasks are running
|
||||||
|
|
||||||
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
async Task LoadTilesFromQueueAsync()
|
async Task LoadTilesFromQueueAsync()
|
||||||
{
|
{
|
||||||
while (tileStack.TryPop(out var tile)) // use captured tileStack variable in local function
|
while (tileStack.TryPop(out var tile)) // use captured tileStack variable in local function
|
||||||
{
|
{
|
||||||
tile.IsPending = false;
|
tile.IsPending = false;
|
||||||
|
|
||||||
|
progress?.Report((double)(tileCount - tileStack.Count) / tileCount);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LoadTileImage(tile, tileSource, cacheName).ConfigureAwait(false);
|
await LoadTileImage(tile, tileSource, cacheName, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger?.LogError(ex, "Failed loading tile image {zoom}/{column}/{row}", tile.ZoomLevel, tile.Column, tile.Row);
|
Logger?.LogError(ex, "Failed loading tile image {zoom}/{column}/{row}", tile.ZoomLevel, tile.Column, tile.Row);
|
||||||
}
|
}
|
||||||
|
|
||||||
progress?.Report((double)(tileCount - tileStack.Count) / tileCount);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,14 +146,14 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task LoadTileImage(Tile tile, TileSource tileSource, string cacheName)
|
private static async Task LoadTileImage(Tile tile, TileSource tileSource, string cacheName, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Pass tileSource.LoadImageAsync calls to platform-specific method
|
// Pass tileSource.LoadImageAsync calls to platform-specific method
|
||||||
// LoadTileImage(Tile, Func<Task<ImageSource>>) for execution on the UI thread in WinUI and UWP.
|
// LoadTileImage(Tile, Func<Task<ImageSource>>) for execution on the UI thread in WinUI and UWP.
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(cacheName))
|
if (string.IsNullOrEmpty(cacheName))
|
||||||
{
|
{
|
||||||
Task<ImageSource> LoadImage() => tileSource.LoadImageAsync(tile.Column, tile.Row, tile.ZoomLevel);
|
Task<ImageSource> LoadImage() => tileSource.LoadImageAsync(tile.Column, tile.Row, tile.ZoomLevel, cancellationToken);
|
||||||
|
|
||||||
await LoadTileImage(tile, LoadImage).ConfigureAwait(false);
|
await LoadTileImage(tile, LoadImage).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +163,7 @@ namespace MapControl
|
||||||
|
|
||||||
if (uri != null)
|
if (uri != null)
|
||||||
{
|
{
|
||||||
var buffer = await LoadCachedBuffer(tile, uri, cacheName).ConfigureAwait(false);
|
var buffer = await LoadCachedBuffer(tile, uri, cacheName, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (buffer != null && buffer.Length > 0)
|
if (buffer != null && buffer.Length > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -160,7 +175,7 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<byte[]> LoadCachedBuffer(Tile tile, Uri uri, string cacheName)
|
private static async Task<byte[]> LoadCachedBuffer(Tile tile, Uri uri, string cacheName, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
byte[] buffer = null;
|
byte[] buffer = null;
|
||||||
|
|
||||||
|
|
@ -175,7 +190,7 @@ namespace MapControl
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
buffer = await Cache.GetAsync(cacheKey).ConfigureAwait(false);
|
buffer = await Cache.GetAsync(cacheKey, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -184,7 +199,7 @@ namespace MapControl
|
||||||
|
|
||||||
if (buffer == null)
|
if (buffer == null)
|
||||||
{
|
{
|
||||||
var response = await ImageLoader.GetHttpResponseAsync(uri).ConfigureAwait(false);
|
var response = await ImageLoader.GetHttpResponseAsync(uri, null, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
|
|
@ -201,7 +216,7 @@ namespace MapControl
|
||||||
: response.MaxAge.Value
|
: response.MaxAge.Value
|
||||||
};
|
};
|
||||||
|
|
||||||
await Cache.SetAsync(cacheKey, buffer, options).ConfigureAwait(false);
|
await Cache.SetAsync(cacheKey, buffer, options, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
@ -73,11 +74,11 @@ namespace MapControl
|
||||||
/// Loads a tile ImageSource asynchronously from GetUri(column, row, zoomLevel).
|
/// Loads a tile ImageSource asynchronously from GetUri(column, row, zoomLevel).
|
||||||
/// This method is called by TileImageLoader when caching is disabled.
|
/// This method is called by TileImageLoader when caching is disabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual Task<ImageSource> LoadImageAsync(int column, int row, int zoomLevel)
|
public virtual Task<ImageSource> LoadImageAsync(int column, int row, int zoomLevel, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var uri = GetUri(column, row, zoomLevel);
|
var uri = GetUri(column, row, zoomLevel);
|
||||||
|
|
||||||
return uri != null ? ImageLoader.LoadImageAsync(uri) : Task.FromResult((ImageSource)null);
|
return uri != null ? ImageLoader.LoadImageAsync(uri, null, cancellationToken) : Task.FromResult((ImageSource)null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
#if WPF
|
#if WPF
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
@ -162,7 +164,7 @@ namespace MapControl
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads an ImageSource from the URL returned by GetMapRequestUri().
|
/// Loads an ImageSource from the URL returned by GetMapRequestUri().
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override async Task<ImageSource> GetImageAsync(BoundingBox boundingBox, IProgress<double> progress)
|
protected override async Task<ImageSource> GetImageAsync(BoundingBox boundingBox, IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ImageSource image = null;
|
ImageSource image = null;
|
||||||
|
|
||||||
|
|
@ -185,7 +187,7 @@ namespace MapControl
|
||||||
|
|
||||||
if (uri != null)
|
if (uri != null)
|
||||||
{
|
{
|
||||||
image = await ImageLoader.LoadImageAsync(new Uri(uri), progress);
|
image = await ImageLoader.LoadImageAsync(new Uri(uri), progress, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -208,7 +210,7 @@ namespace MapControl
|
||||||
|
|
||||||
if (uri1 != null && uri2 != null)
|
if (uri1 != null && uri2 != null)
|
||||||
{
|
{
|
||||||
image = await ImageLoader.LoadMergedImageAsync(new Uri(uri1), new Uri(uri2), progress);
|
image = await ImageLoader.LoadMergedImageAsync(new Uri(uri1), new Uri(uri2), progress, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
Children.Clear();
|
Children.Clear();
|
||||||
|
|
||||||
await LoadTilesAsync(null, null); // stop TileImageLoader
|
CancelLoadTilesAsync();
|
||||||
}
|
}
|
||||||
else if (UpdateChildLayers(tileMatrixSet))
|
else if (UpdateChildLayers(tileMatrixSet))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
@ -45,17 +46,18 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task<ImageSource> LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress<double> progress)
|
internal static async Task<ImageSource> LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
WriteableBitmap mergedBitmap = null;
|
WriteableBitmap mergedBitmap = null;
|
||||||
var p1 = 0d;
|
var p1 = 0d;
|
||||||
var p2 = 0d;
|
var p2 = 0d;
|
||||||
|
|
||||||
var images = await Task.WhenAll(
|
var images = await Task.WhenAll(
|
||||||
LoadImageAsync(uri1, new Progress<double>(p => { p1 = p; progress.Report((p1 + p2) / 2d); })),
|
LoadImageAsync(uri1, new Progress<double>(p => { p1 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken),
|
||||||
LoadImageAsync(uri2, new Progress<double>(p => { p2 = p; progress.Report((p1 + p2) / 2d); })));
|
LoadImageAsync(uri2, new Progress<double>(p => { p2 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken));
|
||||||
|
|
||||||
if (images.Length == 2 &&
|
if (!cancellationToken.IsCancellationRequested &&
|
||||||
|
images.Length == 2 &&
|
||||||
images[0] is BitmapSource bitmap1 &&
|
images[0] is BitmapSource bitmap1 &&
|
||||||
images[1] is BitmapSource bitmap2 &&
|
images[1] is BitmapSource bitmap2 &&
|
||||||
bitmap1.PixelHeight == bitmap2.PixelHeight &&
|
bitmap1.PixelHeight == bitmap2.PixelHeight &&
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices.WindowsRuntime;
|
using System.Runtime.InteropServices.WindowsRuntime;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Windows.Graphics.Imaging;
|
using Windows.Graphics.Imaging;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
|
|
@ -66,7 +67,7 @@ namespace MapControl
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task<WriteableBitmap> LoadWriteableBitmapAsync(Uri uri, IProgress<double> progress)
|
internal static async Task<WriteableBitmap> LoadWriteableBitmapAsync(Uri uri, IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
WriteableBitmap bitmap = null;
|
WriteableBitmap bitmap = null;
|
||||||
|
|
||||||
|
|
@ -74,7 +75,7 @@ namespace MapControl
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await GetHttpResponseAsync(uri, progress);
|
var response = await GetHttpResponseAsync(uri, progress, cancellationToken);
|
||||||
|
|
||||||
if (response?.Buffer != null)
|
if (response?.Buffer != null)
|
||||||
{
|
{
|
||||||
|
|
@ -97,17 +98,18 @@ namespace MapControl
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task<ImageSource> LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress<double> progress)
|
internal static async Task<ImageSource> LoadMergedImageAsync(Uri uri1, Uri uri2, IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
WriteableBitmap mergedBitmap = null;
|
WriteableBitmap mergedBitmap = null;
|
||||||
var p1 = 0d;
|
var p1 = 0d;
|
||||||
var p2 = 0d;
|
var p2 = 0d;
|
||||||
|
|
||||||
var bitmaps = await Task.WhenAll(
|
var bitmaps = await Task.WhenAll(
|
||||||
LoadWriteableBitmapAsync(uri1, new Progress<double>(p => { p1 = p; progress.Report((p1 + p2) / 2d); })),
|
LoadWriteableBitmapAsync(uri1, new Progress<double>(p => { p1 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken),
|
||||||
LoadWriteableBitmapAsync(uri2, new Progress<double>(p => { p2 = p; progress.Report((p1 + p2) / 2d); })));
|
LoadWriteableBitmapAsync(uri2, new Progress<double>(p => { p2 = p; progress.Report((p1 + p2) / 2d); }), cancellationToken));
|
||||||
|
|
||||||
if (bitmaps.Length == 2 &&
|
if (!cancellationToken.IsCancellationRequested &&
|
||||||
|
bitmaps.Length == 2 &&
|
||||||
bitmaps[0] != null &&
|
bitmaps[0] != null &&
|
||||||
bitmaps[1] != null &&
|
bitmaps[1] != null &&
|
||||||
bitmaps[0].PixelHeight == bitmaps[1].PixelHeight)
|
bitmaps[0].PixelHeight == bitmaps[1].PixelHeight)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue