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: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -16,12 +16,22 @@ namespace Caching
|
|||
{
|
||||
/// <summary>
|
||||
/// ObjectCache implementation based on local image files.
|
||||
/// The only valid data type for cached values is a byte[], which contains
|
||||
/// an 8-byte binary UTC time (as created by DateTime.ToBinary), followed
|
||||
/// by a PNG, JPEG, BMP, GIF, TIFF or WMP image buffer.
|
||||
/// The only valid data type for cached values is a byte array containing an
|
||||
/// 8-byte timestamp followed by a PNG, JPEG, BMP, GIF, TIFF or WMP image buffer.
|
||||
/// </summary>
|
||||
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(
|
||||
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
|
||||
FileSystemRights.FullControl, AccessControlType.Allow);
|
||||
|
|
@ -169,36 +179,31 @@ namespace Caching
|
|||
|
||||
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);
|
||||
|
||||
var extension = GetFileExtension(buffer);
|
||||
var path = GetPath(key) + GetFileExtension(buffer);
|
||||
|
||||
if (extension != null)
|
||||
try
|
||||
{
|
||||
var path = GetPath(key) + extension;
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
try
|
||||
using (var fileStream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
using (var fileStream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
fileStream.Write(buffer, 8, buffer.Length - 8);
|
||||
}
|
||||
|
||||
var fileSecurity = File.GetAccessControl(path);
|
||||
fileSecurity.AddAccessRule(fullControlRule);
|
||||
File.SetAccessControl(path, fileSecurity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceWarning("ImageFileCache: Writing file {0} failed: {1}", path, ex.Message);
|
||||
fileStream.Write(buffer, 8, buffer.Length - 8);
|
||||
}
|
||||
|
||||
var fileSecurity = File.GetAccessControl(path);
|
||||
fileSecurity.AddAccessRule(fullControlRule);
|
||||
File.SetAccessControl(path, fileSecurity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceWarning("ImageFileCache: Writing file {0} failed: {1}", path, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -274,42 +279,24 @@ namespace Caching
|
|||
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)
|
||||
{
|
||||
string extension = null;
|
||||
var creationTime = DateTime.FromBinary(BitConverter.ToInt64(buffer, 0));
|
||||
|
||||
if (creationTime.Kind == DateTimeKind.Utc && creationTime <= DateTime.UtcNow)
|
||||
var fileType = imageFileTypes.FirstOrDefault(t =>
|
||||
{
|
||||
Func<Tuple<string, byte[]>, bool> match =
|
||||
t =>
|
||||
int i = 0;
|
||||
|
||||
if (t.Item2.Length <= buffer.Length - 8)
|
||||
{
|
||||
while (i < t.Item2.Length && t.Item2[i] == buffer[i + 8])
|
||||
{
|
||||
int i = 0;
|
||||
if (t.Item2.Length + 8 <= buffer.Length)
|
||||
{
|
||||
while (i < t.Item2.Length && t.Item2[i] == buffer[i + 8])
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return i == t.Item2.Length;
|
||||
};
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
extension = fileTypes.Where(match).Select(t => t.Item1).FirstOrDefault();
|
||||
}
|
||||
return i == t.Item2.Length;
|
||||
});
|
||||
|
||||
return extension;
|
||||
return fileType != null ? fileType.Item1 : ".bin";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[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
|
||||
// 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.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the image of a map tile. ImageTileSource bypasses download and
|
||||
/// cache processing in TileImageLoader. By overriding the LoadImage method,
|
||||
/// Provides the image of a map tile. ImageTileSource bypasses downloading and
|
||||
/// optional caching in TileImageLoader. By overriding the LoadImage method,
|
||||
/// 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.
|
||||
/// </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)
|
||||
{
|
||||
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>
|
||||
<Compile Include="AnimationEx.Silverlight.cs" />
|
||||
<Compile Include="Freezable.cs" />
|
||||
<Compile Include="ImageTileSource.cs" />
|
||||
<Compile Include="Int32Rect.cs" />
|
||||
<Compile Include="Location.cs" />
|
||||
<Compile Include="LocationCollection.cs" />
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="GlyphRunText.cs" />
|
||||
<Compile Include="ImageTileSource.cs" />
|
||||
<Compile Include="ImageTileSource.WPF.cs" />
|
||||
<Compile Include="Location.cs" />
|
||||
<Compile Include="LocationCollection.cs" />
|
||||
<Compile Include="LocationCollectionConverter.cs" />
|
||||
|
|
@ -63,6 +64,7 @@
|
|||
<Compile Include="MapGraticule.cs" />
|
||||
<Compile Include="MapGraticule.WPF.cs" />
|
||||
<Compile Include="MapImage.cs" />
|
||||
<Compile Include="MapImage.WPF.cs" />
|
||||
<Compile Include="MapImageLayer.cs" />
|
||||
<Compile Include="MapItem.WPF.cs" />
|
||||
<Compile Include="MapItemsControl.WPF.cs" />
|
||||
|
|
@ -74,6 +76,7 @@
|
|||
<Compile Include="MapPolyline.cs" />
|
||||
<Compile Include="MapPolyline.WPF.cs" />
|
||||
<Compile Include="MapRectangle.cs" />
|
||||
<Compile Include="MapRectangle.WPF.cs" />
|
||||
<Compile Include="MapScale.cs" />
|
||||
<Compile Include="MapTransform.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>
|
||||
/// Fills a rectangular area with an ImageBrush from the Source property.
|
||||
/// </summary>
|
||||
public class MapImage : MapRectangle
|
||||
public partial class MapImage : MapRectangle
|
||||
{
|
||||
private static readonly MatrixTransform imageTransform = new MatrixTransform
|
||||
{
|
||||
Matrix = new Matrix(1d, 0d, 0d, -1d, 0d, 1d)
|
||||
};
|
||||
|
||||
static MapImage()
|
||||
{
|
||||
imageTransform.Freeze();
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
|
||||
"Source", typeof(ImageSource), typeof(MapImage),
|
||||
new PropertyMetadata(null, (o, e) => ((MapImage)o).SourceChanged((ImageSource)e.NewValue)));
|
||||
|
|
@ -45,7 +40,6 @@ namespace MapControl
|
|||
RelativeTransform = imageTransform
|
||||
};
|
||||
|
||||
imageBrush.Freeze();
|
||||
Fill = imageBrush;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,6 @@ namespace MapControl
|
|||
|
||||
try
|
||||
{
|
||||
var bitmap = new BitmapImage();
|
||||
var request = (HttpWebRequest)WebRequest.Create(uri);
|
||||
request.UserAgent = "XAML Map Control";
|
||||
|
||||
|
|
@ -128,15 +127,8 @@ namespace MapControl
|
|||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
responseStream.CopyTo(memoryStream);
|
||||
|
||||
bitmap.BeginInit();
|
||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmap.StreamSource = memoryStream;
|
||||
bitmap.EndInit();
|
||||
bitmap.Freeze();
|
||||
image = BitmapFrame.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
}
|
||||
|
||||
image = bitmap;
|
||||
}
|
||||
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>
|
||||
/// Fills a rectangular area defined by South, North, West and East with a Brush.
|
||||
/// </summary>
|
||||
public class MapRectangle : MapPath
|
||||
public partial class MapRectangle : MapPath
|
||||
{
|
||||
private const double geometryScale = 1e6;
|
||||
|
||||
|
|
@ -26,11 +26,6 @@ namespace MapControl
|
|||
ScaleY = 1d / geometryScale
|
||||
};
|
||||
|
||||
static MapRectangle()
|
||||
{
|
||||
geometryScaleTransform.Freeze();
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SouthProperty = DependencyProperty.Register(
|
||||
"South", typeof(double), typeof(MapRectangle),
|
||||
new PropertyMetadata(double.NaN, (o, e) => ((MapRectangle)o).UpdateData()));
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ using System.Windows;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ namespace MapControl
|
|||
{
|
||||
if (animateOpacity)
|
||||
{
|
||||
var bitmapImage = image as BitmapImage;
|
||||
var bitmap = image as BitmapImage;
|
||||
|
||||
if (bitmapImage != null)
|
||||
if (bitmap != null)
|
||||
{
|
||||
bitmapImage.ImageOpened += BitmapImageOpened;
|
||||
bitmapImage.ImageFailed += BitmapImageFailed;
|
||||
bitmap.ImageOpened += BitmapImageOpened;
|
||||
bitmap.ImageFailed += BitmapImageFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -52,20 +52,26 @@ namespace MapControl
|
|||
}
|
||||
|
||||
Image.Source = image;
|
||||
HasImage = true;
|
||||
HasImageSource = true;
|
||||
}
|
||||
|
||||
private void BitmapImageOpened(object sender, RoutedEventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).ImageOpened -= BitmapImageOpened;
|
||||
((BitmapImage)sender).ImageFailed -= BitmapImageFailed;
|
||||
var bitmap = (BitmapImage)sender;
|
||||
|
||||
bitmap.ImageOpened -= BitmapImageOpened;
|
||||
bitmap.ImageFailed -= BitmapImageFailed;
|
||||
|
||||
Image.BeginAnimation(Image.OpacityProperty, new DoubleAnimation { To = 1d, Duration = AnimationDuration });
|
||||
}
|
||||
|
||||
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).ImageOpened -= BitmapImageOpened;
|
||||
((BitmapImage)sender).ImageFailed -= BitmapImageFailed;
|
||||
var bitmap = (BitmapImage)sender;
|
||||
|
||||
bitmap.ImageOpened -= BitmapImageOpened;
|
||||
bitmap.ImageFailed -= BitmapImageFailed;
|
||||
|
||||
Image.Source = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ namespace MapControl
|
|||
{
|
||||
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;
|
||||
bitmapImage.DownloadFailed += BitmapDownloadFailed;
|
||||
bitmap.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed += BitmapDownloadFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -43,20 +43,26 @@ namespace MapControl
|
|||
}
|
||||
|
||||
Brush.ImageSource = image;
|
||||
HasImage = true;
|
||||
HasImageSource = true;
|
||||
}
|
||||
|
||||
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
|
||||
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
|
||||
var bitmap = (BitmapSource)sender;
|
||||
|
||||
bitmap.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed -= BitmapDownloadFailed;
|
||||
|
||||
Brush.BeginAnimation(ImageBrush.OpacityProperty, new DoubleAnimation(1d, AnimationDuration));
|
||||
}
|
||||
|
||||
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
|
||||
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
|
||||
var bitmap = (BitmapSource)sender;
|
||||
|
||||
bitmap.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed -= BitmapDownloadFailed;
|
||||
|
||||
Brush.ImageSource = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace MapControl
|
|||
Y = y;
|
||||
}
|
||||
|
||||
public bool HasImage { get; private set; }
|
||||
public bool HasImageSource { get; private set; }
|
||||
|
||||
public int XIndex
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,37 +2,50 @@
|
|||
// Copyright © Clemens Fischer 2012-2013
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
#if NETFX_CORE
|
||||
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 by their URIs.
|
||||
/// Loads map tile images.
|
||||
/// </summary>
|
||||
internal class TileImageLoader
|
||||
{
|
||||
private readonly TileLayer tileLayer;
|
||||
|
||||
internal TileImageLoader(TileLayer tileLayer)
|
||||
internal void BeginGetTiles(TileLayer tileLayer, IEnumerable<Tile> tiles)
|
||||
{
|
||||
this.tileLayer = tileLayer;
|
||||
}
|
||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||
|
||||
internal void StartGetTiles(IEnumerable<Tile> tiles)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
try
|
||||
{
|
||||
tile.SetImageSource(new BitmapImage(uri), tileLayer.AnimateTileOpacity);
|
||||
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);
|
||||
|
||||
image = uri != null ? new BitmapImage(uri) : null;
|
||||
}
|
||||
|
||||
tile.SetImageSource(image, tileLayer.AnimateTileOpacity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Creating tile image failed: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Cache;
|
||||
using System.Runtime.Caching;
|
||||
using System.Threading;
|
||||
using System.Windows.Media;
|
||||
|
|
@ -19,18 +18,14 @@ using System.Windows.Threading;
|
|||
namespace MapControl
|
||||
{
|
||||
/// <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>
|
||||
public class TileImageLoader
|
||||
{
|
||||
private readonly TileLayer tileLayer;
|
||||
private readonly ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>();
|
||||
private int downloadThreadCount;
|
||||
|
||||
/// <summary>
|
||||
/// Default Name of an ObjectCache instance that is assigned to the Cache property.
|
||||
/// </summary>
|
||||
public static readonly string DefaultCacheName = "TileCache";
|
||||
public const string DefaultCacheName = "TileCache";
|
||||
|
||||
/// <summary>
|
||||
/// 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");
|
||||
|
||||
/// <summary>
|
||||
/// The ObjectCache used to cache tile images.
|
||||
/// The default is System.Runtime.Caching.MemoryCache.Default.
|
||||
/// The ObjectCache used to cache tile images. The default is null.
|
||||
/// </summary>
|
||||
public static ObjectCache Cache { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time interval after which cached images expire. The default value is 30 days.
|
||||
/// When an image is not retrieved from the cache during this interval it is considered
|
||||
/// as expired and will be removed from the cache. If an image is retrieved from the
|
||||
/// cache and the CacheUpdateAge time interval has expired, the image is downloaded
|
||||
/// again and rewritten to the cache with a new expiration time.
|
||||
/// 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 as expired
|
||||
/// and will be removed from the cache, provided that the cache implementation supports expiration.
|
||||
/// If an image is retrieved from the cache and the CacheUpdateAge time interval has expired,
|
||||
/// the image is downloaded again and rewritten to the cache with a new expiration time.
|
||||
/// </summary>
|
||||
public static TimeSpan CacheExpiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static TimeSpan CacheUpdateAge { get; set; }
|
||||
|
||||
static TileImageLoader()
|
||||
{
|
||||
Cache = MemoryCache.Default;
|
||||
CacheExpiration = TimeSpan.FromDays(30d);
|
||||
CacheUpdateAge = TimeSpan.FromDays(1d);
|
||||
CacheExpiration = TimeSpan.FromDays(7);
|
||||
CacheUpdateAge = TimeSpan.FromDays(1);
|
||||
}
|
||||
|
||||
internal TileImageLoader(TileLayer tileLayer)
|
||||
{
|
||||
this.tileLayer = tileLayer;
|
||||
}
|
||||
private readonly ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>();
|
||||
private int threadCount;
|
||||
|
||||
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,79 +85,87 @@ namespace MapControl
|
|||
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)
|
||||
{
|
||||
var tiles = (List<Tile>)tileList;
|
||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||
|
||||
if (imageTileSource != null && !imageTileSource.CanLoadAsync)
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
if (!imageTileSource.CanLoadAsync) // call LoadImage in UI thread
|
||||
{
|
||||
tileLayer.Dispatcher.BeginInvoke(
|
||||
(Action<Tile, ImageTileSource>)((t, ts) => t.SetImageSource(ts.LoadImage(t.XIndex, t.Y, t.ZoomLevel), tileLayer.AnimateTileOpacity)),
|
||||
DispatcherPriority.Background, tile, imageTileSource);
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
dispatcher.BeginInvoke(
|
||||
(Action<Tile, ImageTileSource>)((t, ts) => t.SetImageSource(LoadImage(ts, t), animateOpacity)),
|
||||
DispatcherPriority.Background, tile, imageTileSource);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!tileSource.UriFormat.StartsWith("file:")) // always load local image files asynchronously
|
||||
{
|
||||
if (imageTileSource == null && Cache != null &&
|
||||
!string.IsNullOrWhiteSpace(tileLayer.SourceName) &&
|
||||
!tileLayer.TileSource.UriFormat.StartsWith("file://"))
|
||||
if (Cache == null || string.IsNullOrWhiteSpace(sourceName))
|
||||
{
|
||||
var outdatedTiles = new List<Tile>(tiles.Count);
|
||||
// no caching here: use default asynchronous downloading and caching done by WPF
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
var key = GetCacheKey(tile);
|
||||
var buffer = Cache.Get(key) as byte[];
|
||||
var image = CreateImage(buffer);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
tileLayer.Dispatcher.BeginInvoke(
|
||||
(Action<Tile, ImageSource>)((t, i) => t.SetImageSource(i, tileLayer.AnimateTileOpacity)),
|
||||
DispatcherPriority.Background, tile, image);
|
||||
|
||||
long creationTime = BitConverter.ToInt64(buffer, 0);
|
||||
|
||||
if (DateTime.FromBinary(creationTime) + CacheUpdateAge < DateTime.UtcNow)
|
||||
{
|
||||
// update outdated cache
|
||||
outdatedTiles.Add(tile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingTiles.Enqueue(tile);
|
||||
}
|
||||
dispatcher.BeginInvoke(
|
||||
(Action<Tile, TileSource>)((t, ts) => t.SetImageSource(CreateImage(ts, t), animateOpacity)),
|
||||
DispatcherPriority.Background, tile, tileSource);
|
||||
}
|
||||
|
||||
tiles = outdatedTiles; // enqueue outdated tiles at last
|
||||
return;
|
||||
}
|
||||
|
||||
var outdatedTiles = new List<Tile>(tiles.Count);
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
pendingTiles.Enqueue(tile);
|
||||
var key = GetCacheKey(sourceName, tile);
|
||||
var buffer = Cache.Get(key) as byte[];
|
||||
var image = CreateImage(buffer);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
dispatcher.BeginInvoke(
|
||||
(Action<Tile, ImageSource>)((t, i) => t.SetImageSource(i, animateOpacity)),
|
||||
DispatcherPriority.Background, tile, image);
|
||||
|
||||
long creationTime = BitConverter.ToInt64(buffer, 0);
|
||||
|
||||
if (DateTime.FromBinary(creationTime) + CacheUpdateAge < DateTime.UtcNow)
|
||||
{
|
||||
outdatedTiles.Add(tile); // update outdated cache
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingTiles.Enqueue(tile); // not yet cached
|
||||
}
|
||||
}
|
||||
|
||||
while (downloadThreadCount < Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads))
|
||||
{
|
||||
Interlocked.Increment(ref downloadThreadCount);
|
||||
tiles = outdatedTiles; // enqueue outdated tiles at last
|
||||
}
|
||||
|
||||
ThreadPool.QueueUserWorkItem(LoadTiles);
|
||||
}
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
pendingTiles.Enqueue(tile);
|
||||
}
|
||||
|
||||
while (threadCount < Math.Min(pendingTiles.Count, maxDownloads))
|
||||
{
|
||||
Interlocked.Increment(ref threadCount);
|
||||
|
||||
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;
|
||||
|
||||
while (pendingTiles.TryDequeue(out tile))
|
||||
|
|
@ -168,65 +175,96 @@ namespace MapControl
|
|||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceWarning("Loading tile image failed: {0}", ex.Message);
|
||||
}
|
||||
image = LoadImage(imageTileSource, tile);
|
||||
}
|
||||
else
|
||||
{
|
||||
var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
if (uri.Scheme == "http")
|
||||
if (uri.Scheme == "file")
|
||||
{
|
||||
image = CreateImage(uri.AbsolutePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = DownloadImage(uri);
|
||||
image = CreateImage(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = CreateImage(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
if (image != null || !tile.HasImageSource) // do not set null if tile already has an image (from cache)
|
||||
{
|
||||
tileLayer.Dispatcher.BeginInvoke(
|
||||
(Action<Tile, ImageSource>)((t, i) => t.SetImageSource(i, tileLayer.AnimateTileOpacity)),
|
||||
dispatcher.BeginInvoke(
|
||||
(Action<Tile, ImageSource>)((t, i) => t.SetImageSource(i, animateOpacity)),
|
||||
DispatcherPriority.Background, tile, image);
|
||||
}
|
||||
|
||||
if (buffer != null && Cache != null)
|
||||
{
|
||||
Cache.Set(GetCacheKey(tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration });
|
||||
}
|
||||
if (buffer != null && image != null)
|
||||
{
|
||||
Cache.Set(GetCacheKey(sourceName, tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration });
|
||||
}
|
||||
}
|
||||
|
||||
Interlocked.Decrement(ref downloadThreadCount);
|
||||
Interlocked.Decrement(ref threadCount);
|
||||
}
|
||||
|
||||
private static ImageSource CreateImage(Uri uri)
|
||||
private static string GetCacheKey(string sourceName, Tile tile)
|
||||
{
|
||||
var image = new BitmapImage();
|
||||
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
|
||||
{
|
||||
image.BeginInit();
|
||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||
image.UriSource = uri;
|
||||
image.EndInit();
|
||||
image.Freeze();
|
||||
image = tileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceWarning("Loading tile image failed: {0}", ex.Message);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static ImageSource CreateImage(TileSource tileSource, Tile tile)
|
||||
{
|
||||
ImageSource image = null;
|
||||
|
||||
try
|
||||
{
|
||||
image = BitmapFrame.Create(tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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;
|
||||
|
|
@ -234,7 +272,7 @@ namespace MapControl
|
|||
|
||||
private static ImageSource CreateImage(byte[] buffer)
|
||||
{
|
||||
BitmapImage image = null;
|
||||
ImageSource image = null;
|
||||
|
||||
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))
|
||||
{
|
||||
image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||
image.StreamSource = stream;
|
||||
image.EndInit();
|
||||
image.Freeze();
|
||||
image = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.TraceWarning("Creating tile image failed: {0}", ex.Message);
|
||||
image = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -269,11 +301,6 @@ namespace MapControl
|
|||
var request = (HttpWebRequest)WebRequest.Create(uri);
|
||||
request.UserAgent = "XAML Map Control";
|
||||
|
||||
if (Cache != null)
|
||||
{
|
||||
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
|
||||
}
|
||||
|
||||
using (var response = (HttpWebResponse)request.GetResponse())
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
{
|
||||
|
|
@ -289,8 +316,6 @@ namespace MapControl
|
|||
buffer = length > 0 ? memoryStream.GetBuffer() : memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
//Trace.TraceInformation("Downloaded {0}", uri);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
private readonly MatrixTransform transform = new MatrixTransform();
|
||||
private readonly TileImageLoader tileImageLoader;
|
||||
private readonly TileImageLoader tileImageLoader = new TileImageLoader();
|
||||
private List<Tile> tiles = new List<Tile>();
|
||||
private string description = string.Empty;
|
||||
private Int32Rect grid;
|
||||
|
|
@ -48,7 +48,6 @@ namespace MapControl
|
|||
|
||||
public TileLayer()
|
||||
{
|
||||
tileImageLoader = new TileImageLoader(this);
|
||||
MinZoomLevel = 1;
|
||||
MaxZoomLevel = 18;
|
||||
MaxParallelDownloads = 8;
|
||||
|
|
@ -96,7 +95,7 @@ namespace MapControl
|
|||
{
|
||||
SelectTiles();
|
||||
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)
|
||||
{
|
||||
var hostIndex = (x + y + zoomLevel) % 3;
|
||||
var hostIndex = (x + y) % 3;
|
||||
|
||||
return new Uri(UriFormat.
|
||||
Replace("{c}", "abc".Substring(hostIndex, 1)).
|
||||
|
|
@ -113,7 +113,7 @@ namespace MapControl
|
|||
|
||||
private Uri GetGoogleMapsUri(int x, int y, int zoomLevel)
|
||||
{
|
||||
var hostIndex = (x + y + zoomLevel) % 4;
|
||||
var hostIndex = (x + y) % 4;
|
||||
|
||||
return new Uri(UriFormat.
|
||||
Replace("{i}", hostIndex.ToString()).
|
||||
|
|
@ -124,7 +124,7 @@ namespace MapControl
|
|||
|
||||
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.
|
||||
Replace("{n}", hostIndex.ToString()).
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@
|
|||
<Compile Include="..\AnimationEx.WinRT.cs">
|
||||
<Link>AnimationEx.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Freezable.cs">
|
||||
<Link>Freezable.cs</Link>
|
||||
<Compile Include="..\ImageTileSource.cs">
|
||||
<Link>ImageTileSource.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Int32Rect.cs">
|
||||
<Link>Int32Rect.cs</Link>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Caching;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
|
@ -22,6 +23,9 @@ namespace WpfApplication
|
|||
{
|
||||
switch (Properties.Settings.Default.TileCache)
|
||||
{
|
||||
case "MemoryCache":
|
||||
TileImageLoader.Cache = MemoryCache.Default;
|
||||
break;
|
||||
case "FileDbCache":
|
||||
TileImageLoader.Cache = new FileDbCache(TileImageLoader.DefaultCacheName, TileImageLoader.DefaultCacheDirectory);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.8.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue