Version 3.3. Improved WinRT TileImageLoader

This commit is contained in:
ClemensF 2017-07-18 18:20:57 +02:00
parent 8f5a0627fb
commit ace1057669
16 changed files with 110 additions and 95 deletions

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -13,9 +13,9 @@ using System.Windows.Media.Imaging;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <summary>
/// Provides the image of a map tile. ImageTileSource bypasses image /// Provides the image of a map tile.
/// downloading in TileImageLoader. By overriding the LoadImage method, /// ImageTileSource bypasses image downloading and optional caching in TileImageLoader.
/// an application can provide tile images from an arbitrary source. /// By overriding the LoadImage method, an application can provide tile images from an arbitrary source.
/// </summary> /// </summary>
public class ImageTileSource : TileSource public class ImageTileSource : TileSource
{ {

View file

@ -55,7 +55,7 @@ namespace MapControl
public static readonly DependencyProperty TileSourceProperty = DependencyProperty.Register( public static readonly DependencyProperty TileSourceProperty = DependencyProperty.Register(
nameof(TileSource), typeof(TileSource), typeof(MapTileLayer), nameof(TileSource), typeof(TileSource), typeof(MapTileLayer),
new PropertyMetadata(null, (o, e) => ((MapTileLayer)o).ResetTiles())); new PropertyMetadata(null, (o, e) => ((MapTileLayer)o).TileSourcePropertyChanged()));
public static readonly DependencyProperty SourceNameProperty = DependencyProperty.Register( public static readonly DependencyProperty SourceNameProperty = DependencyProperty.Register(
nameof(SourceName), typeof(string), typeof(MapTileLayer), new PropertyMetadata(null)); nameof(SourceName), typeof(string), typeof(MapTileLayer), new PropertyMetadata(null));
@ -125,7 +125,7 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Name of the TileSource. Used as key in a TileLayerCollection and as component of a tile cache key. /// Name of the TileSource. Used as component of a tile cache key.
/// </summary> /// </summary>
public string SourceName public string SourceName
{ {
@ -134,8 +134,7 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Description of the MapTileLayer. /// Description of the MapTileLayer. Used to display copyright information on top of the map.
/// Used to display copyright information on top of the map.
/// </summary> /// </summary>
public string Description public string Description
{ {
@ -198,8 +197,7 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Optional background brush. /// Optional background brush. Sets MapBase.Background if not null and the MapTileLayer is the base map layer.
/// Sets MapBase.Background if not null and the MapTileLayer is the base map layer.
/// </summary> /// </summary>
public Brush MapBackground public Brush MapBackground
{ {
@ -208,8 +206,7 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Optional foreground brush. /// Optional foreground brush. Sets MapBase.Foreground if not null and the MapTileLayer is the base map layer.
/// Sets MapBase.Foreground if not null and the MapTileLayer is the base map layer.
/// </summary> /// </summary>
public Brush MapForeground public Brush MapForeground
{ {
@ -287,7 +284,16 @@ namespace MapControl
else else
{ {
TileGrid = null; TileGrid = null;
ResetTiles(); UpdateTiles();
}
}
private void TileSourcePropertyChanged()
{
if (TileGrid != null)
{
Tiles.Clear();
UpdateTiles();
} }
} }
@ -295,8 +301,7 @@ namespace MapControl
{ {
if (TileGrid == null || e.ProjectionChanged || Math.Abs(e.LongitudeOffset) > 180d) if (TileGrid == null || e.ProjectionChanged || Math.Abs(e.LongitudeOffset) > 180d)
{ {
// update immediately when map projection has changed or map center has moved across 180° longitude UpdateTileGrid(); // update immediately when projection has changed or center has moved across 180° longitude
UpdateTileGrid();
} }
else else
{ {
@ -314,9 +319,8 @@ namespace MapControl
} }
} }
private Point GetTileCenter(double tileScale) private Point GetTileCenter(double tileScale) // map center in tile index coordinates
{ {
// map center in tile index coordinates
return new Point( return new Point(
tileScale * (0.5 + parentMap.Center.Longitude / 360d), tileScale * (0.5 + parentMap.Center.Longitude / 360d),
tileScale * (0.5 - WebMercatorProjection.LatitudeToY(parentMap.Center.Latitude) / 360d)); tileScale * (0.5 - WebMercatorProjection.LatitudeToY(parentMap.Center.Latitude) / 360d));
@ -354,27 +358,7 @@ namespace MapControl
MatrixEx.TranslateScaleRotateTranslate(tileOrigin, scale, parentMap.Heading, viewCenter); MatrixEx.TranslateScaleRotateTranslate(tileOrigin, scale, parentMap.Heading, viewCenter);
} }
private void ResetTiles()
{
Tiles.Clear();
UpdateTiles();
}
private void UpdateTiles() private void UpdateTiles()
{
SelectTiles();
Children.Clear();
foreach (var tile in Tiles)
{
Children.Add(tile.Image);
}
TileImageLoader.LoadTiles(this);
}
private void SelectTiles()
{ {
var newTiles = new List<Tile>(); var newTiles = new List<Tile>();
@ -406,8 +390,7 @@ namespace MapControl
if (equivalentTile != null) if (equivalentTile != null)
{ {
// do not animate to avoid flicker when crossing 180° tile.SetImage(equivalentTile.Image.Source, false); // do not animate to avoid flicker when crossing 180° longitude
tile.SetImage(equivalentTile.Image.Source, false);
} }
} }
@ -418,6 +401,15 @@ namespace MapControl
} }
Tiles = newTiles; Tiles = newTiles;
Children.Clear();
foreach (var tile in Tiles)
{
Children.Add(tile.Image);
}
TileImageLoader.LoadTiles(this);
} }
} }
} }

View file

@ -14,8 +14,8 @@ using System.Windows;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -13,7 +13,7 @@ namespace MapControl
/// <summary> /// <summary>
/// Loads map tile images. /// Loads map tile images.
/// </summary> /// </summary>
internal class TileImageLoader : ITileImageLoader public class TileImageLoader : ITileImageLoader
{ {
public void LoadTiles(MapTileLayer tileLayer) public void LoadTiles(MapTileLayer tileLayer)
{ {

View file

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -69,11 +68,11 @@ namespace MapControl
{ {
pendingTiles.Clear(); pendingTiles.Clear();
var tileStack = tileLayer.Tiles.Where(t => t.Pending).Reverse().ToArray(); var tiles = tileLayer.Tiles.Where(t => t.Pending);
if (tileStack.Length > 0) if (tiles.Any())
{ {
pendingTiles.PushRange(tileStack); pendingTiles.PushRange(tiles.Reverse().ToArray());
var tileSource = tileLayer.TileSource; var tileSource = tileLayer.TileSource;
var sourceName = tileLayer.SourceName; var sourceName = tileLayer.SourceName;

View file

@ -64,21 +64,20 @@ namespace MapControl
{ {
pendingTiles.Clear(); pendingTiles.Clear();
var tileSource = tileLayer.TileSource;
var imageTileSource = tileSource as ImageTileSource;
var tiles = tileLayer.Tiles.Where(t => t.Pending); var tiles = tileLayer.Tiles.Where(t => t.Pending);
if (imageTileSource != null) if (tiles.Any())
{ {
LoadTiles(imageTileSource, tiles); var tileSource = tileLayer.TileSource;
} var imageTileSource = tileSource as ImageTileSource;
else
{
var tileStack = tiles.Reverse().ToArray();
if (tileStack.Length > 0) if (imageTileSource != null)
{ {
pendingTiles.PushRange(tileStack); LoadTiles(tiles, imageTileSource);
}
else
{
pendingTiles.PushRange(tiles.Reverse().ToArray());
var sourceName = tileLayer.SourceName; var sourceName = tileLayer.SourceName;
var maxDownloads = tileLayer.MaxParallelDownloads; var maxDownloads = tileLayer.MaxParallelDownloads;
@ -98,7 +97,7 @@ namespace MapControl
} }
} }
private void LoadTiles(ImageTileSource tileSource, IEnumerable<Tile> tiles) private void LoadTiles(IEnumerable<Tile> tiles, ImageTileSource tileSource)
{ {
foreach (var tile in tiles) foreach (var tile in tiles)
{ {
@ -134,7 +133,15 @@ namespace MapControl
if (uri != null) if (uri != null)
{ {
if (Cache == null || sourceName == null) if (!uri.IsAbsoluteUri)
{
await LoadImageFromFile(tile, uri.OriginalString);
}
else if (uri.Scheme == "file")
{
await LoadImageFromFile(tile, uri.LocalPath);
}
else if (Cache == null || sourceName == null)
{ {
await DownloadImage(tile, uri, null); await DownloadImage(tile, uri, null);
} }
@ -186,7 +193,14 @@ namespace MapControl
{ {
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
return await LoadImageFromHttpResponse(response, tile, cacheKey); string tileInfo;
if (!response.Headers.TryGetValue("X-VE-Tile-Info", out tileInfo) || tileInfo != "no-tile") // set by Bing Maps
{
await LoadImageFromHttpResponse(response, tile, cacheKey);
}
return true;
} }
Debug.WriteLine("{0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase); Debug.WriteLine("{0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
@ -200,15 +214,8 @@ namespace MapControl
return false; return false;
} }
private async Task<bool> LoadImageFromHttpResponse(HttpResponseMessage response, Tile tile, string cacheKey) private async Task LoadImageFromHttpResponse(HttpResponseMessage response, Tile tile, string cacheKey)
{ {
string tileInfo;
if (response.Headers.TryGetValue("X-VE-Tile-Info", out tileInfo) && tileInfo == "no-tile") // set by Bing Maps
{
return true;
}
using (var stream = new InMemoryRandomAccessStream()) using (var stream = new InMemoryRandomAccessStream())
{ {
using (var content = response.Content) using (var content = response.Content)
@ -219,9 +226,9 @@ namespace MapControl
await stream.FlushAsync(); await stream.FlushAsync();
stream.Seek(0); stream.Seek(0);
var loaded = await LoadImageFromStream(tile, stream); await LoadImageFromStream(tile, stream);
if (loaded && cacheKey != null) if (cacheKey != null)
{ {
var buffer = new Windows.Storage.Streams.Buffer((uint)stream.Size); var buffer = new Windows.Storage.Streams.Buffer((uint)stream.Size);
@ -242,14 +249,29 @@ namespace MapControl
await Cache.SetAsync(cacheKey, buffer, DateTime.UtcNow.Add(expiration)); await Cache.SetAsync(cacheKey, buffer, DateTime.UtcNow.Add(expiration));
} }
return loaded;
} }
} }
private async Task<bool> LoadImageFromStream(Tile tile, IRandomAccessStream stream) private async Task LoadImageFromFile(Tile tile, string path)
{ {
var tcs = new TaskCompletionSource<bool>(); try
{
var file = await StorageFile.GetFileFromPathAsync(path);
using (var stream = await file.OpenReadAsync())
{
await LoadImageFromStream(tile, stream);
}
}
catch (Exception ex)
{
Debug.WriteLine("{0}: {1}", path, ex.Message);
}
}
private async Task LoadImageFromStream(Tile tile, IRandomAccessStream stream)
{
var tcs = new TaskCompletionSource<object>();
await tile.Image.Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () => await tile.Image.Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
{ {
@ -258,16 +280,16 @@ namespace MapControl
var image = new BitmapImage(); var image = new BitmapImage();
await image.SetSourceAsync(stream); await image.SetSourceAsync(stream);
tile.SetImage(image, true, false); tile.SetImage(image, true, false);
tcs.SetResult(true);
tcs.SetResult(null);
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message); tcs.SetException(ex);
tcs.SetResult(false);
} }
}); });
return await tcs.Task; await tcs.Task;
} }
} }
} }

View file

@ -26,6 +26,7 @@
<DefineConstants>TRACE;DEBUG;NETFX_CORE</DefineConstants> <DefineConstants>TRACE;DEBUG;NETFX_CORE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<GenerateLibraryLayout>true</GenerateLibraryLayout>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
@ -34,6 +35,7 @@
<DefineConstants>TRACE;NETFX_CORE</DefineConstants> <DefineConstants>TRACE;NETFX_CORE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<GenerateLibraryLayout>true</GenerateLibraryLayout>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\AzimuthalEquidistantProjection.cs"> <Compile Include="..\AzimuthalEquidistantProjection.cs">
@ -212,7 +214,7 @@
</PreBuildEvent> </PreBuildEvent>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>copy "$(ProjectDir)MapControl.WinRT.xr.xml" "$(TargetDir)"</PostBuildEvent> <PostBuildEvent>copy "$(ProjectDir)$(TargetName).xr.xml" "$(TargetDir)$(TargetName)\"</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("3.2.0")] [assembly: AssemblyVersion("3.3.0")]
[assembly: AssemblyFileVersion("3.2.0")] [assembly: AssemblyFileVersion("3.3.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]