mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Simplified TileImageLoader and TileSource, added ImageTileSource, fixed FileDbCache
This commit is contained in:
parent
300c22a2e7
commit
2fd3f5f8f6
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
13
SampleApps/SampleApplication/ImageTileSource.cs
Normal file
13
SampleApps/SampleApplication/ImageTileSource.cs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 & OpenStreetMap Contributors"
|
<map:TileLayer Name="MapQuest OSM" Description="MapQuest OSM - © {y} MapQuest & 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}&y={y}&z={z}" MaxZoomLevel="20"/>
|
TileSource="http://mt{i}.google.com/vt/x={x}&y={y}&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&stl=h" MaxZoomLevel="20" HasDarkBackground="True"/>-->
|
TileSource="http://ecn.t{i}.tiles.virtualearth.net/tiles/h{q}.jpeg?g=0&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>
|
||||||
|
|
|
||||||
|
|
@ -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"]));
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue