Simplified caching in TileImageLoader, FileDbCache deletes expired items on creation, TileLayers collection moved to Window resources in SampleApplication.

This commit is contained in:
ClemensF 2012-08-13 19:16:59 +02:00
parent fbeb01fca3
commit ae4fb7881a
7 changed files with 126 additions and 153 deletions

View file

@ -13,7 +13,7 @@ using System.Windows.Media.Animation;
namespace MapControl
{
/// <summary>
/// The main map control. Draws map content provided by the TileLayers or the MainTileLayer property.
/// The main map control. Draws map content provided by the TileLayers or the BaseTileLayer 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.
/// Map is a MapPanel and hence can contain map overlays like other MapPanels or MapItemsControls.
@ -46,10 +46,10 @@ namespace MapControl
(o, e) => ((Map)o).TileLayersPropertyChanged((TileLayerCollection)e.OldValue, (TileLayerCollection)e.NewValue),
(o, v) => ((Map)o).CoerceTileLayersProperty((TileLayerCollection)v)));
public static readonly DependencyProperty MainTileLayerProperty = DependencyProperty.Register(
"MainTileLayer", typeof(TileLayer), typeof(Map), new FrameworkPropertyMetadata(
(o, e) => ((Map)o).MainTileLayerPropertyChanged((TileLayer)e.NewValue),
(o, v) => ((Map)o).CoerceMainTileLayerProperty((TileLayer)v)));
public static readonly DependencyProperty BaseTileLayerProperty = DependencyProperty.Register(
"BaseTileLayer", typeof(TileLayer), typeof(Map), new FrameworkPropertyMetadata(
(o, e) => ((Map)o).BaseTileLayerPropertyChanged((TileLayer)e.NewValue),
(o, v) => ((Map)o).CoerceBaseTileLayerProperty((TileLayer)v)));
public static readonly DependencyProperty TileOpacityProperty = DependencyProperty.Register(
"TileOpacity", typeof(double), typeof(Map), new FrameworkPropertyMetadata(1d,
@ -112,9 +112,9 @@ namespace MapControl
Loaded += (o, e) =>
{
if (MainTileLayer == null)
if (BaseTileLayer == null)
{
MainTileLayer = new TileLayer
BaseTileLayer = new TileLayer
{
Name = "OpenStreetMap",
Description = "© {y} OpenStreetMap Contributors, CC-BY-SA",
@ -202,12 +202,12 @@ namespace MapControl
}
/// <summary>
/// Gets or sets the main TileLayer used by this Map, i.e. TileLayers[0].
/// Gets or sets the base TileLayer used by this Map, i.e. TileLayers[0].
/// </summary>
public TileLayer MainTileLayer
public TileLayer BaseTileLayer
{
get { return (TileLayer)GetValue(MainTileLayerProperty); }
set { SetValue(MainTileLayerProperty, value); }
get { return (TileLayer)GetValue(BaseTileLayerProperty); }
set { SetValue(BaseTileLayerProperty, value); }
}
/// <summary>
@ -491,7 +491,7 @@ namespace MapControl
break;
}
UpdateMainTileLayer();
UpdateBaseTileLayer();
}
private void TileLayersPropertyChanged(TileLayerCollection oldTileLayers, TileLayerCollection newTileLayers)
@ -509,7 +509,7 @@ namespace MapControl
tileContainer.AddTileLayers(0, newTileLayers);
}
UpdateMainTileLayer();
UpdateBaseTileLayer();
}
private TileLayerCollection CoerceTileLayersProperty(TileLayerCollection tileLayers)
@ -522,21 +522,21 @@ namespace MapControl
return tileLayers;
}
private void MainTileLayerPropertyChanged(TileLayer mainTileLayer)
private void BaseTileLayerPropertyChanged(TileLayer baseTileLayer)
{
if (mainTileLayer != null)
if (baseTileLayer != null)
{
if (TileLayers.Count == 0)
{
TileLayers.Add(mainTileLayer);
TileLayers.Add(baseTileLayer);
}
else if (TileLayers[0] != mainTileLayer)
else if (TileLayers[0] != baseTileLayer)
{
TileLayers[0] = mainTileLayer;
TileLayers[0] = baseTileLayer;
}
}
if (mainTileLayer != null && mainTileLayer.HasDarkBackground)
if (baseTileLayer != null && baseTileLayer.HasDarkBackground)
{
if (DarkForeground != null)
{
@ -562,23 +562,23 @@ namespace MapControl
}
}
private TileLayer CoerceMainTileLayerProperty(TileLayer mainTileLayer)
private TileLayer CoerceBaseTileLayerProperty(TileLayer baseTileLayer)
{
if (mainTileLayer == null && TileLayers.Count > 0)
if (baseTileLayer == null && TileLayers.Count > 0)
{
mainTileLayer = TileLayers[0];
baseTileLayer = TileLayers[0];
}
return mainTileLayer;
return baseTileLayer;
}
private void UpdateMainTileLayer()
private void UpdateBaseTileLayer()
{
TileLayer mainTileLayer = TileLayers.FirstOrDefault();
TileLayer baseTileLayer = TileLayers.FirstOrDefault();
if (MainTileLayer != mainTileLayer)
if (BaseTileLayer != baseTileLayer)
{
MainTileLayer = mainTileLayer;
BaseTileLayer = baseTileLayer;
}
}

View file

@ -113,6 +113,20 @@ namespace MapControl
IsSelected = !IsSelected;
}
private void IsSelectedChanged(bool isSelected)
{
if (isSelected)
{
VisualStateManager.GoToState(this, "Selected", true);
RaiseEvent(new RoutedEventArgs(SelectedEvent));
}
else
{
VisualStateManager.GoToState(this, "Unselected", true);
RaiseEvent(new RoutedEventArgs(UnselectedEvent));
}
}
private void CommonStateChanged()
{
if (!IsEnabled)
@ -128,19 +142,5 @@ namespace MapControl
VisualStateManager.GoToState(this, "Normal", true);
}
}
private void IsSelectedChanged(bool isSelected)
{
if (isSelected)
{
VisualStateManager.GoToState(this, "Selected", true);
RaiseEvent(new RoutedEventArgs(SelectedEvent));
}
else
{
VisualStateManager.GoToState(this, "Unselected", true);
RaiseEvent(new RoutedEventArgs(UnselectedEvent));
}
}
}
}

View file

@ -23,18 +23,6 @@ namespace MapControl
/// </summary>
public class TileImageLoader : DispatcherObject
{
[Serializable]
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 ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>();
@ -151,19 +139,19 @@ namespace MapControl
newTiles.ForEach(tile =>
{
string key = CacheKey(tile);
CachedImage cachedImage = Cache.Get(key) as CachedImage;
byte[] imageBuffer = Cache.Get(key) as byte[];
if (cachedImage == null)
if (imageBuffer == null)
{
pendingTiles.Enqueue(tile);
}
else if (!CreateTileImage(tile, cachedImage.ImageBuffer))
else if (!CreateTileImage(tile, imageBuffer))
{
// got corrupted buffer from cache
Cache.Remove(key);
pendingTiles.Enqueue(tile);
}
else if (cachedImage.CreationTime + CacheUpdateAge < DateTime.UtcNow)
else if (GetCreationTime(imageBuffer) + CacheUpdateAge < DateTime.UtcNow)
{
// update cached image
outdatedTiles.Add(tile);
@ -190,18 +178,16 @@ namespace MapControl
tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
byte[] imageBuffer = DownloadImage(tile);
if (imageBuffer != null &&
CreateTileImage(tile, imageBuffer) &&
Cache != null)
if (imageBuffer != null && CreateTileImage(tile, imageBuffer) && Cache != null)
{
Cache.Set(CacheKey(tile), new CachedImage(imageBuffer), new CacheItemPolicy { SlidingExpiration = CacheExpiration });
Cache.Set(CacheKey(tile), imageBuffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration });
}
}
}
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 byte[] DownloadImage(Tile tile)
@ -222,9 +208,9 @@ namespace MapControl
{
if (response.ContentLength > 0)
{
using (MemoryStream memoryStream = new MemoryStream((int)response.ContentLength))
using (MemoryStream memoryStream = new MemoryStream((int)response.ContentLength + 8))
{
responseStream.CopyTo(memoryStream);
CopyWithCreationTime(responseStream, memoryStream);
buffer = memoryStream.GetBuffer();
}
}
@ -232,7 +218,7 @@ namespace MapControl
{
using (MemoryStream memoryStream = new MemoryStream())
{
responseStream.CopyTo(memoryStream);
CopyWithCreationTime(responseStream, memoryStream);
buffer = memoryStream.ToArray();
}
}
@ -265,7 +251,7 @@ namespace MapControl
try
{
using (Stream stream = new MemoryStream(buffer))
using (Stream stream = new MemoryStream(buffer, 0, buffer.Length - 8, false))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
@ -284,6 +270,17 @@ namespace MapControl
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)
{
Trace.TraceWarning("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));