mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-01-20 23:50:17 +01:00
Version 3.2. Improved TileImageLoader.
This commit is contained in:
parent
2ed22e59b2
commit
6938c8251d
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
|
@ -11,43 +10,49 @@ using System.Windows.Media.Imaging;
|
|||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates frozen BitmapSources from Stream or Uri.
|
||||
/// Creates frozen BitmapSources from Stream, file or Uri.
|
||||
/// </summary>
|
||||
public static class BitmapSourceHelper
|
||||
{
|
||||
public static BitmapSource FromStream(Stream stream)
|
||||
{
|
||||
var bitmap = new BitmapImage();
|
||||
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
}
|
||||
|
||||
bitmap.BeginInit();
|
||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmap.StreamSource = stream;
|
||||
bitmap.EndInit();
|
||||
bitmap.Freeze();
|
||||
public static BitmapSource FromFile(string path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
return FromStream(fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
public static BitmapSource FromUri(Uri uri)
|
||||
{
|
||||
try
|
||||
if (!uri.IsAbsoluteUri)
|
||||
{
|
||||
using (var response = WebRequest.Create(uri).GetResponse())
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
responseStream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return FromStream(memoryStream);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
return FromFile(uri.OriginalString);
|
||||
}
|
||||
|
||||
return null;
|
||||
if (uri.Scheme == "file")
|
||||
{
|
||||
return FromFile(uri.LocalPath);
|
||||
}
|
||||
|
||||
using (var response = WebRequest.Create(uri).GetResponse())
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
responseStream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return FromStream(memoryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,40 +3,22 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the image of a map tile. ImageTileSource bypasses image downloading
|
||||
/// and optional caching in TileImageLoader. By overriding the LoadImage method,
|
||||
/// an application can provide tile images from an arbitrary source.
|
||||
/// If the IsAsync property is true, LoadImage will be called from a separate,
|
||||
/// non-UI thread and must therefore return a frozen ImageSource.
|
||||
/// Provides the image of a map tile.
|
||||
/// ImageTileSource bypasses image downloading and optional caching in TileImageLoader.
|
||||
/// By overriding the LoadImage method, an application can provide tile images from an arbitrary source.
|
||||
/// LoadImage will be called from a non-UI thread and must therefore return a frozen ImageSource.
|
||||
/// </summary>
|
||||
public class ImageTileSource : TileSource
|
||||
{
|
||||
public bool IsAsync { get; set; }
|
||||
|
||||
public virtual ImageSource LoadImage(int x, int y, int zoomLevel)
|
||||
{
|
||||
ImageSource image = null;
|
||||
|
||||
var uri = GetUri(x, y, zoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
if (IsAsync)
|
||||
{
|
||||
image = BitmapSourceHelper.FromUri(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = new BitmapImage(uri);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
return uri != null ? BitmapSourceHelper.FromUri(uri) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
|
@ -16,7 +17,17 @@ namespace MapControl
|
|||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var image = BitmapSourceHelper.FromUri(uri);
|
||||
BitmapSource image = null;
|
||||
|
||||
try
|
||||
{
|
||||
image = BitmapSourceHelper.FromUri(uri);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", uri, ex.Message);
|
||||
}
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() => UpdateImage(image)));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Relative size of the map image in relation to the current viewport size.
|
||||
/// Setting a value greater than one will let ImageLayer request images that
|
||||
/// Setting a value greater than one will let MapImageLayer request images that
|
||||
/// are larger than the viewport, in order to support smooth panning.
|
||||
/// </summary>
|
||||
public double RelativeImageSize
|
||||
|
|
@ -151,7 +151,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Description of the ImageLayer.
|
||||
/// Description of the MapImageLayer.
|
||||
/// Used to display copyright information on top of the map.
|
||||
/// </summary>
|
||||
public string Description
|
||||
|
|
@ -162,7 +162,7 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Optional foreground brush.
|
||||
/// Sets MapBase.Foreground if not null and the ImageLayer is the base map layer.
|
||||
/// Sets MapBase.Foreground if not null and the MapImageLayer is the base map layer.
|
||||
/// </summary>
|
||||
public Brush MapForeground
|
||||
{
|
||||
|
|
@ -172,7 +172,7 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Optional background brush.
|
||||
/// Sets MapBase.Background if not null and the ImageLayer is the base map layer.
|
||||
/// Sets MapBase.Background if not null and the MapImageLayer is the base map layer.
|
||||
/// </summary>
|
||||
public Brush MapBackground
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ namespace MapControl
|
|||
{
|
||||
public interface ITileImageLoader
|
||||
{
|
||||
void BeginLoadTiles(MapTileLayer tileLayer, IEnumerable<Tile> tiles);
|
||||
void CancelLoadTiles(MapTileLayer tileLayer);
|
||||
void LoadTiles(MapTileLayer tileLayer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -38,7 +37,7 @@ namespace MapControl
|
|||
public partial class MapTileLayer : Panel, IMapLayer
|
||||
{
|
||||
/// <summary>
|
||||
/// A default TileLayer using OpenStreetMap data.
|
||||
/// A default MapTileLayer using OpenStreetMap data.
|
||||
/// </summary>
|
||||
public static MapTileLayer OpenStreetMapTileLayer
|
||||
{
|
||||
|
|
@ -56,7 +55,7 @@ namespace MapControl
|
|||
|
||||
public static readonly DependencyProperty TileSourceProperty = DependencyProperty.Register(
|
||||
nameof(TileSource), typeof(TileSource), typeof(MapTileLayer),
|
||||
new PropertyMetadata(null, (o, e) => ((MapTileLayer)o).UpdateTiles(true)));
|
||||
new PropertyMetadata(null, (o, e) => ((MapTileLayer)o).ResetTiles()));
|
||||
|
||||
public static readonly DependencyProperty SourceNameProperty = DependencyProperty.Register(
|
||||
nameof(SourceName), typeof(string), typeof(MapTileLayer), new PropertyMetadata(null));
|
||||
|
|
@ -84,9 +83,6 @@ namespace MapControl
|
|||
public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register(
|
||||
nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapTileLayer), new PropertyMetadata(true));
|
||||
|
||||
public static readonly DependencyProperty LoadTilesDescendingProperty = DependencyProperty.Register(
|
||||
nameof(LoadTilesDescending), typeof(bool), typeof(MapTileLayer), new PropertyMetadata(false));
|
||||
|
||||
public static readonly DependencyProperty MapBackgroundProperty = DependencyProperty.Register(
|
||||
nameof(MapBackground), typeof(Brush), typeof(MapTileLayer), new PropertyMetadata(null));
|
||||
|
||||
|
|
@ -138,7 +134,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Description of the TileLayer.
|
||||
/// Description of the MapTileLayer.
|
||||
/// Used to display copyright information on top of the map.
|
||||
/// </summary>
|
||||
public string Description
|
||||
|
|
@ -148,7 +144,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an offset to the Map's ZoomLevel for a relative scale between the Map and the TileLayer.
|
||||
/// Adds an offset to the Map's ZoomLevel for a relative scale between the Map and the MapTileLayer.
|
||||
/// </summary>
|
||||
public double ZoomLevelOffset
|
||||
{
|
||||
|
|
@ -157,7 +153,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum zoom level supported by the TileLayer.
|
||||
/// Minimum zoom level supported by the MapTileLayer.
|
||||
/// </summary>
|
||||
public int MinZoomLevel
|
||||
{
|
||||
|
|
@ -166,7 +162,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum zoom level supported by the TileLayer.
|
||||
/// Maximum zoom level supported by the MapTileLayer.
|
||||
/// </summary>
|
||||
public int MaxZoomLevel
|
||||
{
|
||||
|
|
@ -175,7 +171,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of parallel downloads that may be performed by the TileLayer's ITileImageLoader.
|
||||
/// Maximum number of parallel downloads that may be performed by the MapTileLayer's ITileImageLoader.
|
||||
/// </summary>
|
||||
public int MaxParallelDownloads
|
||||
{
|
||||
|
|
@ -201,19 +197,9 @@ namespace MapControl
|
|||
set { SetValue(UpdateWhileViewportChangingProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controls the order of zoom levels in which map tiles are loaded.
|
||||
/// The default is value is false, i.e. tiles are loaded in ascending order.
|
||||
/// </summary>
|
||||
public bool LoadTilesDescending
|
||||
{
|
||||
get { return (bool)GetValue(LoadTilesDescendingProperty); }
|
||||
set { SetValue(LoadTilesDescendingProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optional background brush.
|
||||
/// Sets MapBase.Background if not null and the TileLayer is the base map layer.
|
||||
/// Sets MapBase.Background if not null and the MapTileLayer is the base map layer.
|
||||
/// </summary>
|
||||
public Brush MapBackground
|
||||
{
|
||||
|
|
@ -223,7 +209,7 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Optional foreground brush.
|
||||
/// Sets MapBase.Foreground if not null and the TileLayer is the base map layer.
|
||||
/// Sets MapBase.Foreground if not null and the MapTileLayer is the base map layer.
|
||||
/// </summary>
|
||||
public Brush MapForeground
|
||||
{
|
||||
|
|
@ -295,13 +281,13 @@ namespace MapControl
|
|||
{
|
||||
TileGrid = tileGrid;
|
||||
SetRenderTransform();
|
||||
UpdateTiles(false);
|
||||
UpdateTiles();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TileGrid = null;
|
||||
UpdateTiles(true);
|
||||
ResetTiles();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -368,38 +354,24 @@ namespace MapControl
|
|||
MatrixEx.TranslateScaleRotateTranslate(tileOrigin, scale, parentMap.Heading, viewCenter);
|
||||
}
|
||||
|
||||
private void UpdateTiles(bool clearTiles)
|
||||
private void ResetTiles()
|
||||
{
|
||||
if (Tiles.Count > 0)
|
||||
{
|
||||
TileImageLoader.CancelLoadTiles(this);
|
||||
}
|
||||
|
||||
if (clearTiles)
|
||||
{
|
||||
Tiles.Clear();
|
||||
}
|
||||
Tiles.Clear();
|
||||
UpdateTiles();
|
||||
}
|
||||
|
||||
private void UpdateTiles()
|
||||
{
|
||||
SelectTiles();
|
||||
|
||||
Children.Clear();
|
||||
|
||||
if (Tiles.Count > 0)
|
||||
foreach (var tile in Tiles)
|
||||
{
|
||||
foreach (var tile in Tiles)
|
||||
{
|
||||
Children.Add(tile.Image);
|
||||
}
|
||||
|
||||
var pendingTiles = Tiles.Where(t => t.Pending);
|
||||
|
||||
if (LoadTilesDescending)
|
||||
{
|
||||
pendingTiles = pendingTiles.OrderByDescending(t => t.ZoomLevel); // higher zoom levels first
|
||||
}
|
||||
|
||||
TileImageLoader.BeginLoadTiles(this, pendingTiles);
|
||||
Children.Add(tile.Image);
|
||||
}
|
||||
|
||||
TileImageLoader.LoadTiles(this);
|
||||
}
|
||||
|
||||
private void SelectTiles()
|
||||
|
|
@ -409,13 +381,7 @@ namespace MapControl
|
|||
if (parentMap != null && TileGrid != null && TileSource != null)
|
||||
{
|
||||
var maxZoomLevel = Math.Min(TileGrid.ZoomLevel, MaxZoomLevel);
|
||||
var minZoomLevel = MinZoomLevel;
|
||||
|
||||
if (minZoomLevel < maxZoomLevel && this != parentMap.Children.Cast<UIElement>().FirstOrDefault())
|
||||
{
|
||||
// do not load background tiles if this is not the base layer
|
||||
minZoomLevel = maxZoomLevel;
|
||||
}
|
||||
var minZoomLevel = parentMap.MapLayer == this ? MinZoomLevel : maxZoomLevel; // load background tiles only if this is the base layer
|
||||
|
||||
for (var z = minZoomLevel; z <= maxZoomLevel; z++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used in TileLayer.
|
||||
/// Used in MapTileLayer.
|
||||
/// </summary>
|
||||
public static Matrix TranslateScaleRotateTranslate(
|
||||
Point translation1, double scale, double rotationAngle, Point translation2)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ using System.Windows;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -25,30 +25,27 @@ namespace MapControl
|
|||
{
|
||||
Pending = false;
|
||||
|
||||
if (image != null)
|
||||
if (fadeIn && FadeDuration > TimeSpan.Zero)
|
||||
{
|
||||
if (fadeIn && FadeDuration > TimeSpan.Zero)
|
||||
{
|
||||
BitmapImage bitmap;
|
||||
BitmapImage bitmap;
|
||||
|
||||
if (isDownloading && (bitmap = image as BitmapImage) != null)
|
||||
{
|
||||
bitmap.ImageOpened += BitmapImageOpened;
|
||||
bitmap.ImageFailed += BitmapImageFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.BeginAnimation(UIElement.OpacityProperty,
|
||||
new DoubleAnimation { From = 0d, To = 1d, Duration = FadeDuration });
|
||||
}
|
||||
if (isDownloading && (bitmap = image as BitmapImage) != null)
|
||||
{
|
||||
bitmap.ImageOpened += BitmapImageOpened;
|
||||
bitmap.ImageFailed += BitmapImageFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.Opacity = 1d;
|
||||
Image.BeginAnimation(UIElement.OpacityProperty,
|
||||
new DoubleAnimation { From = 0d, To = 1d, Duration = FadeDuration });
|
||||
}
|
||||
|
||||
Image.Source = image;
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.Opacity = 1d;
|
||||
}
|
||||
|
||||
Image.Source = image;
|
||||
}
|
||||
|
||||
private void BitmapImageOpened(object sender, RoutedEventArgs e)
|
||||
|
|
|
|||
|
|
@ -17,30 +17,39 @@ namespace MapControl
|
|||
{
|
||||
Pending = false;
|
||||
|
||||
if (image != null)
|
||||
if (Image.Dispatcher.CheckAccess())
|
||||
{
|
||||
if (fadeIn && FadeDuration > TimeSpan.Zero)
|
||||
{
|
||||
var bitmap = image as BitmapSource;
|
||||
SetImageSource(image, fadeIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.Dispatcher.BeginInvoke(new Action(() => SetImageSource(image, fadeIn)));
|
||||
}
|
||||
}
|
||||
|
||||
if (bitmap != null && !bitmap.IsFrozen && bitmap.IsDownloading)
|
||||
{
|
||||
bitmap.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed += BitmapDownloadFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.BeginAnimation(UIElement.OpacityProperty,
|
||||
new DoubleAnimation(0d, 1d, FadeDuration));
|
||||
}
|
||||
private void SetImageSource(ImageSource image, bool fadeIn)
|
||||
{
|
||||
if (fadeIn && FadeDuration > TimeSpan.Zero)
|
||||
{
|
||||
var bitmap = image as BitmapSource;
|
||||
|
||||
if (bitmap != null && !bitmap.IsFrozen && bitmap.IsDownloading)
|
||||
{
|
||||
bitmap.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed += BitmapDownloadFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.Opacity = 1d;
|
||||
Image.BeginAnimation(UIElement.OpacityProperty,
|
||||
new DoubleAnimation(0d, 1d, FadeDuration));
|
||||
}
|
||||
|
||||
Image.Source = image;
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.Opacity = 1d;
|
||||
}
|
||||
|
||||
Image.Source = image;
|
||||
}
|
||||
|
||||
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace MapControl
|
|||
{
|
||||
public partial class Tile
|
||||
{
|
||||
public static TimeSpan FadeDuration { get; set; } = TimeSpan.FromSeconds(0.2);
|
||||
public static TimeSpan FadeDuration { get; set; } = TimeSpan.FromSeconds(0.1);
|
||||
|
||||
public readonly int ZoomLevel;
|
||||
public readonly int X;
|
||||
|
|
@ -25,10 +25,9 @@ namespace MapControl
|
|||
ZoomLevel = zoomLevel;
|
||||
X = x;
|
||||
Y = y;
|
||||
Pending = true;
|
||||
}
|
||||
|
||||
public bool Pending { get; private set; }
|
||||
public bool Pending { get; set; } = true;
|
||||
|
||||
public int XIndex
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
|
@ -15,41 +15,39 @@ namespace MapControl
|
|||
/// </summary>
|
||||
internal class TileImageLoader : ITileImageLoader
|
||||
{
|
||||
public void BeginLoadTiles(MapTileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||
public void LoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||
var tileSource = tileLayer.TileSource;
|
||||
var imageTileSource = tileSource as ImageTileSource;
|
||||
|
||||
foreach (var tile in tiles)
|
||||
foreach (var tile in tileLayer.Tiles.Where(t => t.Pending))
|
||||
{
|
||||
tile.Pending = false;
|
||||
|
||||
try
|
||||
{
|
||||
ImageSource image = null;
|
||||
Uri uri;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
else
|
||||
else if ((uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel)) != null)
|
||||
{
|
||||
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
image = new BitmapImage(uri);
|
||||
}
|
||||
image = new BitmapImage(uri);
|
||||
}
|
||||
|
||||
tile.SetImage(image);
|
||||
if (image != null)
|
||||
{
|
||||
tile.SetImage(image);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Loading tile image failed: {0}", ex.Message);
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelLoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -63,195 +62,99 @@ namespace MapControl
|
|||
Cache = MemoryCache.Default;
|
||||
}
|
||||
|
||||
private class PendingTile
|
||||
{
|
||||
public readonly Tile Tile;
|
||||
public readonly ImageSource CachedImage;
|
||||
|
||||
public PendingTile(Tile tile, ImageSource cachedImage)
|
||||
{
|
||||
Tile = tile;
|
||||
CachedImage = cachedImage;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ConcurrentQueue<PendingTile> pendingTiles = new ConcurrentQueue<PendingTile>();
|
||||
private readonly ConcurrentStack<Tile> pendingTiles = new ConcurrentStack<Tile>();
|
||||
private int taskCount;
|
||||
|
||||
public void BeginLoadTiles(MapTileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||
public void LoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
if (tiles.Any())
|
||||
pendingTiles.Clear();
|
||||
|
||||
var tileStack = tileLayer.Tiles.Where(t => t.Pending).Reverse().ToArray();
|
||||
|
||||
if (tileStack.Length > 0)
|
||||
{
|
||||
// get current TileLayer property values in UI thread
|
||||
var dispatcher = tileLayer.Dispatcher;
|
||||
pendingTiles.PushRange(tileStack);
|
||||
|
||||
var tileSource = tileLayer.TileSource;
|
||||
var imageTileSource = tileSource as ImageTileSource;
|
||||
var sourceName = tileLayer.SourceName;
|
||||
var maxDownloads = tileLayer.MaxParallelDownloads;
|
||||
|
||||
if (imageTileSource != null && !imageTileSource.IsAsync) // call LoadImage in UI thread with low priority
|
||||
while (taskCount < Math.Min(pendingTiles.Count, maxDownloads))
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
Interlocked.Increment(ref taskCount);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
dispatcher.BeginInvoke(new Action<Tile>(t => t.SetImage(LoadImage(imageTileSource, t))), DispatcherPriority.Background, tile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var tileList = tiles.ToList(); // evaluate immediately
|
||||
var sourceName = tileLayer.SourceName;
|
||||
var maxDownloads = tileLayer.MaxParallelDownloads;
|
||||
LoadPendingTiles(tileSource, sourceName);
|
||||
|
||||
Task.Run(() => GetTiles(tileList, dispatcher, tileSource, sourceName, maxDownloads));
|
||||
Interlocked.Decrement(ref taskCount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelLoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
PendingTile pendingTile;
|
||||
|
||||
while (pendingTiles.TryDequeue(out pendingTile)) ; // no Clear method
|
||||
}
|
||||
|
||||
private void GetTiles(IEnumerable<Tile> tiles, Dispatcher dispatcher, TileSource tileSource, string sourceName, int maxDownloads)
|
||||
{
|
||||
var useCache = Cache != null
|
||||
&& !string.IsNullOrEmpty(sourceName)
|
||||
&& !(tileSource is ImageTileSource)
|
||||
&& !tileSource.UriFormat.StartsWith("file:");
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
ImageSource cachedImage = null;
|
||||
|
||||
if (useCache && GetCachedImage(CacheKey(sourceName, tile), out cachedImage))
|
||||
{
|
||||
dispatcher.BeginInvoke(new Action<Tile, ImageSource>((t, i) => t.SetImage(i)), tile, cachedImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingTiles.Enqueue(new PendingTile(tile, cachedImage));
|
||||
}
|
||||
}
|
||||
|
||||
var newTaskCount = Math.Min(pendingTiles.Count, maxDownloads) - taskCount;
|
||||
|
||||
while (newTaskCount-- > 0)
|
||||
{
|
||||
Interlocked.Increment(ref taskCount);
|
||||
|
||||
Task.Run(() => LoadPendingTiles(dispatcher, tileSource, sourceName));
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadPendingTiles(Dispatcher dispatcher, TileSource tileSource, string sourceName)
|
||||
private void LoadPendingTiles(TileSource tileSource, string sourceName)
|
||||
{
|
||||
var imageTileSource = tileSource as ImageTileSource;
|
||||
PendingTile pendingTile;
|
||||
Tile tile;
|
||||
|
||||
while (pendingTiles.TryDequeue(out pendingTile))
|
||||
while (pendingTiles.TryPop(out tile))
|
||||
{
|
||||
var tile = pendingTile.Tile;
|
||||
ImageSource image = null;
|
||||
tile.Pending = false;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
image = LoadImage(imageTileSource, tile);
|
||||
}
|
||||
else
|
||||
{
|
||||
var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
if (!uri.IsAbsoluteUri)
|
||||
{
|
||||
image = LoadImage(uri.OriginalString);
|
||||
}
|
||||
else if (uri.Scheme == "file")
|
||||
{
|
||||
image = LoadImage(uri.LocalPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = DownloadImage(uri, CacheKey(sourceName, tile))
|
||||
?? pendingTile.CachedImage; // use possibly cached image if download failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispatcher.BeginInvoke(new Action<Tile, ImageSource>((t, i) => t.SetImage(i)), tile, image);
|
||||
}
|
||||
|
||||
Interlocked.Decrement(ref taskCount);
|
||||
}
|
||||
|
||||
private static ImageSource LoadImage(ImageTileSource tileSource, Tile tile)
|
||||
{
|
||||
ImageSource image = null;
|
||||
|
||||
try
|
||||
{
|
||||
image = tileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static ImageSource LoadImage(string path)
|
||||
{
|
||||
ImageSource image = null;
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
ImageSource image = null;
|
||||
Uri uri;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
image = BitmapSourceHelper.FromStream(fileStream);
|
||||
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
else if ((uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel)) != null)
|
||||
{
|
||||
image = LoadImage(uri, sourceName, tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
tile.SetImage(image);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", path, ex.Message);
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static ImageSource DownloadImage(Uri uri, string cacheKey)
|
||||
private ImageSource LoadImage(Uri uri, string sourceName, int x, int y, int zoomLevel)
|
||||
{
|
||||
ImageSource image = null;
|
||||
|
||||
try
|
||||
{
|
||||
var request = WebRequest.CreateHttp(uri);
|
||||
|
||||
if (HttpUserAgent != null)
|
||||
if (!uri.IsAbsoluteUri)
|
||||
{
|
||||
request.UserAgent = HttpUserAgent;
|
||||
image = BitmapSourceHelper.FromFile(uri.OriginalString);
|
||||
}
|
||||
|
||||
using (var response = (HttpWebResponse)request.GetResponse())
|
||||
else if (uri.Scheme == "file")
|
||||
{
|
||||
if (response.Headers["X-VE-Tile-Info"] != "no-tile") // set by Bing Maps
|
||||
{
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
responseStream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
image = BitmapSourceHelper.FromStream(memoryStream);
|
||||
image = BitmapSourceHelper.FromFile(uri.LocalPath);
|
||||
}
|
||||
else if (Cache == null || string.IsNullOrEmpty(sourceName))
|
||||
{
|
||||
image = DownloadImage(uri, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cacheKey = string.Format("{0}/{1}/{2}/{3}", sourceName, zoomLevel, x, y);
|
||||
|
||||
if (cacheKey != null)
|
||||
{
|
||||
SetCachedImage(cacheKey, memoryStream, GetExpiration(response.Headers));
|
||||
}
|
||||
}
|
||||
if (!GetCachedImage(cacheKey, ref image))
|
||||
{
|
||||
// Either no cached image was found or expiration time has expired.
|
||||
// If download fails use possibly cached but expired image anyway.
|
||||
image = DownloadImage(uri, cacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,15 +170,41 @@ namespace MapControl
|
|||
return image;
|
||||
}
|
||||
|
||||
private static string CacheKey(string sourceName, Tile tile)
|
||||
private static ImageSource DownloadImage(Uri uri, string cacheKey)
|
||||
{
|
||||
return string.IsNullOrEmpty(sourceName) ? null : string.Format("{0}/{1}/{2}/{3}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y);
|
||||
ImageSource image = null;
|
||||
var request = WebRequest.CreateHttp(uri);
|
||||
|
||||
if (HttpUserAgent != null)
|
||||
{
|
||||
request.UserAgent = HttpUserAgent;
|
||||
}
|
||||
|
||||
using (var response = (HttpWebResponse)request.GetResponse())
|
||||
{
|
||||
if (response.Headers["X-VE-Tile-Info"] != "no-tile") // set by Bing Maps
|
||||
{
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
responseStream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
image = BitmapSourceHelper.FromStream(memoryStream);
|
||||
|
||||
if (cacheKey != null)
|
||||
{
|
||||
SetCachedImage(cacheKey, memoryStream, GetExpiration(response.Headers));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static bool GetCachedImage(string cacheKey, out ImageSource image)
|
||||
private static bool GetCachedImage(string cacheKey, ref ImageSource image)
|
||||
{
|
||||
image = null;
|
||||
|
||||
var result = false;
|
||||
var buffer = Cache.Get(cacheKey) as byte[];
|
||||
|
||||
if (buffer != null)
|
||||
|
|
@ -294,7 +223,7 @@ namespace MapControl
|
|||
expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
return expiration > DateTime.UtcNow;
|
||||
result = expiration > DateTime.UtcNow;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -302,7 +231,7 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void SetCachedImage(string cacheKey, MemoryStream memoryStream, DateTime expiration)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
|
|
@ -56,130 +57,124 @@ namespace MapControl
|
|||
MinimumCacheExpiration = TimeSpan.FromHours(1);
|
||||
}
|
||||
|
||||
private class PendingTile
|
||||
{
|
||||
public readonly Tile Tile;
|
||||
public readonly Uri Uri;
|
||||
|
||||
public PendingTile(Tile tile, Uri uri)
|
||||
{
|
||||
Tile = tile;
|
||||
Uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ConcurrentQueue<PendingTile> pendingTiles = new ConcurrentQueue<PendingTile>();
|
||||
private readonly ConcurrentStack<Tile> pendingTiles = new ConcurrentStack<Tile>();
|
||||
private int taskCount;
|
||||
|
||||
public void BeginLoadTiles(MapTileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||
public void LoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
pendingTiles.Clear();
|
||||
|
||||
var tileSource = tileLayer.TileSource;
|
||||
var imageTileSource = tileSource as ImageTileSource;
|
||||
var tiles = tileLayer.Tiles.Where(t => t.Pending);
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
tile.SetImage(imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
LoadTiles(imageTileSource, tiles);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
var tileStack = tiles.Reverse().ToArray();
|
||||
|
||||
if (tileStack.Length > 0)
|
||||
{
|
||||
Uri uri = null;
|
||||
pendingTiles.PushRange(tileStack);
|
||||
|
||||
try
|
||||
{
|
||||
uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
var sourceName = tileLayer.SourceName;
|
||||
var maxDownloads = tileLayer.MaxParallelDownloads;
|
||||
|
||||
if (uri == null)
|
||||
while (taskCount < Math.Min(pendingTiles.Count, maxDownloads))
|
||||
{
|
||||
tile.SetImage(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingTiles.Enqueue(new PendingTile(tile, uri));
|
||||
Interlocked.Increment(ref taskCount);
|
||||
|
||||
var newTaskCount = Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads) - taskCount;
|
||||
var sourceName = tileLayer.SourceName;
|
||||
|
||||
while (newTaskCount-- > 0)
|
||||
Task.Run(async () =>
|
||||
{
|
||||
Interlocked.Increment(ref taskCount);
|
||||
await LoadPendingTiles(tileSource, sourceName);
|
||||
|
||||
Task.Run(() => LoadPendingTiles(tileSource, sourceName)); // Task.Run(Func<Task>)
|
||||
}
|
||||
Interlocked.Decrement(ref taskCount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelLoadTiles(MapTileLayer tileLayer)
|
||||
private void LoadTiles(ImageTileSource tileSource, IEnumerable<Tile> tiles)
|
||||
{
|
||||
PendingTile pendingTile;
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
tile.Pending = false;
|
||||
|
||||
while (pendingTiles.TryDequeue(out pendingTile)) ; // no Clear method
|
||||
try
|
||||
{
|
||||
var image = tileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
tile.SetImage(image);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadPendingTiles(TileSource tileSource, string sourceName)
|
||||
{
|
||||
PendingTile pendingTile;
|
||||
Tile tile;
|
||||
|
||||
while (pendingTiles.TryDequeue(out pendingTile))
|
||||
while (pendingTiles.TryPop(out tile))
|
||||
{
|
||||
var tile = pendingTile.Tile;
|
||||
var uri = pendingTile.Uri;
|
||||
tile.Pending = false;
|
||||
|
||||
if (Cache == null || sourceName == null)
|
||||
try
|
||||
{
|
||||
await DownloadImage(tile, uri, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var extension = Path.GetExtension(uri.LocalPath);
|
||||
var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (string.IsNullOrEmpty(extension) || extension == ".jpeg")
|
||||
if (uri != null)
|
||||
{
|
||||
extension = ".jpg";
|
||||
}
|
||||
|
||||
var cacheKey = string.Format(@"{0}\{1}\{2}\{3}{4}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension);
|
||||
var cacheItem = await Cache.GetAsync(cacheKey);
|
||||
var loaded = false;
|
||||
|
||||
if (cacheItem == null || cacheItem.Expiration <= DateTime.UtcNow)
|
||||
{
|
||||
loaded = await DownloadImage(tile, uri, cacheKey);
|
||||
}
|
||||
|
||||
if (!loaded && cacheItem != null && cacheItem.Buffer != null)
|
||||
{
|
||||
using (var stream = new InMemoryRandomAccessStream())
|
||||
if (Cache == null || sourceName == null)
|
||||
{
|
||||
await stream.WriteAsync(cacheItem.Buffer);
|
||||
await stream.FlushAsync();
|
||||
stream.Seek(0);
|
||||
await DownloadImage(tile, uri, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var extension = Path.GetExtension(uri.LocalPath);
|
||||
|
||||
await LoadImageFromStream(tile, stream);
|
||||
if (string.IsNullOrEmpty(extension) || extension == ".jpeg")
|
||||
{
|
||||
extension = ".jpg";
|
||||
}
|
||||
|
||||
var cacheKey = string.Format(@"{0}\{1}\{2}\{3}{4}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension);
|
||||
var cacheItem = await Cache.GetAsync(cacheKey);
|
||||
var loaded = false;
|
||||
|
||||
if (cacheItem == null || cacheItem.Expiration <= DateTime.UtcNow)
|
||||
{
|
||||
loaded = await DownloadImage(tile, uri, cacheKey);
|
||||
}
|
||||
|
||||
if (!loaded && cacheItem != null && cacheItem.Buffer != null)
|
||||
{
|
||||
using (var stream = new InMemoryRandomAccessStream())
|
||||
{
|
||||
await stream.WriteAsync(cacheItem.Buffer);
|
||||
await stream.FlushAsync();
|
||||
stream.Seek(0);
|
||||
|
||||
await LoadImageFromStream(tile, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
Interlocked.Decrement(ref taskCount);
|
||||
}
|
||||
|
||||
private async Task<bool> DownloadImage(Tile tile, Uri uri, string cacheKey)
|
||||
|
|
@ -211,7 +206,6 @@ namespace MapControl
|
|||
|
||||
if (response.Headers.TryGetValue("X-VE-Tile-Info", out tileInfo) && tileInfo == "no-tile") // set by Bing Maps
|
||||
{
|
||||
tile.SetImage(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -255,28 +249,25 @@ namespace MapControl
|
|||
|
||||
private async Task<bool> LoadImageFromStream(Tile tile, IRandomAccessStream stream)
|
||||
{
|
||||
var completion = new TaskCompletionSource<bool>();
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
var action = tile.Image.Dispatcher.RunAsync(
|
||||
CoreDispatcherPriority.Normal,
|
||||
async () =>
|
||||
await tile.Image.Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var image = new BitmapImage();
|
||||
await image.SetSourceAsync(stream);
|
||||
tile.SetImage(image, true, false);
|
||||
completion.SetResult(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
tile.SetImage(null);
|
||||
completion.SetResult(false);
|
||||
}
|
||||
});
|
||||
var image = new BitmapImage();
|
||||
await image.SetSourceAsync(stream);
|
||||
tile.SetImage(image, true, false);
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
});
|
||||
|
||||
return await completion.Task;
|
||||
return await tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the map projection has changed, i.e. if a TileLayer or ImageLayer should be
|
||||
/// Indicates if the map projection has changed, i.e. if a MapTileLayer or MapImageLayer should be
|
||||
/// immediately updated, or MapPath Data in cartesian map coordinates should be recalculated.
|
||||
/// </summary>
|
||||
public bool ProjectionChanged { get; }
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ namespace ViewModel
|
|||
{
|
||||
SourceName = "OpenStreetMap German",
|
||||
Description = "© [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)",
|
||||
TileSource = new TileSource { UriFormat = "http://{c}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png" }
|
||||
TileSource = new TileSource { UriFormat = "http://{c}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png" },
|
||||
MaxZoomLevel = 19
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ namespace UniversalApp
|
|||
|
||||
public MainPage()
|
||||
{
|
||||
//TileImageLoader.Cache = new MapControl.Caching.ImageFileCache();
|
||||
//TileImageLoader.Cache = new MapControl.Caching.FileDbCache();
|
||||
//MapControl.TileImageLoader.Cache = new MapControl.Caching.ImageFileCache();
|
||||
//MapControl.TileImageLoader.Cache = new MapControl.Caching.FileDbCache();
|
||||
|
||||
InitializeComponent();
|
||||
DataContext = ViewModel;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.1.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.0")]
|
||||
[assembly: AssemblyVersion("3.2.0")]
|
||||
[assembly: AssemblyFileVersion("3.2.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue