Simplified TileImageLoader and TileSource, added ImageTileSource, fixed FileDbCache

This commit is contained in:
ClemensF 2012-07-20 21:57:29 +02:00
parent 300c22a2e7
commit 2fd3f5f8f6
12 changed files with 280 additions and 195 deletions

View file

@ -77,13 +77,12 @@ namespace Caching
try try
{ {
fileDb.Open(path, false); fileDb.Open(path, false);
Trace.TraceInformation("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, path);
} }
catch catch
{ {
CreateDatebase(); CreateDatabase();
} }
Trace.TraceInformation("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, path);
} }
public bool AutoFlush public bool AutoFlush
@ -139,19 +138,14 @@ namespace Caching
{ {
count = fileDb.NumRecords; count = fileDb.NumRecords;
} }
catch (Exception ex1) catch (Exception ex)
{ {
Trace.TraceWarning("FileDbCache: FileDb.NumRecords failed: {0}", ex1.Message); Trace.TraceWarning("FileDbCache: FileDb.NumRecords failed: {0}", ex.Message);
try if (RepairDatabase())
{ {
fileDb.Reindex();
count = fileDb.NumRecords; count = fileDb.NumRecords;
} }
catch (Exception ex2)
{
Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}", ex2.Message);
}
} }
} }
@ -168,19 +162,14 @@ namespace Caching
{ {
record = fileDb.GetRecordByKey(key, new string[] { valueField }, false); record = fileDb.GetRecordByKey(key, new string[] { valueField }, false);
} }
catch (Exception ex1) catch (Exception ex)
{ {
Trace.TraceWarning("FileDbCache: FileDb.GetRecordByKey(\"{0}\") failed: {1}", key, ex1.Message); Trace.TraceWarning("FileDbCache: FileDb.GetRecordByKey(\"{0}\") failed: {1}", key, ex.Message);
try if (RepairDatabase())
{ {
fileDb.Reindex();
record = fileDb.GetRecordByKey(key, new string[] { valueField }, false); record = fileDb.GetRecordByKey(key, new string[] { valueField }, false);
} }
catch (Exception ex2)
{
Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}", ex2.Message);
}
} }
} }
@ -228,7 +217,7 @@ namespace Caching
} }
catch (Exception ex1) catch (Exception ex1)
{ {
Trace.TraceWarning("FileDbCache: Failed deserializing item \"{0}\": {1}", key, ex1.Message); Trace.TraceWarning("FileDbCache: Deserializing item \"{0}\" failed: {1}", key, ex1.Message);
try try
{ {
@ -298,7 +287,7 @@ namespace Caching
} }
catch (Exception ex) catch (Exception ex)
{ {
Trace.TraceWarning("FileDbCache: Failed serializing item \"{0}\": {1}", key, ex.Message); Trace.TraceWarning("FileDbCache: Serializing item \"{0}\" failed: {1}", key, ex.Message);
} }
if (valueBuffer != null) if (valueBuffer != null)
@ -318,19 +307,12 @@ namespace Caching
{ {
AddOrUpdateRecord(key, valueBuffer, expires); AddOrUpdateRecord(key, valueBuffer, expires);
} }
catch (Exception ex1) catch (Exception ex)
{ {
Trace.TraceWarning("FileDbCache: FileDb.UpdateRecordByKey(\"{0}\") failed: {1}", key, ex1.Message); Trace.TraceWarning("FileDbCache: FileDb.UpdateRecordByKey(\"{0}\") failed: {1}", key, ex.Message);
try if (RepairDatabase())
{ {
fileDb.Reindex();
AddOrUpdateRecord(key, valueBuffer, expires);
}
catch (Exception ex2)
{
Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}, creating new database", ex2.Message);
CreateDatebase();
AddOrUpdateRecord(key, valueBuffer, expires); AddOrUpdateRecord(key, valueBuffer, expires);
} }
} }
@ -422,14 +404,35 @@ namespace Caching
} }
catch catch
{ {
fileDb.Reindex();
} }
fileDb.Close(); fileDb.Close();
} }
} }
private void CreateDatebase() private bool RepairDatabase()
{
try
{
fileDb.Reindex();
}
catch (Exception ex)
{
Trace.TraceWarning("FileDbCache: FileDb.Reindex() failed: {0}", ex.Message);
return CreateDatabase();
}
return true;
}
private bool CreateDatabase()
{
if (fileDb.IsOpen)
{
fileDb.Close();
}
try
{ {
if (File.Exists(path)) if (File.Exists(path))
{ {
@ -440,12 +443,23 @@ namespace Caching
Directory.CreateDirectory(Path.GetDirectoryName(path)); Directory.CreateDirectory(Path.GetDirectoryName(path));
} }
fileDb.Create(path, new Field[] fileDb.Create(path,
new Field[]
{ {
new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true }, new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true },
new Field(valueField, DataTypeEnum.Byte) { IsArray = true }, new Field(valueField, DataTypeEnum.Byte) { IsArray = true },
new Field(expiresField, DataTypeEnum.DateTime) new Field(expiresField, DataTypeEnum.DateTime)
}); });
Trace.TraceInformation("FileDbCache: Created database {0}", path);
}
catch (Exception ex)
{
Trace.TraceWarning("FileDbCache: Creating database failed: {0}", ex.Message);
return false;
}
return true;
} }
private void AddOrUpdateRecord(string key, object value, DateTime expires) private void AddOrUpdateRecord(string key, object value, DateTime expires)

View file

@ -115,7 +115,7 @@ namespace MapControl
MainTileLayer = new TileLayer MainTileLayer = new TileLayer
{ {
Description = "© {y} OpenStreetMap Contributors, CC-BY-SA", Description = "© {y} OpenStreetMap Contributors, CC-BY-SA",
TileSource = new OpenStreetMapTileSource { UriFormat = "http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png" } TileSource = new TileSource("http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png")
}; };
} }
}; };

View file

@ -5,6 +5,7 @@
using System; using System;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
namespace MapControl namespace MapControl
{ {
@ -41,12 +42,36 @@ namespace MapControl
set set
{ {
if (Brush.ImageSource == null) if (Brush.ImageSource == null)
{
BitmapImage bitmap = value as BitmapImage;
if (bitmap != null && bitmap.IsDownloading)
{
bitmap.DownloadCompleted += BitmapDownloadCompleted;
bitmap.DownloadFailed += BitmapDownloadFailed;
}
else
{ {
Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation); Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation);
} }
}
Brush.ImageSource = value; Brush.ImageSource = value;
} }
} }
private void BitmapDownloadCompleted(object sender, EventArgs e)
{
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation);
}
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
{
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
Brush.ImageSource = null;
}
} }
} }

View file

@ -37,7 +37,6 @@ namespace MapControl
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 readonly HashSet<HttpWebRequest> currentRequests = new HashSet<HttpWebRequest>();
private int numDownloads;
/// <summary> /// <summary>
/// The ObjectCache used to cache tile images. /// The ObjectCache used to cache tile images.
@ -115,12 +114,12 @@ namespace MapControl
this.tileLayer = tileLayer; this.tileLayer = tileLayer;
} }
internal void BeginDownloadTiles(ICollection<Tile> tiles) internal void BeginGetTiles(ICollection<Tile> tiles)
{ {
ThreadPool.QueueUserWorkItem(BeginDownloadTilesAsync, new List<Tile>(tiles.Where(t => t.Image == null && t.Uri == null))); ThreadPool.QueueUserWorkItem(BeginGetTilesAsync, new List<Tile>(tiles.Where(t => t.Image == null && t.Uri == null)));
} }
internal void CancelDownloadTiles() internal void CancelGetTiles()
{ {
lock (pendingTiles) lock (pendingTiles)
{ {
@ -136,10 +135,20 @@ namespace MapControl
} }
} }
private void BeginDownloadTilesAsync(object newTilesList) private void BeginGetTilesAsync(object newTilesList)
{ {
List<Tile> newTiles = (List<Tile>)newTilesList; List<Tile> newTiles = (List<Tile>)newTilesList;
ImageTileSource imageTileSource = tileLayer.TileSource as ImageTileSource;
if (imageTileSource != null)
{
newTiles.ForEach(tile =>
{
Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.Image = imageTileSource.GetImage(tile.XIndex, tile.Y, tile.ZoomLevel)));
});
}
else
{
lock (pendingTiles) lock (pendingTiles)
{ {
if (Cache == null) if (Cache == null)
@ -175,25 +184,33 @@ namespace MapControl
outdatedTiles.ForEach(tile => pendingTiles.Enqueue(tile)); outdatedTiles.ForEach(tile => pendingTiles.Enqueue(tile));
} }
DownloadNextTiles(null); int numDownloads = Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads);
while (--numDownloads >= 0)
{
ThreadPool.QueueUserWorkItem(DownloadTiles);
}
}
} }
} }
private void DownloadNextTiles(object o) private void DownloadTiles(object o)
{ {
while (pendingTiles.Count > 0 && numDownloads < tileLayer.MaxDownloads) while (pendingTiles.Count > 0)
{ {
Tile tile = pendingTiles.Dequeue(); Tile tile;
lock (pendingTiles)
{
if (pendingTiles.Count == 0)
{
break;
}
tile = pendingTiles.Dequeue();
}
tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
numDownloads++;
ThreadPool.QueueUserWorkItem(DownloadTileAsync, tile);
}
}
private void DownloadTileAsync(object t)
{
Tile tile = (Tile)t;
byte[] imageBuffer = DownloadImage(tile); byte[] imageBuffer = DownloadImage(tile);
if (imageBuffer != null && if (imageBuffer != null &&
@ -202,11 +219,6 @@ namespace MapControl
{ {
Cache.Set(CacheKey(tile), new CachedImage(imageBuffer), new CacheItemPolicy { SlidingExpiration = CacheExpiration }); Cache.Set(CacheKey(tile), new CachedImage(imageBuffer), new CacheItemPolicy { SlidingExpiration = CacheExpiration });
} }
lock (pendingTiles)
{
numDownloads--;
DownloadNextTiles(null);
} }
} }
@ -256,7 +268,7 @@ namespace MapControl
{ {
TraceInformation("{0} - {1}", tile.Uri, ((HttpWebResponse)ex.Response).StatusCode); TraceInformation("{0} - {1}", tile.Uri, ((HttpWebResponse)ex.Response).StatusCode);
} }
else if (ex.Status == WebExceptionStatus.RequestCanceled) // by HttpWebRequest.Abort in CancelDownloadTiles else if (ex.Status == WebExceptionStatus.RequestCanceled) // by HttpWebRequest.Abort in CancelGetTiles
{ {
TraceInformation("{0} - {1}", tile.Uri, ex.Status); TraceInformation("{0} - {1}", tile.Uri, ex.Status);
} }
@ -284,11 +296,11 @@ namespace MapControl
} }
private bool CreateTileImage(Tile tile, byte[] buffer) private bool CreateTileImage(Tile tile, byte[] buffer)
{
try
{ {
BitmapImage bitmap = new BitmapImage(); BitmapImage bitmap = new BitmapImage();
try
{
using (Stream stream = new MemoryStream(buffer)) using (Stream stream = new MemoryStream(buffer))
{ {
bitmap.BeginInit(); bitmap.BeginInit();
@ -297,8 +309,6 @@ namespace MapControl
bitmap.EndInit(); bitmap.EndInit();
bitmap.Freeze(); bitmap.Freeze();
} }
Dispatcher.BeginInvoke((Action)(() => tile.Image = bitmap));
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -306,6 +316,7 @@ namespace MapControl
return false; return false;
} }
Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => tile.Image = bitmap));
return true; return true;
} }

View file

@ -30,14 +30,14 @@ namespace MapControl
Name = string.Empty; Name = string.Empty;
MinZoomLevel = 1; MinZoomLevel = 1;
MaxZoomLevel = 18; MaxZoomLevel = 18;
MaxDownloads = 8; MaxParallelDownloads = 8;
} }
public string Name { get; set; } public string Name { 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 MaxParallelDownloads { get; set; }
public bool HasDarkBackground { get; set; } public bool HasDarkBackground { get; set; }
public string Description public string Description
@ -57,21 +57,21 @@ namespace MapControl
this.grid = grid; this.grid = grid;
this.zoomLevel = zoomLevel; this.zoomLevel = zoomLevel;
tileImageLoader.CancelDownloadTiles(); tileImageLoader.CancelGetTiles();
if (VisualParent != null && TileSource != null) if (VisualParent != null && TileSource != null)
{ {
SelectTiles(); SelectTiles();
RenderTiles(); RenderTiles();
tileImageLoader.BeginDownloadTiles(tiles); tileImageLoader.BeginGetTiles(tiles);
} }
} }
internal void ClearTiles() internal void ClearTiles()
{ {
tiles.Clear(); tiles.Clear();
tileImageLoader.CancelDownloadTiles(); tileImageLoader.CancelGetTiles();
} }
private void SelectTiles() private void SelectTiles()

View file

@ -7,48 +7,100 @@ using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Media;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <summary>
/// Defines the URI of a map tile. /// Provides the URI of a map tile.
/// </summary> /// </summary>
[TypeConverter(typeof(TileSourceTypeConverter))] [TypeConverter(typeof(TileSourceTypeConverter))]
public class TileSource public class TileSource
{ {
public string UriFormat { get; set; } private Func<int, int, int, Uri> getUri;
private string uriFormat = string.Empty;
private int hostIndex = -1;
public TileSource()
{
}
public TileSource(string uriFormat)
{
UriFormat = uriFormat;
}
public string UriFormat
{
get { return uriFormat; }
set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("The value of the UriFormat proprty must not be null or empty or white-space only.");
}
if (value.Contains("{x}") && value.Contains("{y}") && value.Contains("{z}"))
{
if (value.Contains("{c}"))
{
getUri = GetOpenStreetMapUri;
}
else if (value.Contains("{i}"))
{
getUri = GetGoogleMapsUri;
}
else if (value.Contains("{n}"))
{
getUri = GetMapQuestUri;
}
else
{
getUri = GetDefaultUri;
}
}
else if (value.Contains("{q}")) // {i} is optional
{
getUri = GetQuadKeyUri;
}
else if (value.Contains("{w}") && value.Contains("{s}") && value.Contains("{e}") && value.Contains("{n}"))
{
getUri = GetBoundingBoxUri;
}
else
{
throw new ArgumentException("The specified UriFormat is not supported.");
}
uriFormat = value;
}
}
public virtual Uri GetUri(int x, int y, int zoomLevel) public virtual Uri GetUri(int x, int y, int zoomLevel)
{
return getUri != null ? getUri(x, y, zoomLevel) : null;
}
private Uri GetDefaultUri(int x, int y, int zoomLevel)
{ {
return new Uri(UriFormat. return new Uri(UriFormat.
Replace("{x}", x.ToString()). Replace("{x}", x.ToString()).
Replace("{y}", y.ToString()). Replace("{y}", y.ToString()).
Replace("{z}", zoomLevel.ToString())); Replace("{z}", zoomLevel.ToString()));
} }
}
public class OpenStreetMapTileSource : TileSource private Uri GetOpenStreetMapUri(int x, int y, int zoomLevel)
{ {
private static string[] hostChars = { "a", "b", "c" }; hostIndex = (hostIndex + 1) % 3;
private int hostChar = -1;
public override Uri GetUri(int x, int y, int zoomLevel)
{
hostChar = (hostChar + 1) % 3;
return new Uri(UriFormat. return new Uri(UriFormat.
Replace("{c}", hostChars[hostChar]). Replace("{c}", "abc".Substring(hostIndex, 1)).
Replace("{x}", x.ToString()). Replace("{x}", x.ToString()).
Replace("{y}", y.ToString()). Replace("{y}", y.ToString()).
Replace("{z}", zoomLevel.ToString())); Replace("{z}", zoomLevel.ToString()));
} }
}
public class GoogleMapsTileSource : TileSource private Uri GetGoogleMapsUri(int x, int y, int zoomLevel)
{
private int hostIndex = -1;
public override Uri GetUri(int x, int y, int zoomLevel)
{ {
hostIndex = (hostIndex + 1) % 4; hostIndex = (hostIndex + 1) % 4;
@ -58,27 +110,19 @@ namespace MapControl
Replace("{y}", y.ToString()). Replace("{y}", y.ToString()).
Replace("{z}", zoomLevel.ToString())); Replace("{z}", zoomLevel.ToString()));
} }
}
public class MapQuestTileSource : TileSource private Uri GetMapQuestUri(int x, int y, int zoomLevel)
{ {
private int hostNumber; hostIndex = (hostIndex % 4) + 1;
public override Uri GetUri(int x, int y, int zoomLevel)
{
hostNumber = (hostNumber % 4) + 1;
return new Uri(UriFormat. return new Uri(UriFormat.
Replace("{n}", hostNumber.ToString()). Replace("{n}", hostIndex.ToString()).
Replace("{x}", x.ToString()). Replace("{x}", x.ToString()).
Replace("{y}", y.ToString()). Replace("{y}", y.ToString()).
Replace("{z}", zoomLevel.ToString())); Replace("{z}", zoomLevel.ToString()));
} }
}
public class QuadKeyTileSource : TileSource private Uri GetQuadKeyUri(int x, int y, int zoomLevel)
{
public override Uri GetUri(int x, int y, int zoomLevel)
{ {
StringBuilder key = new StringBuilder { Length = zoomLevel }; StringBuilder key = new StringBuilder { Length = zoomLevel };
@ -91,11 +135,8 @@ namespace MapControl
Replace("{i}", key.ToString(key.Length - 1, 1)). Replace("{i}", key.ToString(key.Length - 1, 1)).
Replace("{q}", key.ToString())); Replace("{q}", key.ToString()));
} }
}
public class BoundingBoxTileSource : TileSource private Uri GetBoundingBoxUri(int x, int y, int zoomLevel)
{
public override Uri GetUri(int x, int y, int zoomLevel)
{ {
MercatorTransform t = new MercatorTransform(); MercatorTransform t = new MercatorTransform();
double n = 1 << zoomLevel; double n = 1 << zoomLevel;
@ -114,57 +155,28 @@ namespace MapControl
} }
} }
/// <summary>
/// Provides the image of a map tile. ImageTileSource bypasses downloading
/// and caching of tile images that is performed by TileImageLoader.
/// </summary>
public abstract class ImageTileSource : TileSource
{
public abstract ImageSource GetImage(int x, int y, int zoomLevel);
}
/// <summary>
/// Converts from string to TileSource.
/// </summary>
public class TileSourceTypeConverter : TypeConverter public class TileSourceTypeConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{ {
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); return sourceType == typeof(string);
} }
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{ {
string uriFormat = value as string; return new TileSource(value as string);
if (uriFormat != null)
{
TileSource tileSource = null;
if (uriFormat.Contains("{x}") && uriFormat.Contains("{y}") && uriFormat.Contains("{z}"))
{
if (uriFormat.Contains("{c}"))
{
tileSource = new OpenStreetMapTileSource();
}
else if (uriFormat.Contains("{i}"))
{
tileSource = new GoogleMapsTileSource();
}
else if (uriFormat.Contains("{n}"))
{
tileSource = new MapQuestTileSource();
}
else
{
tileSource = new TileSource();
}
}
else if (uriFormat.Contains("{q}"))
{
tileSource = new QuadKeyTileSource();
}
else if (uriFormat.Contains("{w}") && uriFormat.Contains("{s}") && uriFormat.Contains("{e}") && uriFormat.Contains("{n}"))
{
tileSource = new BoundingBoxTileSource();
}
if (tileSource != null)
{
tileSource.UriFormat = uriFormat;
return tileSource;
}
}
return base.ConvertFrom(context, culture, value);
} }
} }
} }

View file

@ -8,7 +8,7 @@
<applicationSettings> <applicationSettings>
<SampleApplication.Properties.Settings> <SampleApplication.Properties.Settings>
<setting name="UsePersistentCache" serializeAs="String"> <setting name="UsePersistentCache" serializeAs="String">
<value>True</value> <value>False</value>
</setting> </setting>
</SampleApplication.Properties.Settings> </SampleApplication.Properties.Settings>
</applicationSettings> </applicationSettings>

View file

@ -0,0 +1,13 @@
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace SampleApplication
{
public class ImageTileSource : MapControl.ImageTileSource
{
public override ImageSource GetImage(int x, int y, int zoomLevel)
{
return new BitmapImage(GetUri(x, y, zoomLevel));
}
}
}

View file

@ -128,6 +128,7 @@
TileSource="http://{c}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png"/> TileSource="http://{c}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png"/>
<map:TileLayer Name="MapQuest OSM" Description="MapQuest OSM - © {y} MapQuest &amp; OpenStreetMap Contributors" <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"/> 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}&amp;y={y}&amp;z={z}" MaxZoomLevel="20"/> 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" <map:TileLayer Name="Google Images" Description="Google Maps - © {y} Google"
@ -138,6 +139,14 @@
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/a{q}.jpeg?g=0" MaxZoomLevel="20" HasDarkBackground="True"/> 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" <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"/>--> 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 that bypasses caching of map tile images -->
<map:TileLayer Name="OSM Uncached" Description="© {y} OpenStreetMap Contributors, CC-BY-SA">
<map:TileLayer.TileSource>
<local:ImageTileSource UriFormat="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
</map:TileLayer.TileSource>
</map:TileLayer>
</ComboBox.Items> </ComboBox.Items>
</ComboBox> </ComboBox>
</StackPanel> </StackPanel>

View file

@ -1,7 +1,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // This code was generated by a tool.
// Runtime Version:4.0.30319.225 // Runtime Version:4.0.30319.269
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@ -25,7 +25,7 @@ namespace SampleApplication.Properties {
[global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")] [global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool UsePersistentCache { public bool UsePersistentCache {
get { get {
return ((bool)(this["UsePersistentCache"])); return ((bool)(this["UsePersistentCache"]));

View file

@ -3,7 +3,7 @@
<Profiles /> <Profiles />
<Settings> <Settings>
<Setting Name="UsePersistentCache" Type="System.Boolean" Scope="Application"> <Setting Name="UsePersistentCache" Type="System.Boolean" Scope="Application">
<Value Profile="(Default)">True</Value> <Value Profile="(Default)">False</Value>
</Setting> </Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View file

@ -54,6 +54,7 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="ImageTileSource.cs" />
<Compile Include="Properties\Settings.Designer.cs"> <Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput> <DesignTimeSharedInput>True</DesignTimeSharedInput>