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

@ -56,14 +56,14 @@ namespace Caching
public FileDbCache(string name, string directory) 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; this.name = name;
@ -78,6 +78,14 @@ namespace Caching
{ {
fileDb.Open(path, false); fileDb.Open(path, false);
Trace.TraceInformation("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, path); 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 catch
{ {
@ -371,9 +379,12 @@ namespace Caching
public void Flush() public void Flush()
{ {
try try
{
if (fileDb.IsOpen)
{ {
fileDb.Flush(); fileDb.Flush();
} }
}
catch (Exception ex) catch (Exception ex)
{ {
Trace.TraceWarning("FileDbCache: FileDb.Flush() failed: {0}", ex.Message); Trace.TraceWarning("FileDbCache: FileDb.Flush() failed: {0}", ex.Message);
@ -383,9 +394,12 @@ namespace Caching
public void Clean() public void Clean()
{ {
try try
{
if (fileDb.IsOpen)
{ {
fileDb.Clean(); fileDb.Clean();
} }
}
catch (Exception ex) catch (Exception ex)
{ {
Trace.TraceWarning("FileDbCache: FileDb.Clean() failed: {0}", ex.Message); Trace.TraceWarning("FileDbCache: FileDb.Clean() failed: {0}", ex.Message);
@ -396,16 +410,6 @@ namespace Caching
{ {
if (fileDb.IsOpen) 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(); fileDb.Close();
} }
} }

View file

@ -13,7 +13,7 @@ using System.Windows.Media.Animation;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <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 /// 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. /// 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. /// 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, e) => ((Map)o).TileLayersPropertyChanged((TileLayerCollection)e.OldValue, (TileLayerCollection)e.NewValue),
(o, v) => ((Map)o).CoerceTileLayersProperty((TileLayerCollection)v))); (o, v) => ((Map)o).CoerceTileLayersProperty((TileLayerCollection)v)));
public static readonly DependencyProperty MainTileLayerProperty = DependencyProperty.Register( public static readonly DependencyProperty BaseTileLayerProperty = DependencyProperty.Register(
"MainTileLayer", typeof(TileLayer), typeof(Map), new FrameworkPropertyMetadata( "BaseTileLayer", typeof(TileLayer), typeof(Map), new FrameworkPropertyMetadata(
(o, e) => ((Map)o).MainTileLayerPropertyChanged((TileLayer)e.NewValue), (o, e) => ((Map)o).BaseTileLayerPropertyChanged((TileLayer)e.NewValue),
(o, v) => ((Map)o).CoerceMainTileLayerProperty((TileLayer)v))); (o, v) => ((Map)o).CoerceBaseTileLayerProperty((TileLayer)v)));
public static readonly DependencyProperty TileOpacityProperty = DependencyProperty.Register( public static readonly DependencyProperty TileOpacityProperty = DependencyProperty.Register(
"TileOpacity", typeof(double), typeof(Map), new FrameworkPropertyMetadata(1d, "TileOpacity", typeof(double), typeof(Map), new FrameworkPropertyMetadata(1d,
@ -112,9 +112,9 @@ namespace MapControl
Loaded += (o, e) => Loaded += (o, e) =>
{ {
if (MainTileLayer == null) if (BaseTileLayer == null)
{ {
MainTileLayer = new TileLayer BaseTileLayer = new TileLayer
{ {
Name = "OpenStreetMap", Name = "OpenStreetMap",
Description = "© {y} OpenStreetMap Contributors, CC-BY-SA", Description = "© {y} OpenStreetMap Contributors, CC-BY-SA",
@ -202,12 +202,12 @@ namespace MapControl
} }
/// <summary> /// <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> /// </summary>
public TileLayer MainTileLayer public TileLayer BaseTileLayer
{ {
get { return (TileLayer)GetValue(MainTileLayerProperty); } get { return (TileLayer)GetValue(BaseTileLayerProperty); }
set { SetValue(MainTileLayerProperty, value); } set { SetValue(BaseTileLayerProperty, value); }
} }
/// <summary> /// <summary>
@ -491,7 +491,7 @@ namespace MapControl
break; break;
} }
UpdateMainTileLayer(); UpdateBaseTileLayer();
} }
private void TileLayersPropertyChanged(TileLayerCollection oldTileLayers, TileLayerCollection newTileLayers) private void TileLayersPropertyChanged(TileLayerCollection oldTileLayers, TileLayerCollection newTileLayers)
@ -509,7 +509,7 @@ namespace MapControl
tileContainer.AddTileLayers(0, newTileLayers); tileContainer.AddTileLayers(0, newTileLayers);
} }
UpdateMainTileLayer(); UpdateBaseTileLayer();
} }
private TileLayerCollection CoerceTileLayersProperty(TileLayerCollection tileLayers) private TileLayerCollection CoerceTileLayersProperty(TileLayerCollection tileLayers)
@ -522,21 +522,21 @@ namespace MapControl
return tileLayers; return tileLayers;
} }
private void MainTileLayerPropertyChanged(TileLayer mainTileLayer) private void BaseTileLayerPropertyChanged(TileLayer baseTileLayer)
{ {
if (mainTileLayer != null) if (baseTileLayer != null)
{ {
if (TileLayers.Count == 0) 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) 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; 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() private void CommonStateChanged()
{ {
if (!IsEnabled) if (!IsEnabled)
@ -128,19 +142,5 @@ namespace MapControl
VisualStateManager.GoToState(this, "Normal", true); 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> /// </summary>
public class TileImageLoader : DispatcherObject 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 TileLayer tileLayer;
private readonly ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>(); private readonly ConcurrentQueue<Tile> pendingTiles = new ConcurrentQueue<Tile>();
@ -151,19 +139,19 @@ namespace MapControl
newTiles.ForEach(tile => newTiles.ForEach(tile =>
{ {
string key = CacheKey(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); pendingTiles.Enqueue(tile);
} }
else if (!CreateTileImage(tile, cachedImage.ImageBuffer)) else if (!CreateTileImage(tile, imageBuffer))
{ {
// got corrupted buffer from cache // got corrupted buffer from cache
Cache.Remove(key); Cache.Remove(key);
pendingTiles.Enqueue(tile); pendingTiles.Enqueue(tile);
} }
else if (cachedImage.CreationTime + CacheUpdateAge < DateTime.UtcNow) else if (GetCreationTime(imageBuffer) + CacheUpdateAge < DateTime.UtcNow)
{ {
// update cached image // update cached image
outdatedTiles.Add(tile); outdatedTiles.Add(tile);
@ -190,18 +178,16 @@ namespace MapControl
tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
byte[] imageBuffer = DownloadImage(tile); byte[] imageBuffer = DownloadImage(tile);
if (imageBuffer != null && if (imageBuffer != null && CreateTileImage(tile, imageBuffer) && Cache != 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) 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) private byte[] DownloadImage(Tile tile)
@ -222,9 +208,9 @@ namespace MapControl
{ {
if (response.ContentLength > 0) 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(); buffer = memoryStream.GetBuffer();
} }
} }
@ -232,7 +218,7 @@ namespace MapControl
{ {
using (MemoryStream memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new MemoryStream())
{ {
responseStream.CopyTo(memoryStream); CopyWithCreationTime(responseStream, memoryStream);
buffer = memoryStream.ToArray(); buffer = memoryStream.ToArray();
} }
} }
@ -265,7 +251,7 @@ namespace MapControl
try try
{ {
using (Stream stream = new MemoryStream(buffer)) using (Stream stream = new MemoryStream(buffer, 0, buffer.Length - 8, false))
{ {
bitmap.BeginInit(); bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.CacheOption = BitmapCacheOption.OnLoad;
@ -284,6 +270,17 @@ 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

@ -5,8 +5,43 @@
xmlns:local="clr-namespace:SampleApplication" xmlns:local="clr-namespace:SampleApplication"
Title="MainWindow" Height="600" Width="800"> Title="MainWindow" Height="600" Width="800">
<Window.Resources> <Window.Resources>
<map:TileLayer x:Key="SeamarksTileLayer" <map:TileLayerCollection x:Key="TileLayers">
Name="Seamarks" Description="© {y} OpenSeaMap 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"/>
<map:TileLayer Name="OpenCycleMap" Description="OpenCycleMap - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png"/>
<map:TileLayer Name="OCM Transport" Description="OpenCycleMap Transport - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png"/>
<map:TileLayer Name="OCM Landscape" Description="OpenCycleMap Landscape - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png"/>
<map:TileLayer Name="MapQuest OSM" Description="MapQuest OSM - © {y} MapQuest &amp; OpenStreetMap Contributors"
TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png"/>
<!-- Note: The providers of the below TileLayers do not allow access to their
map content without using their APIs (i.e. Google Maps API or Bing Maps API).
Hence the declarations below are for demonstration purpose only. -->
<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"/>
<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"/>
<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&amp;stl=h" MaxZoomLevel="20"/>
<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"/>
<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"/>
<!-- The TileLayer below uses an ImageTileSource, which bypasses caching of map tile images -->
<map:TileLayer Name="OSM Uncached" Description="© {y} OpenStreetMap Contributors, CC-BY-SA">
<map:TileLayer.TileSource>
<map:ImageTileSource UriFormat="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
</map:TileLayer.TileSource>
</map:TileLayer>
</map:TileLayerCollection>
<CollectionViewSource x:Key="TileLayersView" Source="{StaticResource TileLayers}"/>
<map:TileLayer x:Key="SeamarksTileLayer" Name="Seamarks" Description="© {y} OpenSeaMap Contributors, CC-BY-SA"
TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png" MinZoomLevel="10" MaxZoomLevel="18"/> TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png" MinZoomLevel="10" MaxZoomLevel="18"/>
<local:SampleItemCollection x:Key="Polylines"/> <local:SampleItemCollection x:Key="Polylines"/>
<local:SampleItemCollection x:Key="Points"/> <local:SampleItemCollection x:Key="Points"/>
@ -90,7 +125,7 @@
<map:Map Name="map" IsManipulationEnabled="True" Margin="2" FontSize="10" <map:Map Name="map" IsManipulationEnabled="True" Margin="2" FontSize="10"
LightForeground="Black" LightBackground="White" DarkForeground="White" DarkBackground="#FF3F3F3F" LightForeground="Black" LightBackground="White" DarkForeground="White" DarkBackground="#FF3F3F3F"
Center="53.5,8.2" ZoomLevel="11" Center="53.5,8.2" ZoomLevel="11"
MainTileLayer="{Binding SelectedItem, ElementName=tileLayerComboBox}" BaseTileLayer="{Binding Source={StaticResource TileLayersView}, Path=CurrentItem}"
ManipulationInertiaStarting="MapManipulationInertiaStarting" ManipulationInertiaStarting="MapManipulationInertiaStarting"
MouseMove="MapMouseMove" MouseLeave="MapMouseLeave"> MouseMove="MapMouseMove" MouseLeave="MapMouseLeave">
<map:MapGraticule Opacity="0.6"/> <map:MapGraticule Opacity="0.6"/>
@ -105,7 +140,7 @@
<map:Pushpin Location="53.5,8.2" Background="Yellow" Foreground="Blue" Content="N 53° 30' E 8° 12'" <map:Pushpin Location="53.5,8.2" Background="Yellow" Foreground="Blue" Content="N 53° 30' E 8° 12'"
Visibility="{Binding (map:MapPanel.ViewportPosition).IsInside, Converter={StaticResource BooleanToVisibilityConverter}, RelativeSource={RelativeSource Self}}"/> Visibility="{Binding (map:MapPanel.ViewportPosition).IsInside, Converter={StaticResource BooleanToVisibilityConverter}, RelativeSource={RelativeSource Self}}"/>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="2,0,0,0" FontSize="10" <TextBlock HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="2,0,0,0" FontSize="10"
Text="{Binding MainTileLayer.Description, ElementName=map}"/> Text="{Binding BaseTileLayer.Description, ElementName=map}"/>
</map:Map> </map:Map>
<Grid Grid.Row="1" Margin="2"> <Grid Grid.Row="1" Margin="2">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -119,43 +154,8 @@
<Slider Name="headingSlider" ToolTip="Heading" Margin="4,0,4,0" Width="100" Minimum="0" Maximum="360" SmallChange="10" LargeChange="45" <Slider Name="headingSlider" ToolTip="Heading" Margin="4,0,4,0" Width="100" Minimum="0" Maximum="360" SmallChange="10" LargeChange="45"
Value="{Binding TargetHeading, ElementName=map}"/> Value="{Binding TargetHeading, ElementName=map}"/>
<CheckBox ToolTip="Map Overlay" Margin="4,0,4,0" VerticalAlignment="Center" Content="Seamarks" Click="SeamarksClick"/> <CheckBox ToolTip="Map Overlay" Margin="4,0,4,0" VerticalAlignment="Center" Content="Seamarks" Click="SeamarksClick"/>
<ComboBox Name="tileLayerComboBox" ToolTip="Main Tile Layer" Margin="4,0,0,0" DisplayMemberPath="Name" SelectedIndex="0"> <ComboBox ToolTip="Main Tile Layer" Margin="4,0,0,0" DisplayMemberPath="Name"
<ComboBox.Items> ItemsSource="{Binding Source={StaticResource TileLayersView}}"/>
<map:TileLayer Name="OpenStreetMap" Description="© {y} OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
<map:TileLayer Name="OpenCycleMap" Description="OpenCycleMap - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png"/>
<map:TileLayer Name="OCM Transport" Description="OpenCycleMap Transport - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png"/>
<map:TileLayer Name="OCM Landscape" Description="OpenCycleMap Landscape - © {y} Andy Allen &amp; OpenStreetMap Contributors, CC-BY-SA"
TileSource="http://{c}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png"/>
<map:TileLayer Name="MapQuest OSM" Description="MapQuest OSM - © {y} MapQuest &amp; OpenStreetMap Contributors"
TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png"/>
<!-- Note: The providers of the below TileLayers do not allow access to their
map content without using their APIs (i.e. Google Maps API or Bing Maps API).
Hence the declarations below are for demonstration purpose only. -->
<!--<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"/>
<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"/>
<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&amp;stl=h" MaxZoomLevel="20"/>
<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"/>
<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"/>-->
<!-- The TileLayer below uses an ImageTileSource, which bypasses caching of map tile images -->
<map:TileLayer Name="OSM Uncached" Description="© {y} OpenStreetMap Contributors, CC-BY-SA">
<map:TileLayer.TileSource>
<map:ImageTileSource UriFormat="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
</map:TileLayer.TileSource>
</map:TileLayer>
</ComboBox.Items>
</ComboBox>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Grid> </Grid>

View file

@ -104,14 +104,14 @@ namespace SampleApplication
}); });
DispatcherTimer timer = new DispatcherTimer(); DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(0.05); timer.Interval = TimeSpan.FromSeconds(0.1);
timer.Tick += MovePoint; timer.Tick += MovePoint;
timer.Start(); timer.Start();
} }
private void MovePoint(object sender, EventArgs e) 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) if (movingPoint.Location.Latitude > 54d)
{ {

View file

@ -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();
}
}
}