diff --git a/MapControl/MapControl.csproj b/MapControl/MapControl.csproj
index d8a3df6c..dce34dd9 100644
--- a/MapControl/MapControl.csproj
+++ b/MapControl/MapControl.csproj
@@ -12,7 +12,8 @@
MapControl
v4.0
512
- Client
+
+
true
@@ -22,6 +23,7 @@
TRACE;DEBUG
prompt
4
+ false
none
@@ -31,13 +33,14 @@
prompt
4
+ false
-
+
diff --git a/MapControl/TileImageLoader.cs b/MapControl/TileImageLoader.cs
index 433074d4..f30958f2 100644
--- a/MapControl/TileImageLoader.cs
+++ b/MapControl/TileImageLoader.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
+using System.Runtime.Caching;
using System.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
@@ -21,18 +22,20 @@ namespace MapControl
public class TileImageLoader : DispatcherObject
{
public static string TileCacheFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl Cache");
- public static TimeSpan TileCacheExpiryAge = TimeSpan.FromDays(1);
+ public static TimeSpan TileCacheExpiryAge = TimeSpan.FromDays(1d);
+ private readonly TileLayer tileLayer;
private readonly Queue pendingTiles = new Queue();
private int numDownloads;
- internal int MaxDownloads;
- internal string TileLayerName;
- internal TileSource TileSource;
+ public TileImageLoader(TileLayer tileLayer)
+ {
+ this.tileLayer = tileLayer;
+ }
private bool IsCached
{
- get { return !string.IsNullOrEmpty(TileCacheFolder) && !string.IsNullOrEmpty(TileLayerName); }
+ get { return tileLayer.IsCached && !string.IsNullOrEmpty(TileCacheFolder); }
}
internal void StartDownloadTiles(ICollection tiles)
@@ -51,39 +54,41 @@ namespace MapControl
private void StartDownloadTilesAsync(object newTilesList)
{
List newTiles = (List)newTilesList;
+ List expiredTiles = new List(newTiles.Count);
lock (pendingTiles)
{
- if (IsCached)
+ newTiles.ForEach(tile =>
{
- List expiredTiles = new List(newTiles.Count);
+ ImageSource image = GetMemoryCachedImage(tile);
- newTiles.ForEach(tile =>
+ if (image == null && IsCached)
{
- bool cacheExpired;
- ImageSource image = GetCachedImage(tile, out cacheExpired);
+ bool fileCacheExpired;
+ image = GetFileCachedImage(tile, out fileCacheExpired);
if (image != null)
{
- Dispatcher.BeginInvoke((Action)(() => tile.Image = image));
+ SetMemoryCachedImage(tile, image);
- if (cacheExpired)
+ if (fileCacheExpired)
{
expiredTiles.Add(tile); // enqueue later
}
}
- else
- {
- pendingTiles.Enqueue(tile);
- }
- });
+ }
- expiredTiles.ForEach(tile => pendingTiles.Enqueue(tile));
- }
- else
- {
- newTiles.ForEach(tile => pendingTiles.Enqueue(tile));
- }
+ if (image != null)
+ {
+ Dispatcher.BeginInvoke((Action)(() => tile.Image = image));
+ }
+ else
+ {
+ pendingTiles.Enqueue(tile);
+ }
+ });
+
+ expiredTiles.ForEach(tile => pendingTiles.Enqueue(tile));
DownloadNextTiles(null);
}
@@ -91,10 +96,10 @@ namespace MapControl
private void DownloadNextTiles(object o)
{
- while (pendingTiles.Count > 0 && numDownloads < MaxDownloads)
+ while (pendingTiles.Count > 0 && numDownloads < tileLayer.MaxDownloads)
{
Tile tile = pendingTiles.Dequeue();
- tile.Uri = TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
+ tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
numDownloads++;
ThreadPool.QueueUserWorkItem(DownloadTileAsync, tile);
@@ -108,53 +113,72 @@ namespace MapControl
if (image != null)
{
+ SetMemoryCachedImage(tile, image);
+
Dispatcher.BeginInvoke((Action)(() => tile.Image = image));
}
lock (pendingTiles)
{
- tile.Uri = null;
numDownloads--;
DownloadNextTiles(null);
}
}
- private ImageSource GetCachedImage(Tile tile, out bool expired)
+ private string MemoryCacheKey(Tile tile)
{
- string tileDir = TileDirectory(tile);
+ return string.Format("{0}/{1}/{2}/{3}", tileLayer.Name, tile.ZoomLevel, tile.XIndex, tile.Y);
+ }
+
+ private string CacheFilePath(Tile tile)
+ {
+ return string.Format("{0}.{1}",
+ Path.Combine(TileCacheFolder, tileLayer.Name, tile.ZoomLevel.ToString(), tile.XIndex.ToString(), tile.Y.ToString()),
+ 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;
- try
+ if (File.Exists(path))
{
- if (Directory.Exists(tileDir))
+ try
{
- string tilePath = Directory.GetFiles(tileDir, string.Format("{0}.*", tile.Y)).FirstOrDefault();
-
- if (tilePath != null)
+ using (Stream fileStream = File.OpenRead(path))
{
- try
- {
- using (Stream fileStream = File.OpenRead(tilePath))
- {
- image = BitmapFrame.Create(fileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
- }
-
- expired = File.GetLastWriteTime(tilePath) + TileCacheExpiryAge <= DateTime.Now;
-
- TraceInformation(expired ? "{0} - Cache Expired" : "{0} - Cached", tilePath);
- }
- catch (Exception exc)
- {
- TraceWarning("{0} - {1}", tilePath, exc.Message);
- File.Delete(tilePath);
- }
+ 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);
}
- }
- catch (Exception exc)
- {
- TraceWarning("{0} - {1}", tileDir, exc.Message);
}
return image;
@@ -180,17 +204,14 @@ namespace MapControl
{
responseStream.CopyTo(memoryStream);
memoryStream.Position = 0;
+ image = BitmapFrame.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
- BitmapDecoder decoder = BitmapDecoder.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
- image = decoder.Frames[0];
-
- string tilePath;
-
- if (IsCached && (tilePath = TilePath(tile, decoder)) != null)
+ if (IsCached)
{
- Directory.CreateDirectory(Path.GetDirectoryName(tilePath));
+ string path = CacheFilePath(tile);
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
- using (Stream fileStream = File.OpenWrite(tilePath))
+ using (Stream fileStream = File.OpenWrite(path))
{
memoryStream.Position = 0;
memoryStream.CopyTo(fileStream);
@@ -221,43 +242,6 @@ namespace MapControl
return image;
}
- private string TileDirectory(Tile tile)
- {
- return Path.Combine(TileCacheFolder, TileLayerName, tile.ZoomLevel.ToString(), tile.XIndex.ToString());
- }
-
- private string TilePath(Tile tile, BitmapDecoder decoder)
- {
- string extension;
-
- if (decoder is PngBitmapDecoder)
- {
- extension = "png";
- }
- else if (decoder is JpegBitmapDecoder)
- {
- extension = "jpg";
- }
- else if (decoder is BmpBitmapDecoder)
- {
- extension = "bmp";
- }
- else if (decoder is GifBitmapDecoder)
- {
- extension = "gif";
- }
- else if (decoder is TiffBitmapDecoder)
- {
- extension = "tif";
- }
- else
- {
- return null;
- }
-
- return Path.Combine(TileDirectory(tile), string.Format("{0}.{1}", tile.Y, extension));
- }
-
private static void TraceWarning(string format, params object[] args)
{
System.Diagnostics.Trace.TraceWarning("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
@@ -265,7 +249,7 @@ namespace MapControl
private static void TraceInformation(string format, params object[] args)
{
- System.Diagnostics.Trace.TraceInformation("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
+ //System.Diagnostics.Trace.TraceInformation("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
}
}
}
diff --git a/MapControl/TileLayer.cs b/MapControl/TileLayer.cs
index 69a58ec2..aefa105a 100644
--- a/MapControl/TileLayer.cs
+++ b/MapControl/TileLayer.cs
@@ -18,58 +18,32 @@ namespace MapControl
[ContentProperty("TileSource")]
public class TileLayer : DrawingVisual
{
- private readonly TileImageLoader tileImageLoader = new TileImageLoader();
+ private readonly TileImageLoader tileImageLoader;
private readonly List tiles = new List();
- private bool isCached = false;
- private string name = string.Empty;
private string description = string.Empty;
private Int32Rect grid;
private int zoomLevel;
public TileLayer()
{
+ tileImageLoader = new TileImageLoader(this);
VisualEdgeMode = EdgeMode.Aliased;
VisualTransform = new MatrixTransform();
+ Name = string.Empty;
+ ImageType = "png";
MinZoomLevel = 1;
MaxZoomLevel = 18;
MaxDownloads = 8;
}
- public bool HasDarkBackground { get; set; }
+ public string Name { get; set; }
+ public string ImageType { get; set; }
+ public TileSource TileSource { get; set; }
public int MinZoomLevel { get; set; }
public int MaxZoomLevel { get; set; }
-
- public int MaxDownloads
- {
- get { return tileImageLoader.MaxDownloads; }
- set { tileImageLoader.MaxDownloads = value; }
- }
-
- public TileSource TileSource
- {
- get { return tileImageLoader.TileSource; }
- set { tileImageLoader.TileSource = value; }
- }
-
- public bool IsCached
- {
- get { return isCached; }
- set
- {
- isCached = value;
- tileImageLoader.TileLayerName = isCached ? name : null;
- }
- }
-
- public string Name
- {
- get { return name; }
- set
- {
- name = value;
- tileImageLoader.TileLayerName = isCached ? name : null;
- }
- }
+ public int MaxDownloads { get; set; }
+ public bool IsCached { get; set; }
+ public bool HasDarkBackground { get; set; }
public string Description
{
@@ -150,7 +124,7 @@ namespace MapControl
tiles.Sort((t1, t2) => t1.ZoomLevel - t2.ZoomLevel);
- System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, string.Join(", ", tiles.Select(t => t.ZoomLevel.ToString())));
+ //System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, string.Join(", ", tiles.Select(t => t.ZoomLevel.ToString())));
}
private void RenderTiles()
@@ -170,4 +144,4 @@ namespace MapControl
}
}
}
-}
\ No newline at end of file
+}
diff --git a/TestApplication/MainWindow.xaml b/TestApplication/MainWindow.xaml
index 2d1078b1..3a63fdd8 100644
--- a/TestApplication/MainWindow.xaml
+++ b/TestApplication/MainWindow.xaml
@@ -120,35 +120,30 @@
+ TileSource="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png" IsCached="False"/>
+ TileSource="http://{c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png" IsCached="False"/>
+ TileSource="http://{c}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png" IsCached="False"/>
+ TileSource="http://{c}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png" IsCached="False"/>
+ TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png" IsCached="False"/>
+ ImageType="jpg" IsCached="False" MaxZoomLevel="20" HasDarkBackground="True"/>-->
diff --git a/TestApplication/TestApplication.csproj b/TestApplication/TestApplication.csproj
index 07180684..56948b58 100644
--- a/TestApplication/TestApplication.csproj
+++ b/TestApplication/TestApplication.csproj
@@ -11,7 +11,8 @@
MapControlTestApp
MapControlTestApp
v4.0
- Client
+
+
512
{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
4
@@ -25,6 +26,7 @@
DEBUG;TRACE
prompt
4
+ false
x86
@@ -34,6 +36,7 @@
TRACE
prompt
4
+ false