diff --git a/Caching/FileDbCache/FileDbCache.cs b/Caching/FileDbCache/FileDbCache.cs index 5cf5531d..7f0748f1 100644 --- a/Caching/FileDbCache/FileDbCache.cs +++ b/Caching/FileDbCache/FileDbCache.cs @@ -56,14 +56,14 @@ namespace Caching public FileDbCache(string name, string directory) { - if (string.IsNullOrEmpty(name)) + if (string.IsNullOrWhiteSpace(name)) { - throw new ArgumentException("The parameter name must not be null or empty."); + throw new ArgumentException("The parameter name must not be null or empty or only white-space."); } - if (string.IsNullOrEmpty(directory)) + if (string.IsNullOrWhiteSpace(directory)) { - throw new ArgumentException("The parameter directory must not be null or empty."); + throw new ArgumentException("The parameter directory must not be null or empty or only white-space."); } this.name = name; @@ -78,6 +78,14 @@ namespace Caching { fileDb.Open(path, false); Trace.TraceInformation("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, path); + + fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, EqualityEnum.LessThan)); + + if (fileDb.NumDeleted > 0) + { + Trace.TraceInformation("FileDbCache: Deleted {0} expired items", fileDb.NumDeleted); + fileDb.Clean(); + } } catch { @@ -372,7 +380,10 @@ namespace Caching { try { - fileDb.Flush(); + if (fileDb.IsOpen) + { + fileDb.Flush(); + } } catch (Exception ex) { @@ -384,7 +395,10 @@ namespace Caching { try { - fileDb.Clean(); + if (fileDb.IsOpen) + { + fileDb.Clean(); + } } catch (Exception ex) { @@ -396,16 +410,6 @@ namespace Caching { if (fileDb.IsOpen) { - try - { - fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, EqualityEnum.LessThanOrEqual)); - Trace.TraceInformation("FileDbCache: Deleted {0} expired items", fileDb.NumDeleted); - fileDb.Clean(); - } - catch - { - } - fileDb.Close(); } } diff --git a/MapControl/Map.cs b/MapControl/Map.cs index dd7256da..8ea5f582 100644 --- a/MapControl/Map.cs +++ b/MapControl/Map.cs @@ -13,7 +13,7 @@ using System.Windows.Media.Animation; namespace MapControl { /// - /// 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 } /// - /// 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]. /// - public TileLayer MainTileLayer + public TileLayer BaseTileLayer { - get { return (TileLayer)GetValue(MainTileLayerProperty); } - set { SetValue(MainTileLayerProperty, value); } + get { return (TileLayer)GetValue(BaseTileLayerProperty); } + set { SetValue(BaseTileLayerProperty, value); } } /// @@ -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; } } diff --git a/MapControl/MapItem.cs b/MapControl/MapItem.cs index bade02bb..4413aa9d 100644 --- a/MapControl/MapItem.cs +++ b/MapControl/MapItem.cs @@ -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)); - } - } } } diff --git a/MapControl/TileImageLoader.cs b/MapControl/TileImageLoader.cs index 00648fbc..75a8a664 100644 --- a/MapControl/TileImageLoader.cs +++ b/MapControl/TileImageLoader.cs @@ -23,18 +23,6 @@ namespace MapControl /// 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 pendingTiles = new ConcurrentQueue(); @@ -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)); diff --git a/SampleApps/SampleApplication/MainWindow.xaml b/SampleApps/SampleApplication/MainWindow.xaml index 9d2bad55..1f10037a 100644 --- a/SampleApps/SampleApplication/MainWindow.xaml +++ b/SampleApps/SampleApplication/MainWindow.xaml @@ -5,8 +5,43 @@ xmlns:local="clr-namespace:SampleApplication" Title="MainWindow" Height="600" Width="800"> - + + + + + + + + + + + + + + + + + + + + + + + + @@ -90,7 +125,7 @@ @@ -105,7 +140,7 @@ + Text="{Binding BaseTileLayer.Description, ElementName=map}"/> @@ -119,43 +154,8 @@ - - - - - - - - - - - - - - - - - - - - - + diff --git a/SampleApps/SampleApplication/MainWindow.xaml.cs b/SampleApps/SampleApplication/MainWindow.xaml.cs index 01afc270..f4d7b241 100644 --- a/SampleApps/SampleApplication/MainWindow.xaml.cs +++ b/SampleApps/SampleApplication/MainWindow.xaml.cs @@ -104,14 +104,14 @@ namespace SampleApplication }); DispatcherTimer timer = new DispatcherTimer(); - timer.Interval = TimeSpan.FromSeconds(0.05); + timer.Interval = TimeSpan.FromSeconds(0.1); timer.Tick += MovePoint; timer.Start(); } private void MovePoint(object sender, EventArgs e) { - movingPoint.Location = new Location(movingPoint.Location.Latitude + 0.0005, movingPoint.Location.Longitude + 0.001); + movingPoint.Location = new Location(movingPoint.Location.Latitude + 0.001, movingPoint.Location.Longitude + 0.002); if (movingPoint.Location.Latitude > 54d) { diff --git a/SampleApps/SampleApplication/MapBackgroundConverter.cs b/SampleApps/SampleApplication/MapBackgroundConverter.cs deleted file mode 100644 index 26ecf976..00000000 --- a/SampleApps/SampleApplication/MapBackgroundConverter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using System.Windows.Media; -using MapControl; - -namespace MapControlTestApp -{ - class MapBackgroundConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - MapBackground mapBackground = (MapBackground)value; - - if (parameter as string == "Foreground") - { - return mapBackground == MapBackground.Light ? Brushes.Black : Brushes.White; - } - - return mapBackground == MapBackground.Light ? Brushes.White : Brushes.Black; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -}