Version 2.7.0.

- WPF TileImageLoader reverted to caching byte arrays instead of BitmapFrames.
- Uses FileDb version 6.1.
This commit is contained in:
ClemensF 2015-11-28 21:09:25 +01:00
parent bc30e1d9ca
commit 5adcd6568e
32 changed files with 336 additions and 300 deletions

Binary file not shown.

View file

@ -6,8 +6,10 @@
<members>
<member name="T:FileDbNs.FileDb">
<summary>
Represents an open FileDb database file. None of the FileDb classes are re-entrant -
access to the class objects must be syncronised by the calling application.
Represents an open FileDb database file. All of the FileDb classes/methods are re-entrant -
there is no need to syncronise access to the class objects by the calling application.
However you should use the try-finally pattern when you open a FileDb to ensure
prompt closing in the finally code block.
</summary>
</member>
@ -503,34 +505,39 @@
<param name="fieldName">The name of the Field to rename</param>
</member>
<member name="M:FileDbNs.FileDb.SetEncryptionKey(System.String)">
<member name="M:FileDbNs.FileDb.SetEncryptor(FileDbNs.IEncryptor)">
<summary>
Used to set your own encryptor. Use this for cross-platform encryption scenarios, where you
have control over the encryption method being used. See the Windows Sample apps for an example.
</summary>
<param name="encryptor"></param>
</member>
<member name="M:FileDbNs.FileDb.EncryptString(System.String)">
<summary>
Convienience method to encrypt a string value. You must first call SetEncryptor with an Encryptor.
</summary>
<param name="value">The string to encrypt</param>
<returns>The encrypted value</returns>
</member>
<member name="M:FileDbNs.FileDb.DecryptString(System.String)">
<summary>
Decrypt a string value. You must first call SetEncryptor with an Encryptor.
</summary>
<param name="value">The string to decrypt</param>
<returns>The decrypted value</returns>
</member>
<member name="M:FileDbNs.FileDb.SetEncryptionKey(System.String,System.String)">
<summary>
*** Only works on Windows platform. Use SetEncryptor to provide your own encryptor for cross platform databases ***
*** Do not use this method anymore ***
Allows you to set an encryption key after the database has been opened. You must set
the encryption key before reading or writing to the database. Encryption is "all or nothing",
meaning all records are either encrypted or not.
</summary>
<param name="encryptionKey">A string value to use as the encryption key</param>
</member>
<member name="M:FileDbNs.FileDb.EncryptString(System.String,System.String)">
<summary>
Encrypt a string value.
Not syncronized.
</summary>
<param name="encryptKey">The key to use for encryption</param>
<param name="value">The value to encrypt</param>
<returns>The encrypted value as a string</returns>
</member>
<member name="M:FileDbNs.FileDb.DecryptString(System.String,System.String)">
<summary>
Decrypt a string value.
Not syncronized.
</summary>
<param name="encryptKey">The key to use for decryption</param>
<param name="value">The value to decrypt</param>
<returns>The decrypted value as a string</returns>
</member>
<member name="M:FileDbNs.FileDb.SelectRecords``1(FileDbNs.FilterExpression)">
<summary>
@ -892,6 +899,39 @@
</summary>
<param name="index">The record index</param>
</member>
<member name="T:FileDbNs.Encryptor">
<summary>
Class uses the .NET AesManaged class for data encryption, but ONLY on the Windows platform
build...the PCL build just returns the same data without doing anything. This is because
encryption namespace isn't available for PCLs. In this case, create your own Encryptor
class using the IEncryptor interface and set it into the FileDb object via SetEncryptor.
</summary>
</member>
<member name="M:FileDbNs.Encryptor.#ctor(System.String,System.String)">
<summary>
Constructor taking a key (password) and salt as a string
</summary>
<param name="encryptionKey"></param>
<param name="salt"></param>
</member>
<member name="M:FileDbNs.Encryptor.Encrypt(System.Byte[])">
<summary>
Encrypt the passed byte array
</summary>
<param name="dataToEncrypt">The data to encrypt</param>
<returns>The encrypted data</returns>
</member>
<member name="M:FileDbNs.Encryptor.Decrypt(System.Byte[])">
<summary>
Decrypt the passed byte array
</summary>
<param name="encryptedData">The data to decrypt</param>
<returns>The decrypted data</returns>
</member>
<member name="T:FileDbNs.DataTypeEnum_old">
<summary>

Binary file not shown.

View file

@ -6,8 +6,10 @@
<members>
<member name="T:FileDbNs.FileDb">
<summary>
Represents an open FileDb database file. None of the FileDb classes are re-entrant -
access to the class objects must be syncronised by the calling application.
Represents an open FileDb database file. All of the FileDb classes/methods are re-entrant -
there is no need to syncronise access to the class objects by the calling application.
However you should use the try-finally pattern when you open a FileDb to ensure
prompt closing in the finally code block.
</summary>
</member>
@ -471,34 +473,39 @@
<param name="fieldName">The name of the Field to rename</param>
</member>
<member name="M:FileDbNs.FileDb.SetEncryptionKey(System.String)">
<member name="M:FileDbNs.FileDb.SetEncryptor(FileDbNs.IEncryptor)">
<summary>
Used to set your own encryptor. Use this for cross-platform encryption scenarios, where you
have control over the encryption method being used. See the Windows Sample apps for an example.
</summary>
<param name="encryptor"></param>
</member>
<member name="M:FileDbNs.FileDb.EncryptString(System.String)">
<summary>
Convienience method to encrypt a string value. You must first call SetEncryptor with an Encryptor.
</summary>
<param name="value">The string to encrypt</param>
<returns>The encrypted value</returns>
</member>
<member name="M:FileDbNs.FileDb.DecryptString(System.String)">
<summary>
Decrypt a string value. You must first call SetEncryptor with an Encryptor.
</summary>
<param name="value">The string to decrypt</param>
<returns>The decrypted value</returns>
</member>
<member name="M:FileDbNs.FileDb.SetEncryptionKey(System.String,System.String)">
<summary>
*** Only works on Windows platform. Use SetEncryptor to provide your own encryptor for cross platform databases ***
*** Do not use this method anymore ***
Allows you to set an encryption key after the database has been opened. You must set
the encryption key before reading or writing to the database. Encryption is "all or nothing",
meaning all records are either encrypted or not.
</summary>
<param name="encryptionKey">A string value to use as the encryption key</param>
</member>
<member name="M:FileDbNs.FileDb.EncryptString(System.String,System.String)">
<summary>
Encrypt a string value.
Not syncronized.
</summary>
<param name="encryptKey">The key to use for encryption</param>
<param name="value">The value to encrypt</param>
<returns>The encrypted value as a string</returns>
</member>
<member name="M:FileDbNs.FileDb.DecryptString(System.String,System.String)">
<summary>
Decrypt a string value.
Not syncronized.
</summary>
<param name="encryptKey">The key to use for decryption</param>
<param name="value">The value to decrypt</param>
<returns>The decrypted value as a string</returns>
</member>
<member name="M:FileDbNs.FileDb.SelectRecords``1(FileDbNs.FilterExpression)">
<summary>
@ -860,6 +867,15 @@
</summary>
<param name="index">The record index</param>
</member>
<member name="T:FileDbNs.Encryptor">
<summary>
Class uses the .NET AesManaged class for data encryption, but ONLY on the Windows platform
build...the PCL build just returns the same data without doing anything. This is because
encryption namespace isn't available for PCLs. In this case, create your own Encryptor
class using the IEncryptor interface and set it into the FileDb object via SetEncryptor.
</summary>
</member>
<member name="T:FileDbNs.DataTypeEnum_old">
<summary>

View file

@ -44,12 +44,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\FileDb\FileDb.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="FileDbCache.cs" />

View file

@ -9,7 +9,6 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using System.Windows.Media.Imaging;
using FileDbNs;
namespace MapControl.Caching
@ -17,7 +16,6 @@ namespace MapControl.Caching
/// <summary>
/// ObjectCache implementation based on FileDb, a free and simple No-SQL database by EzTools Software.
/// See http://www.eztools-software.com/tools/filedb/.
/// The only valid data type for cached values is System.Windows.Media.Imaging.BitmapFrame.
/// </summary>
public class FileDbCache : ObjectCache, IDisposable
{
@ -196,40 +194,19 @@ namespace MapControl.Caching
if (fileDb.IsOpen)
{
Record record = null;
try
{
record = fileDb.GetRecordByKey(key, new string[] { valueField }, false);
var record = fileDb.GetRecordByKey(key, new string[] { valueField }, false);
if (record != null)
{
return record[0];
}
}
catch (Exception ex)
{
Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\") failed: {1}", key, ex.Message);
}
if (record != null)
{
try
{
using (var memoryStream = new MemoryStream((byte[])record[0]))
{
return BitmapFrame.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
catch (Exception ex)
{
Debug.WriteLine("FileDbCache: Decoding \"{0}\" failed: {1}", key, ex.Message);
}
try
{
fileDb.DeleteRecordByKey(key);
}
catch (Exception ex)
{
Debug.WriteLine("FileDbCache: FileDb.DeleteRecordByKey(\"{0}\") failed: {1}", key, ex.Message);
}
}
}
return null;
@ -254,6 +231,11 @@ namespace MapControl.Caching
throw new ArgumentNullException("The parameter key must not be null.");
}
if (value == null)
{
throw new ArgumentNullException("The parameter value must not be null.");
}
if (policy == null)
{
throw new ArgumentNullException("The parameter policy must not be null.");
@ -264,34 +246,7 @@ namespace MapControl.Caching
throw new NotSupportedException("The parameter regionName must be null.");
}
var bitmap = value as BitmapFrame;
if (bitmap == null)
{
throw new ArgumentException("The parameter value must contain a System.Windows.Media.Imaging.BitmapFrame.");
}
if (fileDb.IsOpen)
{
byte[] buffer = null;
try
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(bitmap);
using (var memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
buffer = memoryStream.ToArray();
}
}
catch (Exception ex)
{
Debug.WriteLine("FileDbCache: Encoding \"{0}\" failed: {1}", key, ex.Message);
}
if (buffer != null)
{
var expires = DateTime.MaxValue;
@ -304,10 +259,9 @@ namespace MapControl.Caching
expires = DateTime.UtcNow + policy.SlidingExpiration;
}
if (!AddOrUpdateRecord(key, buffer, expires) && RepairDatabase())
if (!AddOrUpdateRecord(key, value, expires) && RepairDatabase())
{
AddOrUpdateRecord(key, buffer, expires);
}
AddOrUpdateRecord(key, value, expires);
}
}
}
@ -447,7 +401,7 @@ namespace MapControl.Caching
return false;
}
private bool AddOrUpdateRecord(string key, byte[] value, DateTime expires)
private bool AddOrUpdateRecord(string key, object value, DateTime expires)
{
var fieldValues = new FieldValues(3);
fieldValues.Add(valueField, value);

View file

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

View file

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

View file

@ -40,12 +40,9 @@
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="ImageFileCache.cs" />

View file

@ -6,22 +6,32 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Windows.Media.Imaging;
namespace MapControl.Caching
{
/// <summary>
/// ObjectCache implementation based on local image files.
/// The only valid data type for cached values is System.Windows.Media.Imaging.BitmapFrame.
/// The only valid data type for cached values is byte[].
/// </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 }),
new Tuple<string, byte[]>(".bin", new byte[] { }),
};
private static readonly FileSystemAccessRule fullControlRule = new FileSystemAccessRule(
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
FileSystemRights.FullControl, AccessControlType.Allow);
@ -87,44 +97,52 @@ namespace MapControl.Caching
public override bool Contains(string key, string regionName = null)
{
return memoryCache.Contains(key, regionName) || FindFile(GetPath(key)) != null;
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
if (regionName != null)
{
throw new NotSupportedException("The parameter regionName must be null.");
}
return memoryCache.Contains(key) || FindFile(key) != null;
}
public override object Get(string key, string regionName = null)
{
var bitmap = memoryCache.Get(key, regionName) as BitmapFrame;
if (key == null)
{
throw new ArgumentNullException("The parameter key must not be null.");
}
if (bitmap == null)
if (regionName != null)
{
try
throw new NotSupportedException("The parameter regionName must be null.");
}
var buffer = memoryCache.Get(key) as byte[];
if (buffer == null)
{
var path = FindFile(GetPath(key));
var path = FindFile(key);
if (path != null)
{
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
try
{
bitmap = BitmapFrame.Create(fileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
var metadata = (BitmapMetadata)bitmap.Metadata;
DateTime expiration;
// metadata.DateTaken must be parsed in CurrentCulture
if (metadata != null &&
metadata.DateTaken != null &&
DateTime.TryParse(metadata.DateTaken, CultureInfo.CurrentCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out expiration))
buffer = File.ReadAllBytes(path);
memoryCache.Set(key, buffer, new CacheItemPolicy());
}
catch (Exception ex)
{
memoryCache.Set(key, bitmap, expiration, regionName);
Debug.WriteLine("ImageFileCache: Writing file {0} failed: {1}", path, ex.Message);
}
}
}
}
catch
{
}
}
return bitmap;
return buffer;
}
public override CacheItem GetCacheItem(string key, string regionName = null)
@ -141,59 +159,32 @@ namespace MapControl.Caching
public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
{
var bitmap = value as BitmapFrame;
if (bitmap == null)
if (key == null)
{
throw new ArgumentException("The parameter value must contain a System.Windows.Media.Imaging.BitmapFrame.");
throw new ArgumentNullException("The parameter key must not be null.");
}
var metadata = (BitmapMetadata)bitmap.Metadata;
var format = metadata != null ? metadata.Format : "bmp";
BitmapEncoder encoder = null;
switch (format)
if (regionName != null)
{
case "bmp":
encoder = new BmpBitmapEncoder();
break;
case "gif":
encoder = new GifBitmapEncoder();
break;
case "jpg":
encoder = new JpegBitmapEncoder();
break;
case "png":
encoder = new PngBitmapEncoder();
break;
case "tiff":
encoder = new TiffBitmapEncoder();
break;
case "wmphoto":
encoder = new WmpBitmapEncoder();
break;
default:
break;
throw new NotSupportedException("The parameter regionName must be null.");
}
if (encoder == null)
var buffer = value as byte[];
if (buffer == null || buffer.Length == 0)
{
throw new NotSupportedException(string.Format("The bitmap format {0} is not supported.", format));
throw new NotSupportedException("The parameter value must be a non-empty byte array.");
}
memoryCache.Set(key, bitmap, policy, regionName);
memoryCache.Set(key, buffer, policy);
var path = string.Format("{0}.{1}", GetPath(key), format);
var path = Path.Combine(rootFolder, key)
+ imageFileTypes.First(t => t.Item2.SequenceEqual(buffer.Take(t.Item2.Length))).Item1;
try
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
{
encoder.Frames.Add(bitmap);
encoder.Save(fileStream);
}
File.WriteAllBytes(path, buffer);
var fileSecurity = File.GetAccessControl(path);
fileSecurity.AddAccessRule(fullControlRule);
@ -240,32 +231,40 @@ namespace MapControl.Caching
public override object Remove(string key, string regionName = null)
{
var oldValue = Get(key, regionName);
memoryCache.Remove(key, regionName);
try
if (key == null)
{
var path = FindFile(GetPath(key));
throw new ArgumentNullException("The parameter key must not be null.");
}
if (regionName != null)
{
throw new NotSupportedException("The parameter regionName must be null.");
}
memoryCache.Remove(key);
var path = FindFile(key);
if (path != null)
{
try
{
File.Delete(path);
}
}
catch
catch (Exception ex)
{
Debug.WriteLine("ImageFileCache: Removing file {0} failed: {1}", path, ex.Message);
}
}
return oldValue;
return null;
}
private string GetPath(string key)
private string FindFile(string key)
{
return Path.Combine(rootFolder, key);
}
var path = Path.Combine(rootFolder, key);
private static string FindFile(string path)
try
{
if (!string.IsNullOrEmpty(Path.GetExtension(path)))
{
@ -278,6 +277,11 @@ namespace MapControl.Caching
{
return Directory.EnumerateFiles(folderName, Path.GetFileName(path) + ".*").FirstOrDefault();
}
}
catch (Exception ex)
{
Debug.WriteLine("ImageFileCache: Finding file {0} failed: {1}", path, ex.Message);
}
return null;
}

View file

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

View file

@ -40,30 +40,26 @@ namespace MapControl.Caching
{
var item = await rootFolder.TryGetItemAsync(key);
if (item == null || !item.IsOfType(StorageItemTypes.File))
if (item != null && item.IsOfType(StorageItemTypes.File))
{
return null;
}
var file = (StorageFile)item;
var cacheItem = new ImageCacheItem
{
Buffer = await FileIO.ReadBufferAsync(file)
};
//Debug.WriteLine("ImageFileCache: Reading file {0}", file.Path);
try
{
// Use ImageProperties.DateTaken to get expiration date
var imageProperties = await file.Properties.GetImagePropertiesAsync();
cacheItem.Expires = imageProperties.DateTaken.UtcDateTime;
}
catch
return new ImageCacheItem
{
Buffer = await FileIO.ReadBufferAsync(file),
Expires = (await file.Properties.GetImagePropertiesAsync()).DateTaken.UtcDateTime
};
}
catch (Exception ex)
{
Debug.WriteLine("ImageFileCache: Reading file {0} failed: {1}", file.Path, ex.Message);
}
}
//Debug.WriteLine("Loaded cached image {0}", file.Path);
return cacheItem;
return null;
}
public virtual async Task SetAsync(string key, IBuffer buffer, DateTime expires)
@ -79,16 +75,18 @@ namespace MapControl.Caching
}
var file = await folder.CreateFileAsync(names[names.Length - 1], CreationCollisionOption.ReplaceExisting);
//Debug.WriteLine("ImageFileCache: Writing file {0}", file.Path);
await FileIO.WriteBufferAsync(file, buffer);
// Use ImageProperties.DateTaken to store expiration date
var imageProperties = await file.Properties.GetImagePropertiesAsync();
imageProperties.DateTaken = expires;
await imageProperties.SavePropertiesAsync();
// Store expiration date in ImageProperties.DateTaken
var properties = await file.Properties.GetImagePropertiesAsync();
properties.DateTaken = expires;
await properties.SavePropertiesAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine("ImageFileCache: Writing file {0}\\{1} failed: {2}", rootFolder.Path, key, ex.Message);
}
}
}

View file

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

View file

@ -18,6 +18,9 @@ using System.Windows.Media.Imaging;
namespace MapControl
{
/// <summary>
/// Displays Bing Maps tiles. The static ApiKey property must be set to a Bing Maps API Key.
/// </summary>
public class BingMapsTileLayer : TileLayer
{
public enum MapMode
@ -26,6 +29,12 @@ namespace MapControl
}
public BingMapsTileLayer()
: this(new TileImageLoader())
{
}
public BingMapsTileLayer(ITileImageLoader tileImageLoader)
: base(tileImageLoader)
{
MinZoomLevel = 1;
MaxZoomLevel = 21;
@ -43,7 +52,7 @@ namespace MapControl
if (string.IsNullOrEmpty(ApiKey))
{
throw new InvalidOperationException("A Bing Maps API Key must be assigned to the ApiKey property.");
throw new InvalidOperationException("BingMapsTileLayer requires a Bing Maps API Key.");
}
var uri = string.Format("http://dev.virtualearth.net/REST/V1/Imagery/Metadata/{0}?output=xml&key={1}", Mode, ApiKey);

View file

@ -8,7 +8,7 @@ namespace MapControl
{
internal class BingMapsTileSource : TileSource
{
private string[] subdomains;
private readonly string[] subdomains;
public BingMapsTileSource(string uriFormat, string[] subdomains)
: base(uriFormat)

View file

@ -8,7 +8,7 @@ using System.Windows.Input;
namespace MapControl
{
/// <summary>
/// Default input event handling.
/// MapBase with default input event handling.
/// </summary>
public class Map : MapBase
{

View file

@ -8,7 +8,7 @@ using System.Windows.Input;
namespace MapControl
{
/// <summary>
/// Default input event handling.
/// MapBase with default input event handling.
/// </summary>
public class Map : MapBase
{

View file

@ -10,7 +10,7 @@ using Windows.UI.Xaml.Input;
namespace MapControl
{
/// <summary>
/// Default input event handling.
/// MapBase with default input event handling.
/// </summary>
public class Map : MapBase
{

View file

@ -21,7 +21,7 @@ using System.Windows.Media.Animation;
namespace MapControl
{
/// <summary>
/// The map control. Renders map content provided by the TileLayer or TileLayers property.
/// The map control. Displays map content provided by the TileLayer or TileLayers property.
/// The visible map area is defined by the Center and ZoomLevel properties.
/// The map can be rotated by an angle that is given by the Heading property.
/// MapBase can contain map overlay child elements like other MapPanels or MapItemsControls.

View file

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

View file

@ -11,6 +11,7 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Caching;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
@ -197,7 +198,7 @@ namespace MapControl
}
catch (Exception ex)
{
Debug.WriteLine("Loading tile image failed: {0}", (object)ex.Message);
Debug.WriteLine("ImageTileSource.LoadImage: " + ex.Message);
}
return image;
@ -218,7 +219,7 @@ namespace MapControl
}
catch (Exception ex)
{
Debug.WriteLine("Creating tile image failed: {0}", (object)ex.Message);
Debug.WriteLine("{0}: {1}", path, ex.Message);
}
}
@ -232,7 +233,11 @@ namespace MapControl
try
{
var request = HttpWebRequest.CreateHttp(uri);
if (HttpUserAgent != null)
{
request.UserAgent = HttpUserAgent;
}
using (var response = (HttpWebResponse)request.GetResponse())
{
@ -240,23 +245,24 @@ namespace MapControl
using (var memoryStream = new MemoryStream())
{
responseStream.CopyTo(memoryStream);
memoryStream.Position = 0;
memoryStream.Seek(0, SeekOrigin.Begin);
image = BitmapFrame.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
if (cacheKey != null)
{
SetCachedImage(cacheKey, image, GetExpiration(response.Headers));
SetCachedImage(cacheKey, memoryStream, GetExpiration(response.Headers));
}
}
}
}
catch (WebException ex)
{
Debug.WriteLine("Downloading {0} failed: {1}: {2}", uri, ex.Status, ex.Message);
Debug.WriteLine("{0}: {1}: {2}", uri, ex.Status, ex.Message);
}
catch (Exception ex)
{
Debug.WriteLine("Downloading {0} failed: {1}", uri, ex.Message);
Debug.WriteLine("{0}: {1}", uri, ex.Message);
}
return image;
@ -274,34 +280,44 @@ namespace MapControl
private static bool GetCachedImage(string cacheKey, out BitmapSource image)
{
image = Cache.Get(cacheKey) as BitmapSource;
image = null;
if (image == null)
var buffer = Cache.Get(cacheKey) as byte[];
if (buffer != null)
{
try
{
using (var memoryStream = new MemoryStream(buffer))
{
image = BitmapFrame.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
DateTime expiration = DateTime.MinValue;
if (buffer.Length >= 16 && Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == "EXPIRES:")
{
expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc);
}
return expiration > DateTime.UtcNow;
}
catch (Exception ex)
{
Debug.WriteLine("{0}: {1}", cacheKey, ex.Message);
}
}
return false;
}
var metadata = (BitmapMetadata)image.Metadata;
DateTime expiration;
// get cache expiration date from BitmapMetadata.DateTaken, must be parsed with CurrentCulture
return metadata == null
|| metadata.DateTaken == null
|| !DateTime.TryParse(metadata.DateTaken, CultureInfo.CurrentCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out expiration)
|| expiration > DateTime.UtcNow;
}
private static void SetCachedImage(string cacheKey, BitmapSource image, DateTime expiration)
private static void SetCachedImage(string cacheKey, MemoryStream memoryStream, DateTime expiration)
{
var bitmap = BitmapFrame.Create(image);
var metadata = (BitmapMetadata)bitmap.Metadata;
memoryStream.Seek(0, SeekOrigin.End);
memoryStream.Write(Encoding.ASCII.GetBytes("EXPIRES:"), 0, 8);
memoryStream.Write(BitConverter.GetBytes(expiration.Ticks), 0, 8);
// store cache expiration date in BitmapMetadata.DateTaken
metadata.DateTaken = expiration.ToString(CultureInfo.InvariantCulture);
metadata.Freeze();
bitmap.Freeze();
Cache.Set(cacheKey, bitmap, new CacheItemPolicy { AbsoluteExpiration = expiration });
Cache.Set(cacheKey, memoryStream.ToArray(), new CacheItemPolicy { AbsoluteExpiration = expiration });
//Debug.WriteLine("Cached {0}, Expires {1}", cacheKey, expiration);
}

View file

@ -20,7 +20,7 @@ using System.Windows.Threading;
namespace MapControl
{
/// <summary>
/// Fills a rectangular area with map tiles from a TileSource.
/// Fills the map viewport with map tiles from a TileSource.
/// </summary>
#if NETFX_CORE
[ContentProperty(Name = "TileSource")]

View file

@ -153,7 +153,7 @@ namespace MapControl
}
return new Uri(uriFormat
.Replace("{i}", new string(quadkey[zoomLevel - 1], 1))
.Replace("{i}", new string(quadkey, zoomLevel - 1, 1))
.Replace("{q}", new string(quadkey)),
UriKind.RelativeOrAbsolute);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,6 +10,7 @@ namespace UniversalApp
public MainPage()
{
//TileImageLoader.Cache = new MapControl.Caching.ImageFileCache();
//TileImageLoader.Cache = new MapControl.Caching.FileDbCache();
this.InitializeComponent();
}

View file

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

View file

@ -144,6 +144,10 @@
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Caching\FileDbCache.WinRT\FileDbCache.WinRT.csproj">
<Project>{c7bf2b18-cc74-430b-bcb2-600304efa3d8}</Project>
<Name>FileDbCache.WinRT</Name>
</ProjectReference>
<ProjectReference Include="..\..\Caching\ImageFileCache.WinRT\ImageFileCache.WinRT.csproj">
<Project>{f789647e-96f7-43e3-a895-fa3fe8d01260}</Project>
<Name>ImageFileCache.WinRT</Name>

View file

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