Added ImageFileCache, removed TileImageLoader.CreateCache.

This commit is contained in:
ClemensF 2012-08-15 21:31:10 +02:00
parent ae4fb7881a
commit db1201ad47
12 changed files with 490 additions and 121 deletions

View file

@ -12,7 +12,7 @@ namespace Caching
/// <summary> /// <summary>
/// ObjectCache implementation based on EzTools FileDb - http://www.eztools-software.com/tools/filedb/. /// ObjectCache implementation based on EzTools FileDb - http://www.eztools-software.com/tools/filedb/.
/// </summary> /// </summary>
public class FileDbCache : ObjectCache, IDisposable public class FileDbCache : ObjectCache
{ {
private const string keyField = "Key"; private const string keyField = "Key";
private const string valueField = "Value"; private const string valueField = "Value";
@ -67,7 +67,7 @@ namespace Caching
} }
this.name = name; this.name = name;
path = Path.Combine(directory, name); path = Path.Combine(directory, name.Trim());
if (string.IsNullOrEmpty(Path.GetExtension(path))) if (string.IsNullOrEmpty(Path.GetExtension(path)))
{ {
@ -91,6 +91,11 @@ namespace Caching
{ {
CreateDatabase(); CreateDatabase();
} }
if (fileDb.IsOpen)
{
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
}
} }
public bool AutoFlush public bool AutoFlush
@ -406,12 +411,45 @@ namespace Caching
} }
} }
public void Dispose() public void Close()
{
if (fileDb.IsOpen)
{
fileDb.Close();
AppDomain.CurrentDomain.ProcessExit -= OnProcessExit;
}
}
private void OnProcessExit(object sender, EventArgs e)
{
Close();
}
private void CreateDatabase()
{ {
if (fileDb.IsOpen) if (fileDb.IsOpen)
{ {
fileDb.Close(); fileDb.Close();
} }
if (File.Exists(path))
{
File.Delete(path);
}
else
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
}
fileDb.Create(path,
new Field[]
{
new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true },
new Field(valueField, DataTypeEnum.Byte) { IsArray = true },
new Field(expiresField, DataTypeEnum.DateTime)
});
Trace.TraceInformation("FileDbCache: Created database {0}", path);
} }
private bool RepairDatabase() private bool RepairDatabase()
@ -420,47 +458,19 @@ namespace Caching
{ {
fileDb.Reindex(); fileDb.Reindex();
} }
catch (Exception ex) catch (Exception ex1)
{ {
Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}", ex.Message); Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}", ex1.Message);
return CreateDatabase();
}
return true; try
}
private bool CreateDatabase()
{
if (fileDb.IsOpen)
{
fileDb.Close();
}
try
{
if (File.Exists(path))
{ {
File.Delete(path); CreateDatabase();
} }
else catch (Exception ex2)
{ {
Directory.CreateDirectory(Path.GetDirectoryName(path)); Trace.TraceWarning("FileDbCache: Creating database {0} failed: {1}", path, ex2.Message);
return false;
} }
fileDb.Create(path,
new Field[]
{
new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true },
new Field(valueField, DataTypeEnum.Byte) { IsArray = true },
new Field(expiresField, DataTypeEnum.DateTime)
});
Trace.TraceInformation("FileDbCache: Created database {0}", path);
}
catch (Exception ex)
{
Trace.TraceWarning("FileDbCache: Creating database failed: {0}", ex.Message);
return false;
} }
return true; return true;

View file

@ -0,0 +1,293 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
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.
/// </summary>
public class ImageFileCache : ObjectCache
{
private readonly string name;
private readonly string directory;
public ImageFileCache(string name, NameValueCollection config)
: this(name, config["directory"])
{
}
public ImageFileCache(string name, string directory)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("The parameter name must not be null or empty or only white-space.");
}
if (string.IsNullOrWhiteSpace(directory))
{
throw new ArgumentException("The parameter directory must not be null or empty or only white-space.");
}
this.name = name;
this.directory = Path.Combine(directory, name.Trim());
Directory.CreateDirectory(this.directory);
Trace.TraceInformation("Created ImageFileCache in {0}", this.directory);
}
public override string Name
{
get { return name; }
}
public override DefaultCacheCapabilities DefaultCacheCapabilities
{
get { return DefaultCacheCapabilities.InMemoryProvider; }
}
public override object this[string key]
{
get { return Get(key); }
set { Set(key, value, null); }
}
protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
throw new NotSupportedException("LocalFileCache does not support the ability to enumerate items.");
}
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> keys, string regionName = null)
{
throw new NotSupportedException("LocalFileCache does not support the ability to create change monitors.");
}
public override long GetCount(string regionName = null)
{
throw new NotSupportedException("LocalFileCache does not support the ability to count items.");
}
public override bool Contains(string key, string regionName = null)
{
if (regionName != null)
{
throw new NotSupportedException("The parameter regionName must be null.");
}
try
{
return MemoryCache.Default.Contains(key) || FindFile(GetPath(key)) != null;
}
catch
{
return false;
}
}
public override object Get(string key, string regionName = null)
{
if (regionName != null)
{
throw new NotSupportedException("The parameter regionName must be null.");
}
var value = MemoryCache.Default.Get(key);
if (value == null)
{
try
{
var path = FindFile(GetPath(key));
if (path != null)
{
long creationTime = File.GetLastWriteTimeUtc(path).ToBinary();
using (FileStream fileStream = new FileStream(path, FileMode.Open))
using (MemoryStream memoryStream = new MemoryStream((int)(fileStream.Length + 8)))
{
memoryStream.Write(BitConverter.GetBytes(creationTime), 0, 8);
fileStream.CopyTo(memoryStream);
value = memoryStream.GetBuffer();
}
}
}
catch
{
}
}
return value;
}
public override CacheItem GetCacheItem(string key, string regionName = null)
{
var value = Get(key, regionName);
return value != null ? new CacheItem(key, value) : null;
}
public override IDictionary<string, object> GetValues(IEnumerable<string> keys, string regionName = null)
{
if (regionName != null)
{
throw new NotSupportedException("The parameter regionName must be null.");
}
var values = new Dictionary<string, object>();
foreach (string key in keys)
{
values[key] = Get(key);
}
return values;
}
public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
{
if (regionName != null)
{
throw new NotSupportedException("The parameter regionName must be null.");
}
var buffer = value as byte[];
if (buffer == null)
{
throw new NotSupportedException("The parameter value must be a byte[].");
}
MemoryCache.Default.Set(key, buffer, policy);
var extension = GetFileExtension(buffer);
if (extension != null)
{
var path = GetPath(key) + extension;
Directory.CreateDirectory(Path.GetDirectoryName(path));
using (FileStream fileStream = new FileStream(path, FileMode.Create))
{
fileStream.Write(buffer, 8, buffer.Length - 8);
}
}
}
public override void Set(CacheItem item, CacheItemPolicy policy)
{
Set(item.Key, item.Value, policy, item.RegionName);
}
public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
{
Set(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
}
public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
{
var oldValue = Get(key, regionName);
Set(key, value, policy);
return oldValue;
}
public override CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
{
var oldItem = GetCacheItem(item.Key, item.RegionName);
Set(item, policy);
return oldItem;
}
public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
{
return AddOrGetExisting(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
}
public override object Remove(string key, string regionName = null)
{
var oldValue = Get(key, regionName);
MemoryCache.Default.Remove(key);
try
{
var path = FindFile(GetPath(key));
if (path != null)
{
File.Delete(path);
}
}
catch
{
}
return oldValue;
}
private string GetPath(string key)
{
return Path.Combine(directory, key);
}
private static string FindFile(string path)
{
if (!string.IsNullOrEmpty(Path.GetExtension(path)))
{
return path;
}
string directoryName = Path.GetDirectoryName(path);
if (Directory.Exists(directoryName))
{
return Directory.EnumerateFiles(directoryName, Path.GetFileName(path) + ".*").FirstOrDefault();
}
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;
DateTime 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;
if (t.Item2.Length + 8 <= buffer.Length)
{
while (i < t.Item2.Length && t.Item2[i] == buffer[i + 8])
{
i++;
}
}
return i == t.Item2.Length;
};
extension = fileTypes.Where(match).Select(t => t.Item1).FirstOrDefault();
}
return extension;
}
}
}

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{86470440-FEE2-4120-AF5A-3762FB9C536F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Caching</RootNamespace>
<AssemblyName>ImageFileCache</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Caching" />
</ItemGroup>
<ItemGroup>
<Compile Include="ImageFileCache.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ImageFileCache")]
[assembly: AssemblyDescription("ObjectCache implementation based on local image files")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ImageFileCache")]
[assembly: AssemblyCopyright("Copyright © 2012 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("0483659a-9743-41a2-9505-9c1a4d9ecc06")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApplication", "Sample
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SurfaceApplication", "SampleApps\SurfaceApplication\SurfaceApplication.csproj", "{6285FB9D-B7EA-469A-B464-224077967167}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SurfaceApplication", "SampleApps\SurfaceApplication\SurfaceApplication.csproj", "{6285FB9D-B7EA-469A-B464-224077967167}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageFileCache", "Caching\ImageFileCache\ImageFileCache.csproj", "{86470440-FEE2-4120-AF5A-3762FB9C536F}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -59,6 +61,16 @@ Global
{6285FB9D-B7EA-469A-B464-224077967167}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {6285FB9D-B7EA-469A-B464-224077967167}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{6285FB9D-B7EA-469A-B464-224077967167}.Release|Mixed Platforms.Build.0 = Release|Any CPU {6285FB9D-B7EA-469A-B464-224077967167}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{6285FB9D-B7EA-469A-B464-224077967167}.Release|x86.ActiveCfg = Release|Any CPU {6285FB9D-B7EA-469A-B464-224077967167}.Release|x86.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x86.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Any CPU.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -5,14 +5,12 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Runtime.Caching; using System.Runtime.Caching;
using System.Threading; using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Threading; using System.Windows.Threading;
@ -26,6 +24,17 @@ namespace MapControl
private readonly TileLayer tileLayer; private readonly TileLayer tileLayer;
private readonly ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>(); private readonly ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>();
/// <summary>
/// Default Name of an ObjectCache instance that is assigned to the Cache property.
/// </summary>
public static readonly string DefaultCacheName = "TileCache";
/// <summary>
/// Default value for the directory where an ObjectCache instance may save cached data.
/// </summary>
public static readonly string DefaultCacheDirectory =
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 System.Runtime.Caching.MemoryCache.Default. /// The default is System.Runtime.Caching.MemoryCache.Default.
@ -48,53 +57,11 @@ namespace MapControl
/// </summary> /// </summary>
public static TimeSpan CacheUpdateAge { get; set; } public static TimeSpan CacheUpdateAge { get; set; }
/// <summary>
/// Creates an instance of the ObjectCache-derived type T and sets the static Cache
/// property to this instance. Class T must (like System.Runtime.Caching.MemoryCache)
/// provide a constructor with two parameters, first a string that gets the name of
/// the cache instance, second a NameValueCollection that gets the config parameter.
/// If config is null, a new NameValueCollection is created. If config does not already
/// contain an entry with key "directory", a new entry is added with this key and a
/// value that specifies the path to an application data directory where the cache
/// implementation may store persistent cache data files.
/// </summary>
public static void CreateCache<T>(NameValueCollection config = null) where T : ObjectCache
{
if (config == null)
{
config = new NameValueCollection(1);
}
if (config["directory"] == null)
{
config["directory"] = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl");
}
try
{
Cache = (ObjectCache)Activator.CreateInstance(typeof(T), "TileCache", config);
}
catch (Exception ex)
{
Trace.TraceWarning("Could not create instance of type {0} with String and NameValueCollection constructor parameters: {1}", typeof(T), ex.Message);
throw;
}
}
static TileImageLoader() static TileImageLoader()
{ {
Cache = MemoryCache.Default; Cache = MemoryCache.Default;
CacheExpiration = TimeSpan.FromDays(30d); CacheExpiration = TimeSpan.FromDays(30d);
CacheUpdateAge = TimeSpan.FromDays(1d); CacheUpdateAge = TimeSpan.FromDays(1d);
Application.Current.Exit += (o, e) =>
{
IDisposable disposableCache = Cache as IDisposable;
if (disposableCache != null)
{
disposableCache.Dispose();
}
};
} }
internal TileImageLoader(TileLayer tileLayer) internal TileImageLoader(TileLayer tileLayer)
@ -151,7 +118,7 @@ namespace MapControl
Cache.Remove(key); Cache.Remove(key);
pendingTiles.Enqueue(tile); pendingTiles.Enqueue(tile);
} }
else if (GetCreationTime(imageBuffer) + CacheUpdateAge < DateTime.UtcNow) else if (IsCacheOutdated(imageBuffer))
{ {
// update cached image // update cached image
outdatedTiles.Add(tile); outdatedTiles.Add(tile);
@ -206,21 +173,15 @@ namespace MapControl
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream responseStream = response.GetResponseStream()) using (Stream responseStream = response.GetResponseStream())
{ {
if (response.ContentLength > 0) long length = response.ContentLength;
long creationTime = DateTime.UtcNow.ToBinary();
using (MemoryStream memoryStream = length > 0 ? new MemoryStream((int)length + 8) : new MemoryStream())
{ {
using (MemoryStream memoryStream = new MemoryStream((int)response.ContentLength + 8)) memoryStream.Write(BitConverter.GetBytes(creationTime), 0, 8);
{ responseStream.CopyTo(memoryStream);
CopyWithCreationTime(responseStream, memoryStream);
buffer = memoryStream.GetBuffer(); buffer = length > 0 ? memoryStream.GetBuffer() : memoryStream.ToArray();
}
}
else
{
using (MemoryStream memoryStream = new MemoryStream())
{
CopyWithCreationTime(responseStream, memoryStream);
buffer = memoryStream.ToArray();
}
} }
} }
@ -245,13 +206,20 @@ namespace MapControl
return buffer; return buffer;
} }
private bool CreateTileImage(Tile tile, byte[] buffer) private bool IsCacheOutdated(byte[] imageBuffer)
{
long creationTime = BitConverter.ToInt64(imageBuffer, 0);
return DateTime.FromBinary(creationTime) + CacheUpdateAge < DateTime.UtcNow;
}
private bool CreateTileImage(Tile tile, byte[] imageBuffer)
{ {
BitmapImage bitmap = new BitmapImage(); BitmapImage bitmap = new BitmapImage();
try try
{ {
using (Stream stream = new MemoryStream(buffer, 0, buffer.Length - 8, false)) using (Stream stream = new MemoryStream(imageBuffer, 8, imageBuffer.Length - 8, false))
{ {
bitmap.BeginInit(); bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.CacheOption = BitmapCacheOption.OnLoad;
@ -270,17 +238,6 @@ namespace MapControl
return true; return true;
} }
private static DateTime GetCreationTime(byte[] imageBuffer)
{
return new DateTime(BitConverter.ToInt64(imageBuffer, imageBuffer.Length - 8));
}
private static void CopyWithCreationTime(Stream source, Stream target)
{
source.CopyTo(target);
target.Write(BitConverter.GetBytes(DateTime.UtcNow.Ticks), 0, 8);
}
private static void TraceWarning(string format, params object[] args) private static void TraceWarning(string format, params object[] args)
{ {
Trace.TraceWarning("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args)); Trace.TraceWarning("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));

View file

@ -7,8 +7,8 @@
</configSections> </configSections>
<applicationSettings> <applicationSettings>
<SampleApplication.Properties.Settings> <SampleApplication.Properties.Settings>
<setting name="UsePersistentCache" serializeAs="String"> <setting name="TileCache" serializeAs="String">
<value>False</value> <value />
</setting> </setting>
</SampleApplication.Properties.Settings> </SampleApplication.Properties.Settings>
</applicationSettings> </applicationSettings>

View file

@ -21,7 +21,7 @@
map content without using their APIs (i.e. Google Maps API or Bing Maps API). map content without using their APIs (i.e. Google Maps API or Bing Maps API).
Hence the declarations below are for demonstration purpose only. --> Hence the declarations below are for demonstration purpose only. -->
<map:TileLayer Name="Google Maps" Description="Google Maps - © {y} Google" <!--<map:TileLayer Name="Google Maps" Description="Google Maps - © {y} Google"
TileSource="http://mt{i}.google.com/vt/x={x}&amp;y={y}&amp;z={z}" MaxZoomLevel="20"/> TileSource="http://mt{i}.google.com/vt/x={x}&amp;y={y}&amp;z={z}" MaxZoomLevel="20"/>
<map:TileLayer Name="Google Images" Description="Google Maps - © {y} Google" <map:TileLayer Name="Google Images" Description="Google Maps - © {y} Google"
TileSource="http://khm{i}.google.com/kh/v=113&amp;x={x}&amp;y={y}&amp;z={z}" MaxZoomLevel="20" HasDarkBackground="True"/> TileSource="http://khm{i}.google.com/kh/v=113&amp;x={x}&amp;y={y}&amp;z={z}" MaxZoomLevel="20" HasDarkBackground="True"/>
@ -30,7 +30,7 @@
<map:TileLayer Name="Bing Images" Description="Bing Maps - © {y} Microsoft Corporation" <map:TileLayer Name="Bing Images" Description="Bing Maps - © {y} Microsoft Corporation"
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/a{q}.jpeg?g=0" MaxZoomLevel="20" HasDarkBackground="True"/> TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/a{q}.jpeg?g=0" MaxZoomLevel="20" HasDarkBackground="True"/>
<map:TileLayer Name="Bing Hybrid" Description="Bing Maps - © {y} Microsoft Corporation" <map:TileLayer Name="Bing Hybrid" Description="Bing Maps - © {y} Microsoft Corporation"
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/h{q}.jpeg?g=0&amp;stl=h" MaxZoomLevel="20" HasDarkBackground="True"/> TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/h{q}.jpeg?g=0&amp;stl=h" MaxZoomLevel="20" HasDarkBackground="True"/>-->
<!-- The TileLayer below uses an ImageTileSource, which bypasses caching of map tile images --> <!-- The TileLayer below uses an ImageTileSource, which bypasses caching of map tile images -->

View file

@ -19,9 +19,16 @@ namespace SampleApplication
public MainWindow() public MainWindow()
{ {
if (Properties.Settings.Default.UsePersistentCache) switch (Properties.Settings.Default.TileCache)
{ {
TileImageLoader.CreateCache<FileDbCache>(); case "FileDbCache":
TileImageLoader.Cache = new FileDbCache(TileImageLoader.DefaultCacheName, TileImageLoader.DefaultCacheDirectory);
break;
case "ImageFileCache":
TileImageLoader.Cache = new ImageFileCache(TileImageLoader.DefaultCacheName, TileImageLoader.DefaultCacheDirectory);
break;
default:
break;
} }
InitializeComponent(); InitializeComponent();

View file

@ -25,10 +25,10 @@ namespace SampleApplication.Properties {
[global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")] [global::System.Configuration.DefaultSettingValueAttribute("")]
public bool UsePersistentCache { public string TileCache {
get { get {
return ((bool)(this["UsePersistentCache"])); return ((string)(this["TileCache"]));
} }
} }
} }

View file

@ -2,8 +2,8 @@
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="SampleApplication.Properties" GeneratedClassName="Settings"> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="SampleApplication.Properties" GeneratedClassName="Settings">
<Profiles /> <Profiles />
<Settings> <Settings>
<Setting Name="UsePersistentCache" Type="System.Boolean" Scope="Application"> <Setting Name="TileCache" Type="System.String" Scope="Application">
<Value Profile="(Default)">False</Value> <Value Profile="(Default)" />
</Setting> </Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View file

@ -84,6 +84,10 @@
<Project>{EF44F661-B98A-4676-927F-85D138F82300}</Project> <Project>{EF44F661-B98A-4676-927F-85D138F82300}</Project>
<Name>FileDbCache</Name> <Name>FileDbCache</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\Caching\ImageFileCache\ImageFileCache.csproj">
<Project>{86470440-FEE2-4120-AF5A-3762FB9C536F}</Project>
<Name>ImageFileCache</Name>
</ProjectReference>
<ProjectReference Include="..\..\MapControl\MapControl.csproj"> <ProjectReference Include="..\..\MapControl\MapControl.csproj">
<Project>{06481252-2310-414A-B9FC-D5739FDF6BD3}</Project> <Project>{06481252-2310-414A-B9FC-D5739FDF6BD3}</Project>
<Name>MapControl</Name> <Name>MapControl</Name>