mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Replaced local file caching by persistent ObjectCache based on FileDb.
Removed IsCached and ImageType properties from TileLayer.
This commit is contained in:
parent
38e6c23114
commit
9652fc2f56
1560
Caches/FileDbCache/FileDb/FileDb.XML
Normal file
1560
Caches/FileDbCache/FileDb/FileDb.XML
Normal file
File diff suppressed because it is too large
Load diff
BIN
Caches/FileDbCache/FileDb/FileDb.dll
Normal file
BIN
Caches/FileDbCache/FileDb/FileDb.dll
Normal file
Binary file not shown.
5
Caches/FileDbCache/FileDb/FileDb.txt
Normal file
5
Caches/FileDbCache/FileDb/FileDb.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
FileDbCache uses FileDb by Brett Goodman (aka EzTools), a simple No-SQL database for .NET.
|
||||||
|
FileDb is Open Source on Google Code Hosting at http://code.google.com/p/filedb-database/
|
||||||
|
and licensed under the Apache License 2.0, http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
See the product homepage at http://www.eztools-software.com/tools/filedb/.
|
||||||
|
Download FileDb from http://www.eztools-software.com/downloads/filedb.exe.
|
||||||
444
Caches/FileDbCache/FileDbCache.cs
Normal file
444
Caches/FileDbCache/FileDbCache.cs
Normal file
|
|
@ -0,0 +1,444 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.Caching;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using FileDbNs;
|
||||||
|
|
||||||
|
namespace Caching
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ObjectCache implementation based on EzTools FileDb - http://www.eztools-software.com/tools/filedb/.
|
||||||
|
/// </summary>
|
||||||
|
public class FileDbCache : ObjectCache, IDisposable
|
||||||
|
{
|
||||||
|
private const string keyField = "Key";
|
||||||
|
private const string valueField = "Value";
|
||||||
|
private const string expiresField = "Expires";
|
||||||
|
|
||||||
|
private readonly BinaryFormatter formatter = new BinaryFormatter();
|
||||||
|
private readonly FileDb fileDb = new FileDb();
|
||||||
|
private readonly string name;
|
||||||
|
private readonly string path;
|
||||||
|
|
||||||
|
public FileDbCache(string name, string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The parameter path must not be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Path.GetExtension(path)))
|
||||||
|
{
|
||||||
|
path += ".fdb";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
this.path = path;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileDb.Open(path, false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
CreateDatebase();
|
||||||
|
}
|
||||||
|
|
||||||
|
Trace.TraceInformation("FileDbCache created with {0} cached items", fileDb.NumRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AutoFlush
|
||||||
|
{
|
||||||
|
get { return fileDb.AutoFlush; }
|
||||||
|
set { fileDb.AutoFlush = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AutoCleanThreshold
|
||||||
|
{
|
||||||
|
get { return fileDb.AutoCleanThreshold; }
|
||||||
|
set { fileDb.AutoCleanThreshold = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name
|
||||||
|
{
|
||||||
|
get { return name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DefaultCacheCapabilities DefaultCacheCapabilities
|
||||||
|
{
|
||||||
|
get { return DefaultCacheCapabilities.InMemoryProvider | DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations; }
|
||||||
|
}
|
||||||
|
|
||||||
|
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("FileDbCache does not support the ability to enumerate items.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> keys, string regionName = null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("FileDbCache does not support the ability to create change monitors.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long GetCount(string regionName = null)
|
||||||
|
{
|
||||||
|
if (regionName != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("The parameter regionName must be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
long count = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
count = fileDb.NumRecords;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (CheckReindex())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
count = fileDb.NumRecords;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
CreateDatebase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Contains(string key, string regionName = null)
|
||||||
|
{
|
||||||
|
if (regionName != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("The parameter regionName must be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("The parameter key must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
contains = fileDb.GetRecordByKey(key, new string[0], false) != null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (CheckReindex())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
contains = fileDb.GetRecordByKey(key, new string[0], false) != null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
CreateDatebase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return contains;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Get(string key, string regionName = null)
|
||||||
|
{
|
||||||
|
if (regionName != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("The parameter regionName must be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("The parameter key must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
object value = null;
|
||||||
|
Record record = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
record = fileDb.GetRecordByKey(key, new string[] { valueField }, false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (CheckReindex())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
record = fileDb.GetRecordByKey(key, new string[] { valueField }, false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
CreateDatebase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (MemoryStream stream = new MemoryStream((byte[])record[0]))
|
||||||
|
{
|
||||||
|
value = formatter.Deserialize(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
Trace.TraceWarning("FileDbCache.Get({0}): {1}", key, exc.Message);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileDb.DeleteRecordByKey(key);
|
||||||
|
}
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("The parameter value must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("The parameter key must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] valueBuffer = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (MemoryStream stream = new MemoryStream())
|
||||||
|
{
|
||||||
|
formatter.Serialize(stream, value);
|
||||||
|
valueBuffer = stream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
Trace.TraceWarning("FileDbCache.Set({0}): {1}", key, exc.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueBuffer != null)
|
||||||
|
{
|
||||||
|
DateTime expires = DateTime.MaxValue;
|
||||||
|
|
||||||
|
if (policy.AbsoluteExpiration != InfiniteAbsoluteExpiration)
|
||||||
|
{
|
||||||
|
expires = policy.AbsoluteExpiration.DateTime;
|
||||||
|
}
|
||||||
|
else if (policy.SlidingExpiration != NoSlidingExpiration)
|
||||||
|
{
|
||||||
|
expires = DateTime.UtcNow + policy.SlidingExpiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AddOrUpdateRecord(key, valueBuffer, expires);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (CheckReindex())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AddOrUpdateRecord(key, valueBuffer, expires);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
CreateDatebase();
|
||||||
|
AddOrUpdateRecord(key, valueBuffer, expires);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (oldValue != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileDb.DeleteRecordByKey(key);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileDb.Flush();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
CheckReindex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clean()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileDb.Clean();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
CheckReindex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, EqualityEnum.LessThanOrEqual));
|
||||||
|
Trace.TraceInformation("FileDbCache has deleted {0} expired items", fileDb.NumDeleted);
|
||||||
|
fileDb.Clean();
|
||||||
|
fileDb.Close();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (CheckReindex())
|
||||||
|
{
|
||||||
|
fileDb.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckReindex()
|
||||||
|
{
|
||||||
|
if (fileDb.IsOpen)
|
||||||
|
{
|
||||||
|
Trace.TraceWarning("FileDbCache is reindexing database");
|
||||||
|
fileDb.Reindex();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDatebase()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddOrUpdateRecord(string key, object value, DateTime expires)
|
||||||
|
{
|
||||||
|
var fieldValues = new FieldValues(3); // capacity
|
||||||
|
fieldValues.Add(valueField, value);
|
||||||
|
fieldValues.Add(expiresField, expires);
|
||||||
|
|
||||||
|
if (fileDb.GetRecordByKey(key, new string[0], false) == null)
|
||||||
|
{
|
||||||
|
fieldValues.Add(keyField, key);
|
||||||
|
fileDb.AddRecord(fieldValues);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fileDb.UpdateRecordByKey(key, fieldValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
Caches/FileDbCache/FileDbCache.csproj
Normal file
58
Caches/FileDbCache/FileDbCache.csproj
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?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>{EF44F661-B98A-4676-927F-85D138F82300}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Caching</RootNamespace>
|
||||||
|
<AssemblyName>FileDbCache</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="FileDb, Version=3.10.0.0, Culture=neutral, PublicKeyToken=68cc942b9efb3282, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>FileDb\FileDb.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Runtime.Caching" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="FileDbCache.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="FileDb\FileDb.dll" />
|
||||||
|
<Content Include="FileDb\FileDb.txt" />
|
||||||
|
</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>
|
||||||
36
Caches/FileDbCache/Properties/AssemblyInfo.cs
Normal file
36
Caches/FileDbCache/Properties/AssemblyInfo.cs
Normal 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("FileDbCache")]
|
||||||
|
[assembly: AssemblyDescription("ObjectCache implementation based on EzTools FileDb")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("FileDbCache")]
|
||||||
|
[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("c1fc4f52-e47c-4f62-9807-7e096db69851")]
|
||||||
|
|
||||||
|
// 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")]
|
||||||
|
|
@ -5,6 +5,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl", "MapControl\Ma
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication", "TestApplication\TestApplication.csproj", "{CCBCDAE5-E68F-43A8-930A-0749E476D29D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication", "TestApplication\TestApplication.csproj", "{CCBCDAE5-E68F-43A8-930A-0749E476D29D}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDbCache", "Caches\FileDbCache\FileDbCache.csproj", "{EF44F661-B98A-4676-927F-85D138F82300}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -35,6 +37,16 @@ Global
|
||||||
{CCBCDAE5-E68F-43A8-930A-0749E476D29D}.Release|Mixed Platforms.Build.0 = Release|x86
|
{CCBCDAE5-E68F-43A8-930A-0749E476D29D}.Release|Mixed Platforms.Build.0 = Release|x86
|
||||||
{CCBCDAE5-E68F-43A8-930A-0749E476D29D}.Release|x86.ActiveCfg = Release|x86
|
{CCBCDAE5-E68F-43A8-930A-0749E476D29D}.Release|x86.ActiveCfg = Release|x86
|
||||||
{CCBCDAE5-E68F-43A8-930A-0749E476D29D}.Release|x86.Build.0 = Release|x86
|
{CCBCDAE5-E68F-43A8-930A-0749E476D29D}.Release|x86.Build.0 = Release|x86
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// Licensed under the Microsoft Public License (Ms-PL)
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
|
|
||||||
|
|
@ -49,10 +48,5 @@ namespace MapControl
|
||||||
Brush.ImageSource = value;
|
Brush.ImageSource = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return string.Format("{0}.{1}.{2}", ZoomLevel, X, Y);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
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;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
|
@ -16,28 +18,70 @@ using System.Windows.Threading;
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads map tiles by their URIs and optionally caches their image files in a folder
|
/// Loads map tile images by their URIs and optionally caches the images in an ObjectCache.
|
||||||
/// defined by the static TileCacheFolder property.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TileImageLoader : DispatcherObject
|
public class TileImageLoader : DispatcherObject
|
||||||
{
|
{
|
||||||
public static string TileCacheFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl Cache");
|
[Serializable]
|
||||||
public static TimeSpan TileCacheExpiryAge = TimeSpan.FromDays(1d);
|
private class CachedImage
|
||||||
|
{
|
||||||
|
public readonly DateTime CreationTime = DateTime.UtcNow;
|
||||||
|
public readonly byte[] ImageBuffer;
|
||||||
|
|
||||||
|
public CachedImage(byte[] imageBuffer)
|
||||||
|
{
|
||||||
|
ImageBuffer = imageBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly TileLayer tileLayer;
|
private readonly TileLayer tileLayer;
|
||||||
private readonly Queue<Tile> pendingTiles = new Queue<Tile>();
|
private readonly Queue<Tile> pendingTiles = new Queue<Tile>();
|
||||||
|
private readonly HashSet<HttpWebRequest> currentRequests = new HashSet<HttpWebRequest>();
|
||||||
private int numDownloads;
|
private int numDownloads;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ObjectCache used to cache tile images.
|
||||||
|
/// The default is System.Runtime.Caching.MemoryCache.Default.
|
||||||
|
/// </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 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 of
|
||||||
|
/// the CacheExpiration property.
|
||||||
|
/// </summary>
|
||||||
|
public static TimeSpan CacheUpdateAge { get; set; }
|
||||||
|
|
||||||
|
static TileImageLoader()
|
||||||
|
{
|
||||||
|
Cache = MemoryCache.Default;
|
||||||
|
CacheExpiration = TimeSpan.FromDays(30d);
|
||||||
|
CacheUpdateAge = TimeSpan.FromDays(1d);
|
||||||
|
|
||||||
|
Application.Current.Exit += (o, e) =>
|
||||||
|
{
|
||||||
|
IDisposable disposableCache = Cache as IDisposable;
|
||||||
|
if (disposableCache != null)
|
||||||
|
{
|
||||||
|
disposableCache.Dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public TileImageLoader(TileLayer tileLayer)
|
public TileImageLoader(TileLayer tileLayer)
|
||||||
{
|
{
|
||||||
this.tileLayer = tileLayer;
|
this.tileLayer = tileLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsCached
|
|
||||||
{
|
|
||||||
get { return tileLayer.IsCached && !string.IsNullOrEmpty(TileCacheFolder); }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void StartDownloadTiles(ICollection<Tile> tiles)
|
internal void StartDownloadTiles(ICollection<Tile> tiles)
|
||||||
{
|
{
|
||||||
ThreadPool.QueueUserWorkItem(StartDownloadTilesAsync, new List<Tile>(tiles.Where(t => t.Image == null && t.Uri == null)));
|
ThreadPool.QueueUserWorkItem(StartDownloadTilesAsync, new List<Tile>(tiles.Where(t => t.Image == null && t.Uri == null)));
|
||||||
|
|
@ -49,46 +93,54 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
pendingTiles.Clear();
|
pendingTiles.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (currentRequests)
|
||||||
|
{
|
||||||
|
foreach (HttpWebRequest request in currentRequests)
|
||||||
|
{
|
||||||
|
request.Abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartDownloadTilesAsync(object newTilesList)
|
private void StartDownloadTilesAsync(object newTilesList)
|
||||||
{
|
{
|
||||||
List<Tile> newTiles = (List<Tile>)newTilesList;
|
List<Tile> newTiles = (List<Tile>)newTilesList;
|
||||||
List<Tile> expiredTiles = new List<Tile>(newTiles.Count);
|
|
||||||
|
|
||||||
lock (pendingTiles)
|
lock (pendingTiles)
|
||||||
{
|
{
|
||||||
newTiles.ForEach(tile =>
|
if (Cache == null)
|
||||||
{
|
{
|
||||||
ImageSource image = GetMemoryCachedImage(tile);
|
newTiles.ForEach(tile => pendingTiles.Enqueue(tile));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Tile> outdatedTiles = new List<Tile>(newTiles.Count);
|
||||||
|
|
||||||
if (image == null && IsCached)
|
newTiles.ForEach(tile =>
|
||||||
{
|
{
|
||||||
bool fileCacheExpired;
|
string key = CacheKey(tile);
|
||||||
image = GetFileCachedImage(tile, out fileCacheExpired);
|
CachedImage cachedImage = Cache.Get(key) as CachedImage;
|
||||||
|
|
||||||
if (image != null)
|
if (cachedImage == null)
|
||||||
{
|
{
|
||||||
SetMemoryCachedImage(tile, image);
|
pendingTiles.Enqueue(tile);
|
||||||
|
|
||||||
if (fileCacheExpired)
|
|
||||||
{
|
|
||||||
expiredTiles.Add(tile); // enqueue later
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else if (!CreateTileImage(tile, cachedImage.ImageBuffer))
|
||||||
|
{
|
||||||
|
// got garbage from cache
|
||||||
|
Cache.Remove(key);
|
||||||
|
pendingTiles.Enqueue(tile);
|
||||||
|
}
|
||||||
|
else if (cachedImage.CreationTime + CacheUpdateAge < DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
// update cached image
|
||||||
|
outdatedTiles.Add(tile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (image != null)
|
outdatedTiles.ForEach(tile => pendingTiles.Enqueue(tile));
|
||||||
{
|
}
|
||||||
Dispatcher.BeginInvoke((Action)(() => tile.Image = image));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pendingTiles.Enqueue(tile);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expiredTiles.ForEach(tile => pendingTiles.Enqueue(tile));
|
|
||||||
|
|
||||||
DownloadNextTiles(null);
|
DownloadNextTiles(null);
|
||||||
}
|
}
|
||||||
|
|
@ -109,13 +161,13 @@ namespace MapControl
|
||||||
private void DownloadTileAsync(object t)
|
private void DownloadTileAsync(object t)
|
||||||
{
|
{
|
||||||
Tile tile = (Tile)t;
|
Tile tile = (Tile)t;
|
||||||
ImageSource image = DownloadImage(tile);
|
byte[] imageBuffer = DownloadImage(tile);
|
||||||
|
|
||||||
if (image != null)
|
if (imageBuffer != null &&
|
||||||
|
CreateTileImage(tile, imageBuffer) &&
|
||||||
|
Cache != null)
|
||||||
{
|
{
|
||||||
SetMemoryCachedImage(tile, image);
|
Cache.Set(CacheKey(tile), new CachedImage(imageBuffer), new CacheItemPolicy { SlidingExpiration = CacheExpiration });
|
||||||
|
|
||||||
Dispatcher.BeginInvoke((Action)(() => tile.Image = image));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (pendingTiles)
|
lock (pendingTiles)
|
||||||
|
|
@ -125,98 +177,38 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string MemoryCacheKey(Tile tile)
|
private string CacheKey(Tile tile)
|
||||||
{
|
{
|
||||||
return string.Format("{0}/{1}/{2}/{3}", tileLayer.Name, tile.ZoomLevel, tile.XIndex, tile.Y);
|
return string.Format("{0}-{1}-{2}-{3}", tileLayer.Name, tile.ZoomLevel, tile.XIndex, tile.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CacheFilePath(Tile tile)
|
private byte[] DownloadImage(Tile tile)
|
||||||
{
|
{
|
||||||
return string.Format("{0}.{1}",
|
HttpWebRequest request = null;
|
||||||
Path.Combine(TileCacheFolder, tileLayer.Name, tile.ZoomLevel.ToString(), tile.XIndex.ToString(), tile.Y.ToString()),
|
byte[] buffer = null;
|
||||||
tileLayer.ImageType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImageSource GetMemoryCachedImage(Tile tile)
|
|
||||||
{
|
|
||||||
string key = MemoryCacheKey(tile);
|
|
||||||
ImageSource image = MemoryCache.Default.Get(key) as ImageSource;
|
|
||||||
|
|
||||||
if (image != null)
|
|
||||||
{
|
|
||||||
TraceInformation("{0} - Memory Cached", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetMemoryCachedImage(Tile tile, ImageSource image)
|
|
||||||
{
|
|
||||||
MemoryCache.Default.Set(MemoryCacheKey(tile), image,
|
|
||||||
new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(10d) });
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImageSource GetFileCachedImage(Tile tile, out bool expired)
|
|
||||||
{
|
|
||||||
string path = CacheFilePath(tile);
|
|
||||||
ImageSource image = null;
|
|
||||||
expired = false;
|
|
||||||
|
|
||||||
if (File.Exists(path))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (Stream fileStream = File.OpenRead(path))
|
|
||||||
{
|
|
||||||
image = BitmapFrame.Create(fileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
|
||||||
}
|
|
||||||
|
|
||||||
expired = File.GetLastWriteTime(path) + TileCacheExpiryAge <= DateTime.Now;
|
|
||||||
TraceInformation(expired ? "{0} - File Cache Expired" : "{0} - File Cached", path);
|
|
||||||
}
|
|
||||||
catch (Exception exc)
|
|
||||||
{
|
|
||||||
TraceWarning("{0} - {1}", path, exc.Message);
|
|
||||||
File.Delete(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImageSource DownloadImage(Tile tile)
|
|
||||||
{
|
|
||||||
ImageSource image = null;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TraceInformation("{0} - Requesting", tile.Uri);
|
TraceInformation("{0} - Requesting", tile.Uri);
|
||||||
|
|
||||||
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(tile.Uri);
|
request = (HttpWebRequest)WebRequest.Create(tile.Uri);
|
||||||
webRequest.UserAgent = typeof(TileImageLoader).ToString();
|
request.UserAgent = typeof(TileImageLoader).ToString();
|
||||||
webRequest.KeepAlive = true;
|
request.KeepAlive = true;
|
||||||
|
|
||||||
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
|
lock (currentRequests)
|
||||||
|
{
|
||||||
|
currentRequests.Add(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||||
{
|
{
|
||||||
using (Stream responseStream = response.GetResponseStream())
|
using (Stream responseStream = response.GetResponseStream())
|
||||||
{
|
{
|
||||||
using (Stream memoryStream = new MemoryStream((int)response.ContentLength))
|
buffer = new byte[(int)response.ContentLength];
|
||||||
|
|
||||||
|
using (MemoryStream memoryStream = new MemoryStream(buffer))
|
||||||
{
|
{
|
||||||
responseStream.CopyTo(memoryStream);
|
responseStream.CopyTo(memoryStream);
|
||||||
memoryStream.Position = 0;
|
|
||||||
image = BitmapFrame.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
|
||||||
|
|
||||||
if (IsCached)
|
|
||||||
{
|
|
||||||
string path = CacheFilePath(tile);
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
|
||||||
|
|
||||||
using (Stream fileStream = File.OpenWrite(path))
|
|
||||||
{
|
|
||||||
memoryStream.Position = 0;
|
|
||||||
memoryStream.CopyTo(fileStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -225,10 +217,16 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
catch (WebException exc)
|
catch (WebException exc)
|
||||||
{
|
{
|
||||||
|
buffer = null;
|
||||||
|
|
||||||
if (exc.Status == WebExceptionStatus.ProtocolError)
|
if (exc.Status == WebExceptionStatus.ProtocolError)
|
||||||
{
|
{
|
||||||
TraceInformation("{0} - {1}", tile.Uri, ((HttpWebResponse)exc.Response).StatusCode);
|
TraceInformation("{0} - {1}", tile.Uri, ((HttpWebResponse)exc.Response).StatusCode);
|
||||||
}
|
}
|
||||||
|
else if (exc.Status == WebExceptionStatus.RequestCanceled)
|
||||||
|
{
|
||||||
|
TraceInformation("{0} - {1}", tile.Uri, exc.Status);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TraceWarning("{0} - {1}", tile.Uri, exc.Status);
|
TraceWarning("{0} - {1}", tile.Uri, exc.Status);
|
||||||
|
|
@ -236,20 +234,56 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
catch (Exception exc)
|
catch (Exception exc)
|
||||||
{
|
{
|
||||||
|
buffer = null;
|
||||||
|
|
||||||
TraceWarning("{0} - {1}", tile.Uri, exc.Message);
|
TraceWarning("{0} - {1}", tile.Uri, exc.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
if (request != null)
|
||||||
|
{
|
||||||
|
lock (currentRequests)
|
||||||
|
{
|
||||||
|
currentRequests.Remove(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CreateTileImage(Tile tile, byte[] buffer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BitmapImage bitmap = new BitmapImage();
|
||||||
|
|
||||||
|
using (Stream stream = new MemoryStream(buffer))
|
||||||
|
{
|
||||||
|
bitmap.BeginInit();
|
||||||
|
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||||
|
bitmap.StreamSource = stream;
|
||||||
|
bitmap.EndInit();
|
||||||
|
bitmap.Freeze();
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatcher.BeginInvoke((Action)(() => tile.Image = bitmap));
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
TraceWarning("Creating tile image failed: {0}", exc.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TraceWarning(string format, params object[] args)
|
private static void TraceWarning(string format, params object[] args)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Trace.TraceWarning("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
|
Trace.TraceWarning("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TraceInformation(string format, params object[] args)
|
private static void TraceInformation(string format, params object[] args)
|
||||||
{
|
{
|
||||||
//System.Diagnostics.Trace.TraceInformation("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
|
Trace.TraceInformation("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ using System.Windows.Media;
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fills a rectangular area with map tiles from a TileSource. If the IsCached property is true,
|
/// Fills a rectangular area with map tiles from a TileSource.
|
||||||
/// map tiles are cached in a folder defined by the TileImageLoader.TileCacheFolder property.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ContentProperty("TileSource")]
|
[ContentProperty("TileSource")]
|
||||||
public class TileLayer : DrawingVisual
|
public class TileLayer : DrawingVisual
|
||||||
|
|
@ -30,19 +29,16 @@ namespace MapControl
|
||||||
VisualEdgeMode = EdgeMode.Aliased;
|
VisualEdgeMode = EdgeMode.Aliased;
|
||||||
VisualTransform = new MatrixTransform();
|
VisualTransform = new MatrixTransform();
|
||||||
Name = string.Empty;
|
Name = string.Empty;
|
||||||
ImageType = "png";
|
|
||||||
MinZoomLevel = 1;
|
MinZoomLevel = 1;
|
||||||
MaxZoomLevel = 18;
|
MaxZoomLevel = 18;
|
||||||
MaxDownloads = 8;
|
MaxDownloads = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string ImageType { get; set; }
|
|
||||||
public TileSource TileSource { get; set; }
|
public TileSource TileSource { get; set; }
|
||||||
public int MinZoomLevel { get; set; }
|
public int MinZoomLevel { get; set; }
|
||||||
public int MaxZoomLevel { get; set; }
|
public int MaxZoomLevel { get; set; }
|
||||||
public int MaxDownloads { get; set; }
|
public int MaxDownloads { get; set; }
|
||||||
public bool IsCached { get; set; }
|
|
||||||
public bool HasDarkBackground { get; set; }
|
public bool HasDarkBackground { get; set; }
|
||||||
|
|
||||||
public string Description
|
public string Description
|
||||||
|
|
@ -139,7 +135,7 @@ namespace MapControl
|
||||||
drawingContext.DrawRectangle(tile.Brush, null, tileRect);
|
drawingContext.DrawRectangle(tile.Brush, null, tileRect);
|
||||||
|
|
||||||
//if (tile.ZoomLevel == zoomLevel)
|
//if (tile.ZoomLevel == zoomLevel)
|
||||||
// drawingContext.DrawText(new FormattedText(tile.ToString(), System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black), tileRect.TopLeft);
|
// drawingContext.DrawText(new FormattedText(string.Format("{0}-{1}-{2}", tile.ZoomLevel, tile.X, tile.Y), System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black), tileRect.TopLeft);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<map:TileLayer x:Key="SeamarksTileLayer"
|
<map:TileLayer x:Key="SeamarksTileLayer"
|
||||||
Name="Seamarks" Description="© {y} OpenSeaMap Contributors, CC-BY-SA"
|
Name="Seamarks" Description="© {y} OpenSeaMap Contributors, CC-BY-SA"
|
||||||
TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png"
|
TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png" MinZoomLevel="10" MaxZoomLevel="18"/>
|
||||||
IsCached="False" MinZoomLevel="10" MaxZoomLevel="18"/>
|
|
||||||
<local:SampleItemCollection x:Key="Polylines"/>
|
<local:SampleItemCollection x:Key="Polylines"/>
|
||||||
<local:SampleItemCollection x:Key="Points"/>
|
<local:SampleItemCollection x:Key="Points"/>
|
||||||
<local:SampleItemCollection x:Key="Pushpins"/>
|
<local:SampleItemCollection x:Key="Pushpins"/>
|
||||||
|
|
@ -120,30 +119,25 @@
|
||||||
<ComboBox Name="tileLayerComboBox" ToolTip="Main Tile Layer" Margin="4,0,4,0" DisplayMemberPath="Name" SelectedIndex="0">
|
<ComboBox Name="tileLayerComboBox" ToolTip="Main Tile Layer" Margin="4,0,4,0" DisplayMemberPath="Name" SelectedIndex="0">
|
||||||
<ComboBox.Items>
|
<ComboBox.Items>
|
||||||
<map:TileLayer Name="OpenStreetMap" Description="© {y} OpenStreetMap Contributors, CC-BY-SA"
|
<map:TileLayer Name="OpenStreetMap" Description="© {y} OpenStreetMap Contributors, CC-BY-SA"
|
||||||
TileSource="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png" IsCached="False"/>
|
TileSource="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
|
||||||
<map:TileLayer Name="OpenCycleMap" Description="OpenCycleMap - © {y} Andy Allen & OpenStreetMap Contributors, CC-BY-SA"
|
<map:TileLayer Name="OpenCycleMap" Description="OpenCycleMap - © {y} Andy Allen & OpenStreetMap Contributors, CC-BY-SA"
|
||||||
TileSource="http://{c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png" IsCached="False"/>
|
TileSource="http://{c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png"/>
|
||||||
<map:TileLayer Name="OCM Transport" Description="OpenCycleMap Transport - © {y} Andy Allen & OpenStreetMap Contributors, CC-BY-SA"
|
<map:TileLayer Name="OCM Transport" Description="OpenCycleMap Transport - © {y} Andy Allen & OpenStreetMap Contributors, CC-BY-SA"
|
||||||
TileSource="http://{c}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png" IsCached="False"/>
|
TileSource="http://{c}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png"/>
|
||||||
<map:TileLayer Name="OCM Landscape" Description="OpenCycleMap Landscape - © {y} Andy Allen & OpenStreetMap Contributors, CC-BY-SA"
|
<map:TileLayer Name="OCM Landscape" Description="OpenCycleMap Landscape - © {y} Andy Allen & OpenStreetMap Contributors, CC-BY-SA"
|
||||||
TileSource="http://{c}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png" IsCached="False"/>
|
TileSource="http://{c}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png"/>
|
||||||
<map:TileLayer Name="MapQuest OSM" Description="MapQuest OSM - © {y} MapQuest & OpenStreetMap Contributors"
|
<map:TileLayer Name="MapQuest OSM" Description="MapQuest OSM - © {y} MapQuest & OpenStreetMap Contributors"
|
||||||
TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png" IsCached="False"/>
|
TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png"/>
|
||||||
<!--<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}&y={y}&z={z}"
|
TileSource="http://mt{i}.google.com/vt/x={x}&y={y}&z={z}" MaxZoomLevel="20"/>
|
||||||
IsCached="False" 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&x={x}&y={y}&z={z}"
|
TileSource="http://khm{i}.google.com/kh/v=113&x={x}&y={y}&z={z}" MaxZoomLevel="20" HasDarkBackground="True"/>
|
||||||
ImageType="jpg" IsCached="False" MaxZoomLevel="20" HasDarkBackground="True"/>
|
|
||||||
<map:TileLayer Name="Bing Maps" Description="Bing Maps - © {y} Microsoft Corporation"
|
<map:TileLayer Name="Bing Maps" Description="Bing Maps - © {y} Microsoft Corporation"
|
||||||
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/r{q}.png?g=0&stl=h"
|
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/r{q}.png?g=0&stl=h" MaxZoomLevel="20"/>
|
||||||
IsCached="False" MaxZoomLevel="20"/>
|
|
||||||
<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"
|
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/a{q}.jpeg?g=0" MaxZoomLevel="20" HasDarkBackground="True"/>
|
||||||
ImageType="jpg" IsCached="False" 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&stl=h"
|
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/h{q}.jpeg?g=0&stl=h" MaxZoomLevel="20" HasDarkBackground="True"/>-->
|
||||||
ImageType="jpg" IsCached="False" MaxZoomLevel="20" HasDarkBackground="True"/>-->
|
|
||||||
</ComboBox.Items>
|
</ComboBox.Items>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using MapControl;
|
using MapControl;
|
||||||
|
using Caching;
|
||||||
|
|
||||||
namespace MapControlTestApp
|
namespace MapControlTestApp
|
||||||
{
|
{
|
||||||
|
|
@ -13,6 +15,15 @@ namespace MapControlTestApp
|
||||||
{
|
{
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
|
bool usePersistentCache = false;
|
||||||
|
|
||||||
|
if (usePersistentCache)
|
||||||
|
{
|
||||||
|
string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
||||||
|
string cachePath = Path.Combine(appDataFolder, "MapControl", "Cache.fdb");
|
||||||
|
TileImageLoader.Cache = new FileDbCache("MapControlCache", cachePath) { AutoFlush = false };
|
||||||
|
}
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
ICollection<object> polylines = (ICollection<object>)Resources["Polylines"];
|
ICollection<object> polylines = (ICollection<object>)Resources["Polylines"];
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Runtime.Caching" />
|
||||||
<Reference Include="System.Xaml">
|
<Reference Include="System.Xaml">
|
||||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|
@ -74,6 +75,10 @@
|
||||||
<AppDesigner Include="Properties\" />
|
<AppDesigner Include="Properties\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Caches\FileDbCache\FileDbCache.csproj">
|
||||||
|
<Project>{EF44F661-B98A-4676-927F-85D138F82300}</Project>
|
||||||
|
<Name>FileDbCache</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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue