mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Version 1.8.0: Fixed MapImage, added ImageTileSource to Silverlight and WinRT versions, modified TileImageLoader.
This commit is contained in:
parent
8eedb82a9d
commit
9f4ab0f3e3
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,22 @@ namespace Caching
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ObjectCache implementation based on local image files.
|
/// ObjectCache implementation based on local image files.
|
||||||
/// The only valid data type for cached values is a byte[], which contains
|
/// The only valid data type for cached values is a byte array containing an
|
||||||
/// an 8-byte binary UTC time (as created by DateTime.ToBinary), followed
|
/// 8-byte timestamp followed by a PNG, JPEG, BMP, GIF, TIFF or WMP image buffer.
|
||||||
/// by a PNG, JPEG, BMP, GIF, TIFF or WMP image buffer.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ImageFileCache : ObjectCache
|
public class ImageFileCache : ObjectCache
|
||||||
{
|
{
|
||||||
|
private static readonly Tuple<string, byte[]>[] imageFileTypes = new Tuple<string, byte[]>[]
|
||||||
|
{
|
||||||
|
new Tuple<string, byte[]>(".png", new byte[] { 0x89, 0x50, 0x4E, 0x47, 0xD, 0xA, 0x1A, 0xA }),
|
||||||
|
new Tuple<string, byte[]>(".jpg", new byte[] { 0xFF, 0xD8, 0xFF, 0xE0, 0, 0x10, 0x4A, 0x46, 0x49, 0x46, 0 }),
|
||||||
|
new Tuple<string, byte[]>(".bmp", new byte[] { 0x42, 0x4D }),
|
||||||
|
new Tuple<string, byte[]>(".gif", new byte[] { 0x47, 0x49, 0x46 }),
|
||||||
|
new Tuple<string, byte[]>(".tif", new byte[] { 0x49, 0x49, 42, 0 }),
|
||||||
|
new Tuple<string, byte[]>(".tif", new byte[] { 0x4D, 0x4D, 0, 42 }),
|
||||||
|
new Tuple<string, byte[]>(".wdp", new byte[] { 0x49, 0x49, 0xBC }),
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly FileSystemAccessRule fullControlRule = new FileSystemAccessRule(
|
private static readonly FileSystemAccessRule fullControlRule = new FileSystemAccessRule(
|
||||||
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
|
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
|
||||||
FileSystemRights.FullControl, AccessControlType.Allow);
|
FileSystemRights.FullControl, AccessControlType.Allow);
|
||||||
|
|
@ -169,18 +179,14 @@ namespace Caching
|
||||||
|
|
||||||
var buffer = value as byte[];
|
var buffer = value as byte[];
|
||||||
|
|
||||||
if (buffer == null)
|
if (buffer == null || buffer.Length <= 8)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("The parameter value must be a byte[].");
|
throw new NotSupportedException("The parameter value must be a byte[] containing at least 9 bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryCache.Default.Set(key, buffer, policy);
|
MemoryCache.Default.Set(key, buffer, policy);
|
||||||
|
|
||||||
var extension = GetFileExtension(buffer);
|
var path = GetPath(key) + GetFileExtension(buffer);
|
||||||
|
|
||||||
if (extension != null)
|
|
||||||
{
|
|
||||||
var path = GetPath(key) + extension;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -200,7 +206,6 @@ namespace Caching
|
||||||
Trace.TraceWarning("ImageFileCache: Writing file {0} failed: {1}", path, ex.Message);
|
Trace.TraceWarning("ImageFileCache: Writing file {0} failed: {1}", path, ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public override void Set(CacheItem item, CacheItemPolicy policy)
|
public override void Set(CacheItem item, CacheItemPolicy policy)
|
||||||
{
|
{
|
||||||
|
|
@ -274,42 +279,24 @@ namespace Caching
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Tuple<string, byte[]>[] fileTypes = new Tuple<string, byte[]>[]
|
|
||||||
{
|
|
||||||
new Tuple<string, byte[]>(".png", new byte[] { 0x89, 0x50, 0x4E, 0x47, 0xD, 0xA, 0x1A, 0xA }),
|
|
||||||
new Tuple<string, byte[]>(".jpg", new byte[] { 0xFF, 0xD8, 0xFF, 0xE0, 0, 0x10, 0x4A, 0x46, 0x49, 0x46, 0 }),
|
|
||||||
new Tuple<string, byte[]>(".bmp", new byte[] { 0x42, 0x4D }),
|
|
||||||
new Tuple<string, byte[]>(".gif", new byte[] { 0x47, 0x49, 0x46 }),
|
|
||||||
new Tuple<string, byte[]>(".tif", new byte[] { 0x49, 0x49, 42, 0 }),
|
|
||||||
new Tuple<string, byte[]>(".tif", new byte[] { 0x4D, 0x4D, 0, 42 }),
|
|
||||||
new Tuple<string, byte[]>(".wdp", new byte[] { 0x49, 0x49, 0xBC }),
|
|
||||||
};
|
|
||||||
|
|
||||||
private static string GetFileExtension(byte[] buffer)
|
private static string GetFileExtension(byte[] buffer)
|
||||||
{
|
{
|
||||||
string extension = null;
|
var fileType = imageFileTypes.FirstOrDefault(t =>
|
||||||
var creationTime = DateTime.FromBinary(BitConverter.ToInt64(buffer, 0));
|
|
||||||
|
|
||||||
if (creationTime.Kind == DateTimeKind.Utc && creationTime <= DateTime.UtcNow)
|
|
||||||
{
|
|
||||||
Func<Tuple<string, byte[]>, bool> match =
|
|
||||||
t =>
|
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
if (t.Item2.Length + 8 <= buffer.Length)
|
|
||||||
|
if (t.Item2.Length <= buffer.Length - 8)
|
||||||
{
|
{
|
||||||
while (i < t.Item2.Length && t.Item2[i] == buffer[i + 8])
|
while (i < t.Item2.Length && t.Item2[i] == buffer[i + 8])
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return i == t.Item2.Length;
|
return i == t.Item2.Length;
|
||||||
};
|
});
|
||||||
|
|
||||||
extension = fileTypes.Where(match).Select(t => t.Item1).FirstOrDefault();
|
return fileType != null ? fileType.Item1 : ".bin";
|
||||||
}
|
|
||||||
|
|
||||||
return extension;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
|
||||||
// Copyright © Clemens Fischer 2012-2013
|
|
||||||
// Licensed under the Microsoft Public License (Ms-PL)
|
|
||||||
|
|
||||||
#if NETFX_CORE
|
|
||||||
using Windows.UI.Xaml;
|
|
||||||
#else
|
|
||||||
using System.Windows;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace MapControl
|
|
||||||
{
|
|
||||||
internal static class Freezable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides WPF compatibility.
|
|
||||||
/// </summary>
|
|
||||||
public static void Freeze(this DependencyObject obj)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
14
MapControl/ImageTileSource.WPF.cs
Normal file
14
MapControl/ImageTileSource.WPF.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||||
|
// Copyright © Clemens Fischer 2012-2013
|
||||||
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
|
namespace MapControl
|
||||||
|
{
|
||||||
|
public partial class ImageTileSource
|
||||||
|
{
|
||||||
|
public virtual bool CanLoadAsync
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,28 +2,30 @@
|
||||||
// Copyright © Clemens Fischer 2012-2013
|
// Copyright © Clemens Fischer 2012-2013
|
||||||
// Licensed under the Microsoft Public License (Ms-PL)
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
|
#if NETFX_CORE
|
||||||
|
using Windows.UI.Xaml.Media;
|
||||||
|
using Windows.UI.Xaml.Media.Imaging;
|
||||||
|
#else
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides the image of a map tile. ImageTileSource bypasses download and
|
/// Provides the image of a map tile. ImageTileSource bypasses downloading and
|
||||||
/// cache processing in TileImageLoader. By overriding the LoadImage method,
|
/// optional caching in TileImageLoader. By overriding the LoadImage method,
|
||||||
/// an application can provide tile images from an arbitrary source.
|
/// an application can provide tile images from an arbitrary source.
|
||||||
/// If the CanLoadAsync property is true, the LoadImage method will be called
|
/// WPF only: If the CanLoadAsync property is true, LoadImage will be called
|
||||||
/// from a separate, non-UI thread and must hence return a frozen ImageSource.
|
/// from a separate, non-UI thread and must hence return a frozen ImageSource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ImageTileSource : TileSource
|
public partial class ImageTileSource : TileSource
|
||||||
{
|
{
|
||||||
public virtual bool CanLoadAsync
|
|
||||||
{
|
|
||||||
get { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual ImageSource LoadImage(int x, int y, int zoomLevel)
|
public virtual ImageSource LoadImage(int x, int y, int zoomLevel)
|
||||||
{
|
{
|
||||||
return new BitmapImage(GetUri(x, y, zoomLevel));
|
var uri = GetUri(x, y, zoomLevel);
|
||||||
|
|
||||||
|
return uri != null ? new BitmapImage(uri) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AnimationEx.Silverlight.cs" />
|
<Compile Include="AnimationEx.Silverlight.cs" />
|
||||||
<Compile Include="Freezable.cs" />
|
<Compile Include="ImageTileSource.cs" />
|
||||||
<Compile Include="Int32Rect.cs" />
|
<Compile Include="Int32Rect.cs" />
|
||||||
<Compile Include="Location.cs" />
|
<Compile Include="Location.cs" />
|
||||||
<Compile Include="LocationCollection.cs" />
|
<Compile Include="LocationCollection.cs" />
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="GlyphRunText.cs" />
|
<Compile Include="GlyphRunText.cs" />
|
||||||
<Compile Include="ImageTileSource.cs" />
|
<Compile Include="ImageTileSource.cs" />
|
||||||
|
<Compile Include="ImageTileSource.WPF.cs" />
|
||||||
<Compile Include="Location.cs" />
|
<Compile Include="Location.cs" />
|
||||||
<Compile Include="LocationCollection.cs" />
|
<Compile Include="LocationCollection.cs" />
|
||||||
<Compile Include="LocationCollectionConverter.cs" />
|
<Compile Include="LocationCollectionConverter.cs" />
|
||||||
|
|
@ -63,6 +64,7 @@
|
||||||
<Compile Include="MapGraticule.cs" />
|
<Compile Include="MapGraticule.cs" />
|
||||||
<Compile Include="MapGraticule.WPF.cs" />
|
<Compile Include="MapGraticule.WPF.cs" />
|
||||||
<Compile Include="MapImage.cs" />
|
<Compile Include="MapImage.cs" />
|
||||||
|
<Compile Include="MapImage.WPF.cs" />
|
||||||
<Compile Include="MapImageLayer.cs" />
|
<Compile Include="MapImageLayer.cs" />
|
||||||
<Compile Include="MapItem.WPF.cs" />
|
<Compile Include="MapItem.WPF.cs" />
|
||||||
<Compile Include="MapItemsControl.WPF.cs" />
|
<Compile Include="MapItemsControl.WPF.cs" />
|
||||||
|
|
@ -74,6 +76,7 @@
|
||||||
<Compile Include="MapPolyline.cs" />
|
<Compile Include="MapPolyline.cs" />
|
||||||
<Compile Include="MapPolyline.WPF.cs" />
|
<Compile Include="MapPolyline.WPF.cs" />
|
||||||
<Compile Include="MapRectangle.cs" />
|
<Compile Include="MapRectangle.cs" />
|
||||||
|
<Compile Include="MapRectangle.WPF.cs" />
|
||||||
<Compile Include="MapScale.cs" />
|
<Compile Include="MapScale.cs" />
|
||||||
<Compile Include="MapTransform.cs" />
|
<Compile Include="MapTransform.cs" />
|
||||||
<Compile Include="MercatorTransform.cs" />
|
<Compile Include="MercatorTransform.cs" />
|
||||||
|
|
|
||||||
14
MapControl/MapImage.WPF.cs
Normal file
14
MapControl/MapImage.WPF.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||||
|
// Copyright © Clemens Fischer 2012-2013
|
||||||
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
|
namespace MapControl
|
||||||
|
{
|
||||||
|
public partial class MapImage
|
||||||
|
{
|
||||||
|
static MapImage()
|
||||||
|
{
|
||||||
|
imageTransform.Freeze();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,18 +15,13 @@ namespace MapControl
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fills a rectangular area with an ImageBrush from the Source property.
|
/// Fills a rectangular area with an ImageBrush from the Source property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MapImage : MapRectangle
|
public partial class MapImage : MapRectangle
|
||||||
{
|
{
|
||||||
private static readonly MatrixTransform imageTransform = new MatrixTransform
|
private static readonly MatrixTransform imageTransform = new MatrixTransform
|
||||||
{
|
{
|
||||||
Matrix = new Matrix(1d, 0d, 0d, -1d, 0d, 1d)
|
Matrix = new Matrix(1d, 0d, 0d, -1d, 0d, 1d)
|
||||||
};
|
};
|
||||||
|
|
||||||
static MapImage()
|
|
||||||
{
|
|
||||||
imageTransform.Freeze();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
|
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
|
||||||
"Source", typeof(ImageSource), typeof(MapImage),
|
"Source", typeof(ImageSource), typeof(MapImage),
|
||||||
new PropertyMetadata(null, (o, e) => ((MapImage)o).SourceChanged((ImageSource)e.NewValue)));
|
new PropertyMetadata(null, (o, e) => ((MapImage)o).SourceChanged((ImageSource)e.NewValue)));
|
||||||
|
|
@ -45,7 +40,6 @@ namespace MapControl
|
||||||
RelativeTransform = imageTransform
|
RelativeTransform = imageTransform
|
||||||
};
|
};
|
||||||
|
|
||||||
imageBrush.Freeze();
|
|
||||||
Fill = imageBrush;
|
Fill = imageBrush;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,6 @@ namespace MapControl
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var bitmap = new BitmapImage();
|
|
||||||
var request = (HttpWebRequest)WebRequest.Create(uri);
|
var request = (HttpWebRequest)WebRequest.Create(uri);
|
||||||
request.UserAgent = "XAML Map Control";
|
request.UserAgent = "XAML Map Control";
|
||||||
|
|
||||||
|
|
@ -128,15 +127,8 @@ namespace MapControl
|
||||||
using (var memoryStream = new MemoryStream())
|
using (var memoryStream = new MemoryStream())
|
||||||
{
|
{
|
||||||
responseStream.CopyTo(memoryStream);
|
responseStream.CopyTo(memoryStream);
|
||||||
|
image = BitmapFrame.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||||
bitmap.BeginInit();
|
|
||||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
|
||||||
bitmap.StreamSource = memoryStream;
|
|
||||||
bitmap.EndInit();
|
|
||||||
bitmap.Freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
image = bitmap;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
14
MapControl/MapRectangle.WPF.cs
Normal file
14
MapControl/MapRectangle.WPF.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||||
|
// Copyright © Clemens Fischer 2012-2013
|
||||||
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
|
namespace MapControl
|
||||||
|
{
|
||||||
|
public partial class MapRectangle
|
||||||
|
{
|
||||||
|
static MapRectangle()
|
||||||
|
{
|
||||||
|
geometryScaleTransform.Freeze();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ namespace MapControl
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fills a rectangular area defined by South, North, West and East with a Brush.
|
/// Fills a rectangular area defined by South, North, West and East with a Brush.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MapRectangle : MapPath
|
public partial class MapRectangle : MapPath
|
||||||
{
|
{
|
||||||
private const double geometryScale = 1e6;
|
private const double geometryScale = 1e6;
|
||||||
|
|
||||||
|
|
@ -26,11 +26,6 @@ namespace MapControl
|
||||||
ScaleY = 1d / geometryScale
|
ScaleY = 1d / geometryScale
|
||||||
};
|
};
|
||||||
|
|
||||||
static MapRectangle()
|
|
||||||
{
|
|
||||||
geometryScaleTransform.Freeze();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty SouthProperty = DependencyProperty.Register(
|
public static readonly DependencyProperty SouthProperty = DependencyProperty.Register(
|
||||||
"South", typeof(double), typeof(MapRectangle),
|
"South", typeof(double), typeof(MapRectangle),
|
||||||
new PropertyMetadata(double.NaN, (o, e) => ((MapRectangle)o).UpdateData()));
|
new PropertyMetadata(double.NaN, (o, e) => ((MapRectangle)o).UpdateData()));
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ using System.Windows;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,12 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
if (animateOpacity)
|
if (animateOpacity)
|
||||||
{
|
{
|
||||||
var bitmapImage = image as BitmapImage;
|
var bitmap = image as BitmapImage;
|
||||||
|
|
||||||
if (bitmapImage != null)
|
if (bitmap != null)
|
||||||
{
|
{
|
||||||
bitmapImage.ImageOpened += BitmapImageOpened;
|
bitmap.ImageOpened += BitmapImageOpened;
|
||||||
bitmapImage.ImageFailed += BitmapImageFailed;
|
bitmap.ImageFailed += BitmapImageFailed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -52,20 +52,26 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
Image.Source = image;
|
Image.Source = image;
|
||||||
HasImage = true;
|
HasImageSource = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BitmapImageOpened(object sender, RoutedEventArgs e)
|
private void BitmapImageOpened(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
((BitmapImage)sender).ImageOpened -= BitmapImageOpened;
|
var bitmap = (BitmapImage)sender;
|
||||||
((BitmapImage)sender).ImageFailed -= BitmapImageFailed;
|
|
||||||
|
bitmap.ImageOpened -= BitmapImageOpened;
|
||||||
|
bitmap.ImageFailed -= BitmapImageFailed;
|
||||||
|
|
||||||
Image.BeginAnimation(Image.OpacityProperty, new DoubleAnimation { To = 1d, Duration = AnimationDuration });
|
Image.BeginAnimation(Image.OpacityProperty, new DoubleAnimation { To = 1d, Duration = AnimationDuration });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
|
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
((BitmapImage)sender).ImageOpened -= BitmapImageOpened;
|
var bitmap = (BitmapImage)sender;
|
||||||
((BitmapImage)sender).ImageFailed -= BitmapImageFailed;
|
|
||||||
|
bitmap.ImageOpened -= BitmapImageOpened;
|
||||||
|
bitmap.ImageFailed -= BitmapImageFailed;
|
||||||
|
|
||||||
Image.Source = null;
|
Image.Source = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,12 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
if (animateOpacity)
|
if (animateOpacity)
|
||||||
{
|
{
|
||||||
var bitmapImage = image as BitmapImage;
|
var bitmap = image as BitmapSource;
|
||||||
|
|
||||||
if (bitmapImage != null && bitmapImage.IsDownloading)
|
if (bitmap != null && !bitmap.IsFrozen && bitmap.IsDownloading)
|
||||||
{
|
{
|
||||||
bitmapImage.DownloadCompleted += BitmapDownloadCompleted;
|
bitmap.DownloadCompleted += BitmapDownloadCompleted;
|
||||||
bitmapImage.DownloadFailed += BitmapDownloadFailed;
|
bitmap.DownloadFailed += BitmapDownloadFailed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -43,20 +43,26 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
Brush.ImageSource = image;
|
Brush.ImageSource = image;
|
||||||
HasImage = true;
|
HasImageSource = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
|
var bitmap = (BitmapSource)sender;
|
||||||
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
|
|
||||||
|
bitmap.DownloadCompleted -= BitmapDownloadCompleted;
|
||||||
|
bitmap.DownloadFailed -= BitmapDownloadFailed;
|
||||||
|
|
||||||
Brush.BeginAnimation(ImageBrush.OpacityProperty, new DoubleAnimation(1d, AnimationDuration));
|
Brush.BeginAnimation(ImageBrush.OpacityProperty, new DoubleAnimation(1d, AnimationDuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
|
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
|
var bitmap = (BitmapSource)sender;
|
||||||
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
|
|
||||||
|
bitmap.DownloadCompleted -= BitmapDownloadCompleted;
|
||||||
|
bitmap.DownloadFailed -= BitmapDownloadFailed;
|
||||||
|
|
||||||
Brush.ImageSource = null;
|
Brush.ImageSource = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ namespace MapControl
|
||||||
Y = y;
|
Y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasImage { get; private set; }
|
public bool HasImageSource { get; private set; }
|
||||||
|
|
||||||
public int XIndex
|
public int XIndex
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,37 +2,50 @@
|
||||||
// Copyright © Clemens Fischer 2012-2013
|
// Copyright © Clemens Fischer 2012-2013
|
||||||
// Licensed under the Microsoft Public License (Ms-PL)
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Diagnostics;
|
||||||
#if NETFX_CORE
|
#if NETFX_CORE
|
||||||
|
using Windows.UI.Xaml.Media;
|
||||||
using Windows.UI.Xaml.Media.Imaging;
|
using Windows.UI.Xaml.Media.Imaging;
|
||||||
#else
|
#else
|
||||||
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads map tile images by their URIs.
|
/// Loads map tile images.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class TileImageLoader
|
internal class TileImageLoader
|
||||||
{
|
{
|
||||||
private readonly TileLayer tileLayer;
|
internal void BeginGetTiles(TileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||||
|
|
||||||
internal TileImageLoader(TileLayer tileLayer)
|
|
||||||
{
|
{
|
||||||
this.tileLayer = tileLayer;
|
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||||
}
|
|
||||||
|
|
||||||
internal void StartGetTiles(IEnumerable<Tile> tiles)
|
|
||||||
{
|
|
||||||
foreach (var tile in tiles)
|
foreach (var tile in tiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ImageSource image;
|
||||||
|
|
||||||
|
if (imageTileSource != null)
|
||||||
|
{
|
||||||
|
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||||
|
|
||||||
if (uri != null)
|
image = uri != null ? new BitmapImage(uri) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
tile.SetImageSource(image, tileLayer.AnimateTileOpacity);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
tile.SetImageSource(new BitmapImage(uri), tileLayer.AnimateTileOpacity);
|
Debug.WriteLine("Creating tile image failed: {0}", ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Cache;
|
|
||||||
using System.Runtime.Caching;
|
using System.Runtime.Caching;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
@ -19,18 +18,14 @@ using System.Windows.Threading;
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads map tile images by their URIs and optionally caches the images in an ObjectCache.
|
/// Loads map tile images and optionally caches them in a System.Runtime.Caching.ObjectCache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TileImageLoader
|
public class TileImageLoader
|
||||||
{
|
{
|
||||||
private readonly TileLayer tileLayer;
|
|
||||||
private readonly ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>();
|
|
||||||
private int downloadThreadCount;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default Name of an ObjectCache instance that is assigned to the Cache property.
|
/// Default Name of an ObjectCache instance that is assigned to the Cache property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string DefaultCacheName = "TileCache";
|
public const string DefaultCacheName = "TileCache";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default value for the directory where an ObjectCache instance may save cached data.
|
/// Default value for the directory where an ObjectCache instance may save cached data.
|
||||||
|
|
@ -39,44 +34,48 @@ namespace MapControl
|
||||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl");
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ObjectCache used to cache tile images.
|
/// The ObjectCache used to cache tile images. The default is null.
|
||||||
/// The default is System.Runtime.Caching.MemoryCache.Default.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ObjectCache Cache { get; set; }
|
public static ObjectCache Cache { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time interval after which cached images expire. The default value is 30 days.
|
/// The time interval after which cached images expire. The default value is seven days.
|
||||||
/// When an image is not retrieved from the cache during this interval it is considered
|
/// When an image is not retrieved from the cache during this interval it is considered as expired
|
||||||
/// as expired and will be removed from the cache. If an image is retrieved from the
|
/// and will be removed from the cache, provided that the cache implementation supports expiration.
|
||||||
/// cache and the CacheUpdateAge time interval has expired, the image is downloaded
|
/// If an image is retrieved from the cache and the CacheUpdateAge time interval has expired,
|
||||||
/// again and rewritten to the cache with a new expiration time.
|
/// the image is downloaded again and rewritten to the cache with a new expiration time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static TimeSpan CacheExpiration { get; set; }
|
public static TimeSpan CacheExpiration { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time interval after which a cached image is updated and rewritten to the cache.
|
/// The time interval after which a cached image is updated and rewritten to the cache.
|
||||||
/// The default value is one day. This time interval should be shorter than the value
|
/// The default value is one day. This time interval should not be greater than the value
|
||||||
/// of the CacheExpiration property.
|
/// of the CacheExpiration property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static TimeSpan CacheUpdateAge { get; set; }
|
public static TimeSpan CacheUpdateAge { get; set; }
|
||||||
|
|
||||||
static TileImageLoader()
|
static TileImageLoader()
|
||||||
{
|
{
|
||||||
Cache = MemoryCache.Default;
|
CacheExpiration = TimeSpan.FromDays(7);
|
||||||
CacheExpiration = TimeSpan.FromDays(30d);
|
CacheUpdateAge = TimeSpan.FromDays(1);
|
||||||
CacheUpdateAge = TimeSpan.FromDays(1d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal TileImageLoader(TileLayer tileLayer)
|
private readonly ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>();
|
||||||
{
|
private int threadCount;
|
||||||
this.tileLayer = tileLayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void StartGetTiles(IEnumerable<Tile> tiles)
|
internal void BeginGetTiles(TileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||||
{
|
{
|
||||||
if (tileLayer.TileSource != null && tiles.Any())
|
if (tiles.Any())
|
||||||
{
|
{
|
||||||
ThreadPool.QueueUserWorkItem(GetTilesAsync, tiles.ToList());
|
// get current TileLayer property values in UI thread
|
||||||
|
var dispatcher = tileLayer.Dispatcher;
|
||||||
|
var tileSource = tileLayer.TileSource;
|
||||||
|
var sourceName = tileLayer.SourceName;
|
||||||
|
var maxDownloads = tileLayer.MaxParallelDownloads;
|
||||||
|
var animateOpacity = tileLayer.AnimateTileOpacity;
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem(o =>
|
||||||
|
GetTiles(tiles.ToList(), dispatcher, tileSource, sourceName, maxDownloads, animateOpacity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,56 +85,65 @@ namespace MapControl
|
||||||
while (pendingTiles.TryDequeue(out tile)) ; // no Clear method
|
while (pendingTiles.TryDequeue(out tile)) ; // no Clear method
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCacheKey(Tile tile)
|
private void GetTiles(List<Tile> tiles, Dispatcher dispatcher, TileSource tileSource,
|
||||||
|
string sourceName, int maxDownloads, bool animateOpacity)
|
||||||
{
|
{
|
||||||
return string.Format("{0}/{1}/{2}/{3}", tileLayer.SourceName, tile.ZoomLevel, tile.XIndex, tile.Y);
|
var imageTileSource = tileSource as ImageTileSource;
|
||||||
}
|
|
||||||
|
|
||||||
private void GetTilesAsync(object tileList)
|
if (imageTileSource != null)
|
||||||
{
|
{
|
||||||
var tiles = (List<Tile>)tileList;
|
if (!imageTileSource.CanLoadAsync) // call LoadImage in UI thread
|
||||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
|
||||||
|
|
||||||
if (imageTileSource != null && !imageTileSource.CanLoadAsync)
|
|
||||||
{
|
{
|
||||||
foreach (var tile in tiles)
|
foreach (var tile in tiles)
|
||||||
{
|
{
|
||||||
tileLayer.Dispatcher.BeginInvoke(
|
dispatcher.BeginInvoke(
|
||||||
(Action<Tile, ImageTileSource>)((t, ts) => t.SetImageSource(ts.LoadImage(t.XIndex, t.Y, t.ZoomLevel), tileLayer.AnimateTileOpacity)),
|
(Action<Tile, ImageTileSource>)((t, ts) => t.SetImageSource(LoadImage(ts, t), animateOpacity)),
|
||||||
DispatcherPriority.Background, tile, imageTileSource);
|
DispatcherPriority.Background, tile, imageTileSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else if (!tileSource.UriFormat.StartsWith("file:")) // always load local image files asynchronously
|
||||||
{
|
{
|
||||||
if (imageTileSource == null && Cache != null &&
|
if (Cache == null || string.IsNullOrWhiteSpace(sourceName))
|
||||||
!string.IsNullOrWhiteSpace(tileLayer.SourceName) &&
|
|
||||||
!tileLayer.TileSource.UriFormat.StartsWith("file://"))
|
|
||||||
{
|
{
|
||||||
|
// no caching here: use default asynchronous downloading and caching done by WPF
|
||||||
|
|
||||||
|
foreach (var tile in tiles)
|
||||||
|
{
|
||||||
|
dispatcher.BeginInvoke(
|
||||||
|
(Action<Tile, TileSource>)((t, ts) => t.SetImageSource(CreateImage(ts, t), animateOpacity)),
|
||||||
|
DispatcherPriority.Background, tile, tileSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var outdatedTiles = new List<Tile>(tiles.Count);
|
var outdatedTiles = new List<Tile>(tiles.Count);
|
||||||
|
|
||||||
foreach (var tile in tiles)
|
foreach (var tile in tiles)
|
||||||
{
|
{
|
||||||
var key = GetCacheKey(tile);
|
var key = GetCacheKey(sourceName, tile);
|
||||||
var buffer = Cache.Get(key) as byte[];
|
var buffer = Cache.Get(key) as byte[];
|
||||||
var image = CreateImage(buffer);
|
var image = CreateImage(buffer);
|
||||||
|
|
||||||
if (image != null)
|
if (image != null)
|
||||||
{
|
{
|
||||||
tileLayer.Dispatcher.BeginInvoke(
|
dispatcher.BeginInvoke(
|
||||||
(Action<Tile, ImageSource>)((t, i) => t.SetImageSource(i, tileLayer.AnimateTileOpacity)),
|
(Action<Tile, ImageSource>)((t, i) => t.SetImageSource(i, animateOpacity)),
|
||||||
DispatcherPriority.Background, tile, image);
|
DispatcherPriority.Background, tile, image);
|
||||||
|
|
||||||
long creationTime = BitConverter.ToInt64(buffer, 0);
|
long creationTime = BitConverter.ToInt64(buffer, 0);
|
||||||
|
|
||||||
if (DateTime.FromBinary(creationTime) + CacheUpdateAge < DateTime.UtcNow)
|
if (DateTime.FromBinary(creationTime) + CacheUpdateAge < DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
// update outdated cache
|
outdatedTiles.Add(tile); // update outdated cache
|
||||||
outdatedTiles.Add(tile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pendingTiles.Enqueue(tile);
|
pendingTiles.Enqueue(tile); // not yet cached
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,18 +155,17 @@ namespace MapControl
|
||||||
pendingTiles.Enqueue(tile);
|
pendingTiles.Enqueue(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (downloadThreadCount < Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads))
|
while (threadCount < Math.Min(pendingTiles.Count, maxDownloads))
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref downloadThreadCount);
|
Interlocked.Increment(ref threadCount);
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(LoadTiles);
|
ThreadPool.QueueUserWorkItem(o => LoadPendingTiles(dispatcher, tileSource, sourceName, animateOpacity));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadTiles(object o)
|
private void LoadPendingTiles(Dispatcher dispatcher, TileSource tileSource, string sourceName, bool animateOpacity)
|
||||||
{
|
{
|
||||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
var imageTileSource = tileSource as ImageTileSource;
|
||||||
Tile tile;
|
Tile tile;
|
||||||
|
|
||||||
while (pendingTiles.TryDequeue(out tile))
|
while (pendingTiles.TryDequeue(out tile))
|
||||||
|
|
@ -168,65 +175,96 @@ namespace MapControl
|
||||||
|
|
||||||
if (imageTileSource != null)
|
if (imageTileSource != null)
|
||||||
{
|
{
|
||||||
|
image = LoadImage(imageTileSource, tile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||||
|
|
||||||
|
if (uri != null)
|
||||||
|
{
|
||||||
|
if (uri.Scheme == "file")
|
||||||
|
{
|
||||||
|
image = CreateImage(uri.AbsolutePath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer = DownloadImage(uri);
|
||||||
|
image = CreateImage(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image != null || !tile.HasImageSource) // do not set null if tile already has an image (from cache)
|
||||||
|
{
|
||||||
|
dispatcher.BeginInvoke(
|
||||||
|
(Action<Tile, ImageSource>)((t, i) => t.SetImageSource(i, animateOpacity)),
|
||||||
|
DispatcherPriority.Background, tile, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer != null && image != null)
|
||||||
|
{
|
||||||
|
Cache.Set(GetCacheKey(sourceName, tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Interlocked.Decrement(ref threadCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCacheKey(string sourceName, Tile tile)
|
||||||
|
{
|
||||||
|
return string.Format("{0}/{1}/{2}/{3}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImageSource LoadImage(ImageTileSource tileSource, Tile tile)
|
||||||
|
{
|
||||||
|
ImageSource image = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
image = tileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.TraceWarning("Loading tile image failed: {0}", ex.Message);
|
Trace.TraceWarning("Loading tile image failed: {0}", ex.Message);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
|
||||||
|
|
||||||
if (uri != null)
|
return image;
|
||||||
{
|
|
||||||
if (uri.Scheme == "http")
|
|
||||||
{
|
|
||||||
buffer = DownloadImage(uri);
|
|
||||||
image = CreateImage(buffer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
image = CreateImage(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image != null)
|
private static ImageSource CreateImage(TileSource tileSource, Tile tile)
|
||||||
{
|
{
|
||||||
tileLayer.Dispatcher.BeginInvoke(
|
ImageSource image = null;
|
||||||
(Action<Tile, ImageSource>)((t, i) => t.SetImageSource(i, tileLayer.AnimateTileOpacity)),
|
|
||||||
DispatcherPriority.Background, tile, image);
|
|
||||||
|
|
||||||
if (buffer != null && Cache != null)
|
|
||||||
{
|
|
||||||
Cache.Set(GetCacheKey(tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Interlocked.Decrement(ref downloadThreadCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ImageSource CreateImage(Uri uri)
|
|
||||||
{
|
|
||||||
var image = new BitmapImage();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
image.BeginInit();
|
image = BitmapFrame.Create(tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel));
|
||||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
|
||||||
image.UriSource = uri;
|
|
||||||
image.EndInit();
|
|
||||||
image.Freeze();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.TraceWarning("Creating tile image failed: {0}", ex.Message);
|
Trace.TraceWarning("Creating tile image failed: {0}", ex.Message);
|
||||||
image = null;
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImageSource CreateImage(string path)
|
||||||
|
{
|
||||||
|
ImageSource image = null;
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var stream = new FileStream(path, FileMode.Open))
|
||||||
|
{
|
||||||
|
image = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.TraceWarning("Creating tile image failed: {0}", ex.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
|
|
@ -234,7 +272,7 @@ namespace MapControl
|
||||||
|
|
||||||
private static ImageSource CreateImage(byte[] buffer)
|
private static ImageSource CreateImage(byte[] buffer)
|
||||||
{
|
{
|
||||||
BitmapImage image = null;
|
ImageSource image = null;
|
||||||
|
|
||||||
if (buffer != null && buffer.Length > sizeof(long))
|
if (buffer != null && buffer.Length > sizeof(long))
|
||||||
{
|
{
|
||||||
|
|
@ -242,18 +280,12 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
using (var stream = new MemoryStream(buffer, sizeof(long), buffer.Length - sizeof(long), false))
|
using (var stream = new MemoryStream(buffer, sizeof(long), buffer.Length - sizeof(long), false))
|
||||||
{
|
{
|
||||||
image = new BitmapImage();
|
image = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||||
image.BeginInit();
|
|
||||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
|
||||||
image.StreamSource = stream;
|
|
||||||
image.EndInit();
|
|
||||||
image.Freeze();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.TraceWarning("Creating tile image failed: {0}", ex.Message);
|
Trace.TraceWarning("Creating tile image failed: {0}", ex.Message);
|
||||||
image = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,11 +301,6 @@ namespace MapControl
|
||||||
var request = (HttpWebRequest)WebRequest.Create(uri);
|
var request = (HttpWebRequest)WebRequest.Create(uri);
|
||||||
request.UserAgent = "XAML Map Control";
|
request.UserAgent = "XAML Map Control";
|
||||||
|
|
||||||
if (Cache != null)
|
|
||||||
{
|
|
||||||
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var response = (HttpWebResponse)request.GetResponse())
|
using (var response = (HttpWebResponse)request.GetResponse())
|
||||||
using (var responseStream = response.GetResponseStream())
|
using (var responseStream = response.GetResponseStream())
|
||||||
{
|
{
|
||||||
|
|
@ -289,8 +316,6 @@ namespace MapControl
|
||||||
buffer = length > 0 ? memoryStream.GetBuffer() : memoryStream.ToArray();
|
buffer = length > 0 ? memoryStream.GetBuffer() : memoryStream.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Trace.TraceInformation("Downloaded {0}", uri);
|
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
catch (WebException ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly MatrixTransform transform = new MatrixTransform();
|
private readonly MatrixTransform transform = new MatrixTransform();
|
||||||
private readonly TileImageLoader tileImageLoader;
|
private readonly TileImageLoader tileImageLoader = new TileImageLoader();
|
||||||
private List<Tile> tiles = new List<Tile>();
|
private List<Tile> tiles = new List<Tile>();
|
||||||
private string description = string.Empty;
|
private string description = string.Empty;
|
||||||
private Int32Rect grid;
|
private Int32Rect grid;
|
||||||
|
|
@ -48,7 +48,6 @@ namespace MapControl
|
||||||
|
|
||||||
public TileLayer()
|
public TileLayer()
|
||||||
{
|
{
|
||||||
tileImageLoader = new TileImageLoader(this);
|
|
||||||
MinZoomLevel = 1;
|
MinZoomLevel = 1;
|
||||||
MaxZoomLevel = 18;
|
MaxZoomLevel = 18;
|
||||||
MaxParallelDownloads = 8;
|
MaxParallelDownloads = 8;
|
||||||
|
|
@ -96,7 +95,7 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
SelectTiles();
|
SelectTiles();
|
||||||
RenderTiles();
|
RenderTiles();
|
||||||
tileImageLoader.StartGetTiles(tiles.Where(t => !t.HasImage));
|
tileImageLoader.BeginGetTiles(this, tiles.Where(t => !t.HasImageSource));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ namespace MapControl
|
||||||
|
|
||||||
private Uri GetOpenStreetMapUri(int x, int y, int zoomLevel)
|
private Uri GetOpenStreetMapUri(int x, int y, int zoomLevel)
|
||||||
{
|
{
|
||||||
var hostIndex = (x + y + zoomLevel) % 3;
|
var hostIndex = (x + y) % 3;
|
||||||
|
|
||||||
return new Uri(UriFormat.
|
return new Uri(UriFormat.
|
||||||
Replace("{c}", "abc".Substring(hostIndex, 1)).
|
Replace("{c}", "abc".Substring(hostIndex, 1)).
|
||||||
|
|
@ -113,7 +113,7 @@ namespace MapControl
|
||||||
|
|
||||||
private Uri GetGoogleMapsUri(int x, int y, int zoomLevel)
|
private Uri GetGoogleMapsUri(int x, int y, int zoomLevel)
|
||||||
{
|
{
|
||||||
var hostIndex = (x + y + zoomLevel) % 4;
|
var hostIndex = (x + y) % 4;
|
||||||
|
|
||||||
return new Uri(UriFormat.
|
return new Uri(UriFormat.
|
||||||
Replace("{i}", hostIndex.ToString()).
|
Replace("{i}", hostIndex.ToString()).
|
||||||
|
|
@ -124,7 +124,7 @@ namespace MapControl
|
||||||
|
|
||||||
private Uri GetMapQuestUri(int x, int y, int zoomLevel)
|
private Uri GetMapQuestUri(int x, int y, int zoomLevel)
|
||||||
{
|
{
|
||||||
var hostIndex = (x + y + zoomLevel) % 4 + 1;
|
var hostIndex = (x + y) % 4 + 1;
|
||||||
|
|
||||||
return new Uri(UriFormat.
|
return new Uri(UriFormat.
|
||||||
Replace("{n}", hostIndex.ToString()).
|
Replace("{n}", hostIndex.ToString()).
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@
|
||||||
<Compile Include="..\AnimationEx.WinRT.cs">
|
<Compile Include="..\AnimationEx.WinRT.cs">
|
||||||
<Link>AnimationEx.WinRT.cs</Link>
|
<Link>AnimationEx.WinRT.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\Freezable.cs">
|
<Compile Include="..\ImageTileSource.cs">
|
||||||
<Link>Freezable.cs</Link>
|
<Link>ImageTileSource.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\Int32Rect.cs">
|
<Compile Include="..\Int32Rect.cs">
|
||||||
<Link>Int32Rect.cs</Link>
|
<Link>Int32Rect.cs</Link>
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Runtime.Caching;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
@ -22,6 +23,9 @@ namespace WpfApplication
|
||||||
{
|
{
|
||||||
switch (Properties.Settings.Default.TileCache)
|
switch (Properties.Settings.Default.TileCache)
|
||||||
{
|
{
|
||||||
|
case "MemoryCache":
|
||||||
|
TileImageLoader.Cache = MemoryCache.Default;
|
||||||
|
break;
|
||||||
case "FileDbCache":
|
case "FileDbCache":
|
||||||
TileImageLoader.Cache = new FileDbCache(TileImageLoader.DefaultCacheName, TileImageLoader.DefaultCacheDirectory);
|
TileImageLoader.Cache = new FileDbCache(TileImageLoader.DefaultCacheName, TileImageLoader.DefaultCacheDirectory);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("1.7.0")]
|
[assembly: AssemblyVersion("1.8.0")]
|
||||||
[assembly: AssemblyFileVersion("1.7.0")]
|
[assembly: AssemblyFileVersion("1.8.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue