mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-04-05 14:37:01 +00:00
Version 2.1.0:
- TileImageLoader with local file caching in WinRT - Location implements IEquatable - Removed Surface sample application
This commit is contained in:
parent
10527c3f0d
commit
4e0253aa70
38 changed files with 493 additions and 393 deletions
|
|
@ -38,16 +38,15 @@ namespace MapControl
|
|||
public static void DrawGlyphRun(this DrawingContext drawingContext, Brush foreground, GlyphRun glyphRun,
|
||||
Point position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment)
|
||||
{
|
||||
var bbox = glyphRun.ComputeInkBoundingBox();
|
||||
var transform = new TranslateTransform(position.X - bbox.X, position.Y - bbox.Y);
|
||||
var boundingBox = glyphRun.ComputeInkBoundingBox();
|
||||
|
||||
switch (horizontalAlignment)
|
||||
{
|
||||
case HorizontalAlignment.Center:
|
||||
transform.X -= bbox.Width / 2d;
|
||||
position.X -= boundingBox.Width / 2d;
|
||||
break;
|
||||
case HorizontalAlignment.Right:
|
||||
transform.X -= bbox.Width;
|
||||
position.X -= boundingBox.Width;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -56,16 +55,16 @@ namespace MapControl
|
|||
switch (verticalAlignment)
|
||||
{
|
||||
case VerticalAlignment.Center:
|
||||
transform.Y -= bbox.Height / 2d;
|
||||
position.Y -= boundingBox.Height / 2d;
|
||||
break;
|
||||
case VerticalAlignment.Bottom:
|
||||
transform.Y -= bbox.Height;
|
||||
position.Y -= boundingBox.Height;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
drawingContext.PushTransform(transform);
|
||||
drawingContext.PushTransform(new TranslateTransform(position.X - boundingBox.X, position.Y - boundingBox.Y));
|
||||
drawingContext.DrawGlyphRun(foreground, glyphRun);
|
||||
drawingContext.Pop();
|
||||
}
|
||||
|
|
|
|||
14
MapControl/IObjectCache.WinRT.cs
Normal file
14
MapControl/IObjectCache.WinRT.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2014 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public interface IObjectCache
|
||||
{
|
||||
Task<object> GetAsync(string key);
|
||||
Task SetAsync(string key, object value);
|
||||
}
|
||||
}
|
||||
62
MapControl/ImageFileCache.WinRT.cs
Normal file
62
MapControl/ImageFileCache.WinRT.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2014 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class ImageFileCache : IObjectCache
|
||||
{
|
||||
private readonly IStorageFolder rootFolder;
|
||||
|
||||
public ImageFileCache()
|
||||
{
|
||||
rootFolder = ApplicationData.Current.TemporaryFolder;
|
||||
}
|
||||
|
||||
public ImageFileCache(IStorageFolder folder)
|
||||
{
|
||||
rootFolder = folder;
|
||||
}
|
||||
|
||||
public async Task<object> GetAsync(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await PathIO.ReadBufferAsync(Path.Combine(rootFolder.Path, key));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SetAsync(string key, object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var buffer = (IBuffer)value;
|
||||
var names = key.Split('\\');
|
||||
var folder = rootFolder;
|
||||
|
||||
for (int i = 0; i < names.Length - 1; i++)
|
||||
{
|
||||
folder = await folder.CreateFolderAsync(names[i], CreationCollisionOption.OpenIfExists);
|
||||
}
|
||||
|
||||
var file = await folder.CreateFileAsync(names[names.Length - 1], CreationCollisionOption.ReplaceExisting);
|
||||
await FileIO.WriteBufferAsync(file, buffer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// A geographic location with latitude and longitude values in degrees.
|
||||
/// </summary>
|
||||
public partial class Location
|
||||
public partial class Location : IEquatable<Location>
|
||||
{
|
||||
private double latitude;
|
||||
private double longitude;
|
||||
|
|
@ -37,6 +37,23 @@ namespace MapControl
|
|||
set { longitude = value; }
|
||||
}
|
||||
|
||||
public bool Equals(Location location)
|
||||
{
|
||||
return location != null
|
||||
&& location.latitude == latitude
|
||||
&& location.longitude == longitude;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as Location);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return latitude.GetHashCode() ^ longitude.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0:F5},{1:F5}", latitude, longitude);
|
||||
|
|
|
|||
|
|
@ -579,7 +579,7 @@ namespace MapControl
|
|||
{
|
||||
AdjustCenterProperty(TargetCenterProperty, ref targetCenter);
|
||||
|
||||
if (targetCenter.Latitude != Center.Latitude || targetCenter.Longitude != Center.Longitude)
|
||||
if (!targetCenter.Equals(Center))
|
||||
{
|
||||
if (centerAnimation != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@
|
|||
<Compile Include="Tile.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileContainer.cs" />
|
||||
<Compile Include="TileContainer.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileImageLoader.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileImageLoader.Silverlight.cs" />
|
||||
<Compile Include="TileLayer.cs" />
|
||||
<Compile Include="TileLayerCollection.cs" />
|
||||
<Compile Include="TileSource.cs" />
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if WINDOWS_RUNTIME
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
|
@ -31,7 +31,7 @@ namespace MapControl
|
|||
public MapGraticule()
|
||||
{
|
||||
IsHitTestVisible = false;
|
||||
StrokeThickness = 0.5;
|
||||
Stroke = new SolidColorBrush(Color.FromArgb(127, 0, 0, 0));
|
||||
|
||||
path = new Path
|
||||
{
|
||||
|
|
@ -55,7 +55,6 @@ namespace MapControl
|
|||
|
||||
protected override void OnViewportChanged()
|
||||
{
|
||||
var geometry = (PathGeometry)path.Data;
|
||||
var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(new Point(), ParentMap.RenderSize));
|
||||
var start = ParentMap.MapTransform.Transform(new Point(bounds.X, bounds.Y));
|
||||
var end = ParentMap.MapTransform.Transform(new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height));
|
||||
|
|
@ -88,28 +87,22 @@ namespace MapControl
|
|||
graticuleStart = lineStart;
|
||||
graticuleEnd = lineEnd;
|
||||
|
||||
var geometry = (PathGeometry)path.Data;
|
||||
geometry.Figures.Clear();
|
||||
geometry.Transform = ParentMap.ViewportTransform;
|
||||
|
||||
var latLocations = new List<Location>((int)((end.Latitude - labelStart.Latitude) / spacing) + 1);
|
||||
|
||||
for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += spacing)
|
||||
{
|
||||
var location = new Location(lat, lineStart.Longitude);
|
||||
latLocations.Add(location);
|
||||
|
||||
var figure = new PathFigure
|
||||
{
|
||||
StartPoint = ParentMap.MapTransform.Transform(location),
|
||||
StartPoint = ParentMap.MapTransform.Transform(new Location(lat, lineStart.Longitude)),
|
||||
IsClosed = false,
|
||||
IsFilled = false
|
||||
};
|
||||
|
||||
location.Longitude = lineEnd.Longitude;
|
||||
|
||||
figure.Segments.Add(new LineSegment
|
||||
{
|
||||
Point = ParentMap.MapTransform.Transform(location),
|
||||
Point = ParentMap.MapTransform.Transform(new Location(lat, lineEnd.Longitude)),
|
||||
});
|
||||
|
||||
geometry.Figures.Add(figure);
|
||||
|
|
@ -134,14 +127,11 @@ namespace MapControl
|
|||
|
||||
var childIndex = 1; // 0 for Path
|
||||
var format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
|
||||
var measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
|
||||
|
||||
foreach (var location in latLocations)
|
||||
for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += spacing)
|
||||
{
|
||||
for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing)
|
||||
{
|
||||
location.Longitude = lon;
|
||||
|
||||
TextBlock label;
|
||||
|
||||
if (childIndex < Children.Count)
|
||||
|
|
@ -150,9 +140,14 @@ namespace MapControl
|
|||
}
|
||||
else
|
||||
{
|
||||
var renderTransform = new TransformGroup();
|
||||
renderTransform.Children.Add(new TranslateTransform());
|
||||
renderTransform.Children.Add(ParentMap.RotateTransform);
|
||||
renderTransform.Children.Add(new TranslateTransform());
|
||||
|
||||
label = new TextBlock
|
||||
{
|
||||
RenderTransform = new TransformGroup()
|
||||
RenderTransform = renderTransform
|
||||
};
|
||||
|
||||
label.SetBinding(TextBlock.ForegroundProperty, new Binding
|
||||
|
|
@ -175,30 +170,13 @@ namespace MapControl
|
|||
label.FontStyle = FontStyle;
|
||||
label.FontStretch = FontStretch;
|
||||
label.FontWeight = FontWeight;
|
||||
label.Text = string.Format("{0}\n{1}", CoordinateString(lat, format, "NS"), CoordinateString(Location.NormalizeLongitude(lon), format, "EW"));
|
||||
label.Tag = new Location(lat, lon);
|
||||
label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
|
||||
label.Text = string.Format("{0}\n{1}",
|
||||
CoordinateString(location.Latitude, format, "NS"),
|
||||
CoordinateString(Location.NormalizeLongitude(location.Longitude), format, "EW"));
|
||||
|
||||
label.Measure(measureSize);
|
||||
|
||||
var transformGroup = (TransformGroup)label.RenderTransform;
|
||||
|
||||
if (transformGroup.Children.Count == 0)
|
||||
{
|
||||
transformGroup.Children.Add(new TranslateTransform());
|
||||
transformGroup.Children.Add(ParentMap.RotateTransform);
|
||||
transformGroup.Children.Add(new TranslateTransform());
|
||||
}
|
||||
|
||||
var translateTransform = (TranslateTransform)transformGroup.Children[0];
|
||||
var translateTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[0];
|
||||
translateTransform.X = StrokeThickness / 2d + 2d;
|
||||
translateTransform.Y = -label.DesiredSize.Height / 2d;
|
||||
|
||||
var viewportPosition = ParentMap.LocationToViewportPoint(location);
|
||||
translateTransform = (TranslateTransform)transformGroup.Children[2];
|
||||
translateTransform.X = viewportPosition.X;
|
||||
translateTransform.Y = viewportPosition.Y;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +186,18 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
// don't use MapPanel.Location because labels may be at more than 180° distance from map center
|
||||
|
||||
for (int i = 1; i < Children.Count; i++)
|
||||
{
|
||||
var label = (TextBlock)Children[i];
|
||||
var location = (Location)label.Tag;
|
||||
var viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2];
|
||||
var viewportPosition = ParentMap.LocationToViewportPoint(location);
|
||||
viewportTransform.X = viewportPosition.X;
|
||||
viewportTransform.Y = viewportPosition.Y;
|
||||
}
|
||||
|
||||
base.OnViewportChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Copyright © 2014 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if WINDOWS_RUNTIME
|
||||
using Windows.UI.Xaml;
|
||||
#else
|
||||
|
|
@ -43,7 +44,7 @@ namespace MapControl
|
|||
hemisphere = hemispheres[1];
|
||||
}
|
||||
|
||||
var minutes = (int)(value * 60d + 0.5);
|
||||
var minutes = (int)Math.Round(value * 60d);
|
||||
|
||||
return string.Format(format, hemisphere, minutes / 60, (double)(minutes % 60));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ using System.Windows;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("2.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.0.0")]
|
||||
[assembly: AssemblyVersion("2.1.0")]
|
||||
[assembly: AssemblyFileVersion("2.1.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -13,12 +13,6 @@ namespace MapControl
|
|||
{
|
||||
internal partial class TileContainer
|
||||
{
|
||||
private Matrix GetViewportTransformMatrix(double scale, double offsetX, double offsetY)
|
||||
{
|
||||
return new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY)
|
||||
.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y);
|
||||
}
|
||||
|
||||
private Matrix GetTileIndexMatrix(int numTiles)
|
||||
{
|
||||
var scale = (double)numTiles / 360d;
|
||||
|
|
@ -29,6 +23,13 @@ namespace MapControl
|
|||
.Scale(scale, -scale); // map coordinates to tile indices
|
||||
}
|
||||
|
||||
private void UpdateViewportTransform(double scale, double offsetX, double offsetY)
|
||||
{
|
||||
ViewportTransform.Matrix =
|
||||
new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY)
|
||||
.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a RenderTransform with origin at tileGrid.X and tileGrid.Y to minimize rounding errors.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -9,14 +9,6 @@ namespace MapControl
|
|||
{
|
||||
internal partial class TileContainer
|
||||
{
|
||||
private Matrix GetViewportTransformMatrix(double scale, double offsetX, double offsetY)
|
||||
{
|
||||
var transform = new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY);
|
||||
transform.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
private Matrix GetTileIndexMatrix(int numTiles)
|
||||
{
|
||||
var scale = (double)numTiles / 360d;
|
||||
|
|
@ -28,6 +20,14 @@ namespace MapControl
|
|||
return transform;
|
||||
}
|
||||
|
||||
private void UpdateViewportTransform(double scale, double offsetX, double offsetY)
|
||||
{
|
||||
var transform = new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY);
|
||||
transform.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y);
|
||||
|
||||
ViewportTransform.Matrix = transform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a RenderTransform with origin at tileGrid.X and tileGrid.Y to minimize rounding errors.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace MapControl
|
|||
// relative scaled tile size ranges from 0.75 to 1.5 (192 to 384 pixels)
|
||||
private static double zoomLevelSwitchDelta = -Math.Log(0.75, 2d);
|
||||
|
||||
internal static TimeSpan UpdateInterval = TimeSpan.FromSeconds(0.5);
|
||||
public static TimeSpan UpdateInterval = TimeSpan.FromSeconds(0.5);
|
||||
|
||||
private readonly DispatcherTimer updateTimer;
|
||||
private Size viewportSize;
|
||||
|
|
@ -102,7 +102,7 @@ namespace MapControl
|
|||
var transformOffsetX = viewportOrigin.X - mapOrigin.X * scale;
|
||||
var transformOffsetY = viewportOrigin.Y + mapOrigin.Y * scale;
|
||||
|
||||
ViewportTransform.Matrix = GetViewportTransformMatrix(scale, transformOffsetX, transformOffsetY);
|
||||
UpdateViewportTransform(scale, transformOffsetX, transformOffsetY);
|
||||
|
||||
tileLayerOffset.X = transformOffsetX - 180d * scale;
|
||||
tileLayerOffset.Y = transformOffsetY - 180d * scale;
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2014 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
#if WINDOWS_RUNTIME
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
#else
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads map tile images.
|
||||
/// </summary>
|
||||
internal class TileImageLoader
|
||||
{
|
||||
internal void BeginGetTiles(TileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||
{
|
||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
tile.SetImageSource(image, tileLayer.AnimateTileOpacity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Loading tile image failed: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
var image = uri != null ? new BitmapImage(uri) : null;
|
||||
tile.SetImageSource(image, tileLayer.AnimateTileOpacity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Creating tile image failed: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelGetTiles()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
55
MapControl/TileImageLoader.Silverlight.cs
Normal file
55
MapControl/TileImageLoader.Silverlight.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2014 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads map tile images.
|
||||
/// </summary>
|
||||
internal class TileImageLoader
|
||||
{
|
||||
internal void BeginGetTiles(TileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||
{
|
||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
ImageSource image = null;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
image = new BitmapImage(uri);
|
||||
}
|
||||
}
|
||||
|
||||
tile.SetImageSource(image, tileLayer.AnimateTileOpacity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Loading tile image failed: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void CancelGetTiles()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
196
MapControl/TileImageLoader.WinRT.cs
Normal file
196
MapControl/TileImageLoader.WinRT.cs
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2014 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
using Windows.Web.Http;
|
||||
using Windows.Web.Http.Filters;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads map tile images.
|
||||
/// </summary>
|
||||
public class TileImageLoader
|
||||
{
|
||||
public static IObjectCache Cache { get; set; }
|
||||
|
||||
private HttpClient httpClient;
|
||||
|
||||
internal void BeginGetTiles(TileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||
{
|
||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
ImageSource image = null;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
if (Cache == null || string.IsNullOrEmpty(tileLayer.SourceName))
|
||||
{
|
||||
image = new BitmapImage(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bitmap = new BitmapImage();
|
||||
image = bitmap;
|
||||
|
||||
Task.Run(async () => await LoadCachedImage(tileLayer, tile, uri, bitmap));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tile.SetImageSource(image, tileLayer.AnimateTileOpacity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Loading tile image failed: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void CancelGetTiles()
|
||||
{
|
||||
}
|
||||
|
||||
private async Task LoadCachedImage(TileLayer tileLayer, Tile tile, Uri uri, BitmapImage bitmap)
|
||||
{
|
||||
var cacheKey = string.Format(@"{0}\{1}\{2}\{3}{4}",
|
||||
tileLayer.SourceName, tile.ZoomLevel, tile.XIndex, tile.Y, Path.GetExtension(uri.LocalPath));
|
||||
|
||||
var buffer = await Cache.GetAsync(cacheKey) as IBuffer;
|
||||
|
||||
if (buffer != null)
|
||||
{
|
||||
await LoadImageFromBuffer(buffer, bitmap);
|
||||
//Debug.WriteLine("Loaded cached image {0}", cacheKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadAndCacheImage(uri, bitmap, cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadImageFromBuffer(IBuffer buffer, BitmapImage bitmap)
|
||||
{
|
||||
using (var stream = new InMemoryRandomAccessStream())
|
||||
{
|
||||
await stream.WriteAsync(buffer);
|
||||
await stream.FlushAsync();
|
||||
stream.Seek(0);
|
||||
|
||||
await bitmap.Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await bitmap.SetSourceAsync(stream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void DownloadAndCacheImage(Uri uri, BitmapImage bitmap, string cacheKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (httpClient == null)
|
||||
{
|
||||
var filter = new HttpBaseProtocolFilter();
|
||||
filter.AllowAutoRedirect = false;
|
||||
filter.CacheControl.ReadBehavior = HttpCacheReadBehavior.Default;
|
||||
filter.CacheControl.WriteBehavior = HttpCacheWriteBehavior.NoCache;
|
||||
|
||||
httpClient = new HttpClient(filter);
|
||||
}
|
||||
|
||||
httpClient.GetAsync(uri).Completed = async (request, status) =>
|
||||
{
|
||||
if (status == AsyncStatus.Completed)
|
||||
{
|
||||
using (var response = request.GetResults())
|
||||
{
|
||||
await LoadImageFromHttpResponse(response, bitmap, cacheKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", uri, request.ErrorCode != null ? request.ErrorCode.Message : status.ToString());
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", uri, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadImageFromHttpResponse(HttpResponseMessage response, BitmapImage bitmap, string cacheKey)
|
||||
{
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
|
||||
using (var content = response.Content)
|
||||
{
|
||||
await content.WriteToStreamAsync(stream);
|
||||
}
|
||||
|
||||
await stream.FlushAsync();
|
||||
stream.Seek(0);
|
||||
|
||||
await bitmap.Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await bitmap.SetSourceAsync(stream);
|
||||
|
||||
// cache image asynchronously, after successful decoding
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
var buffer = new Windows.Storage.Streams.Buffer((uint)stream.Size);
|
||||
|
||||
stream.Seek(0);
|
||||
await stream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.None);
|
||||
stream.Dispose();
|
||||
|
||||
await Cache.SetAsync(cacheKey, buffer);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", response.RequestMessage.RequestUri, ex.Message);
|
||||
stream.Dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", response.RequestMessage.RequestUri, response.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,9 @@
|
|||
<Compile Include="..\Extensions.WinRT.cs">
|
||||
<Link>Extensions.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ImageFileCache.WinRT.cs">
|
||||
<Link>ImageFileCache.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ImageTileSource.Silverlight.WinRT.cs">
|
||||
<Link>ImageTileSource.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
|
|
@ -51,6 +54,9 @@
|
|||
<Compile Include="..\Int32Rect.cs">
|
||||
<Link>Int32Rect.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\IObjectCache.WinRT.cs">
|
||||
<Link>IObjectCache.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Location.cs">
|
||||
<Link>Location.cs</Link>
|
||||
</Compile>
|
||||
|
|
@ -138,8 +144,8 @@
|
|||
<Compile Include="..\TileContainer.Silverlight.WinRT.cs">
|
||||
<Link>TileContainer.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileImageLoader.Silverlight.WinRT.cs">
|
||||
<Link>TileImageLoader.Silverlight.WinRT.cs</Link>
|
||||
<Compile Include="..\TileImageLoader.WinRT.cs">
|
||||
<Link>TileImageLoader.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileLayer.cs">
|
||||
<Link>TileLayer.cs</Link>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("2.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.0.0")]
|
||||
[assembly: AssemblyVersion("2.1.0")]
|
||||
[assembly: AssemblyFileVersion("2.1.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue