mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
264 lines
8.7 KiB
C#
264 lines
8.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using System.Windows.Threading;
|
|
|
|
namespace MapControl
|
|
{
|
|
public class TileImageLoader : DispatcherObject
|
|
{
|
|
public static string TileCacheDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapCache");
|
|
public static TimeSpan TileCacheExpiryAge = TimeSpan.FromDays(1);
|
|
|
|
private readonly Queue<Tile> pendingTiles = new Queue<Tile>();
|
|
private int numDownloads;
|
|
|
|
internal int MaxDownloads;
|
|
internal string TileLayerName;
|
|
|
|
internal int TilesPending
|
|
{
|
|
get { return pendingTiles.Count; }
|
|
}
|
|
|
|
internal void BeginDownloadTiles(ICollection<Tile> tiles)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(BeginDownloadTiles, new List<Tile>(tiles.Reverse().Where(t => t.LoadState == TileLoadState.NotLoaded)));
|
|
}
|
|
|
|
internal void EndDownloadTiles()
|
|
{
|
|
lock (pendingTiles)
|
|
{
|
|
pendingTiles.Clear();
|
|
}
|
|
}
|
|
|
|
private void BeginDownloadTiles(object newTilesList)
|
|
{
|
|
List<Tile> newTiles = (List<Tile>)newTilesList;
|
|
|
|
lock (pendingTiles)
|
|
{
|
|
if (!string.IsNullOrEmpty(TileCacheDirectory) &&
|
|
!string.IsNullOrEmpty(TileLayerName))
|
|
{
|
|
List<Tile> expiredTiles = new List<Tile>(newTiles.Count);
|
|
|
|
newTiles.ForEach(tile =>
|
|
{
|
|
bool cacheExpired;
|
|
ImageSource image = GetCachedImage(tile, out cacheExpired);
|
|
|
|
if (image != null)
|
|
{
|
|
Dispatcher.BeginInvoke((Action)(() => tile.Image = image));
|
|
|
|
if (cacheExpired)
|
|
{
|
|
expiredTiles.Add(tile); // enqueue later
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pendingTiles.Enqueue(tile);
|
|
}
|
|
});
|
|
|
|
expiredTiles.ForEach(tile => pendingTiles.Enqueue(tile));
|
|
}
|
|
else
|
|
{
|
|
newTiles.ForEach(tile => pendingTiles.Enqueue(tile));
|
|
}
|
|
|
|
DownloadNextTiles(null);
|
|
}
|
|
}
|
|
|
|
private void DownloadNextTiles(object o)
|
|
{
|
|
while (pendingTiles.Count > 0 && numDownloads < MaxDownloads)
|
|
{
|
|
Tile tile = pendingTiles.Dequeue();
|
|
tile.LoadState = TileLoadState.Loading;
|
|
numDownloads++;
|
|
|
|
ThreadPool.QueueUserWorkItem(DownloadTile, tile);
|
|
}
|
|
}
|
|
|
|
private void DownloadTile(object t)
|
|
{
|
|
Tile tile = (Tile)t;
|
|
ImageSource image = DownloadImage(tile);
|
|
|
|
Dispatcher.BeginInvoke((Action)(() => tile.Image = image));
|
|
|
|
lock (pendingTiles)
|
|
{
|
|
numDownloads--;
|
|
DownloadNextTiles(null);
|
|
}
|
|
}
|
|
|
|
private ImageSource GetCachedImage(Tile tile, out bool expired)
|
|
{
|
|
string tileDir = TileDirectory(tile);
|
|
ImageSource image = null;
|
|
expired = false;
|
|
|
|
try
|
|
{
|
|
if (Directory.Exists(tileDir))
|
|
{
|
|
string[] tilePath = Directory.GetFiles(tileDir, string.Format("{0}.*", tile.Y));
|
|
|
|
if (tilePath.Length > 0)
|
|
{
|
|
try
|
|
{
|
|
using (Stream fileStream = File.OpenRead(tilePath[0]))
|
|
{
|
|
image = BitmapFrame.Create(fileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
|
}
|
|
|
|
expired = File.GetLastWriteTime(tilePath[0]) + TileCacheExpiryAge <= DateTime.Now;
|
|
|
|
TraceInformation(expired ? "{0} - Cache Expired" : "{0} - Cached", tile.Uri);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
TraceWarning("{0} - {1}", tilePath[0], exc.Message);
|
|
File.Delete(tilePath[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
TraceWarning("{0} - {1}", tileDir, exc.Message);
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
private ImageSource DownloadImage(Tile tile)
|
|
{
|
|
ImageSource image = null;
|
|
|
|
try
|
|
{
|
|
TraceInformation("{0} - Requesting", tile.Uri);
|
|
|
|
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(tile.Uri);
|
|
webRequest.UserAgent = typeof(TileImageLoader).ToString();
|
|
webRequest.KeepAlive = true;
|
|
|
|
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
|
|
{
|
|
using (Stream responseStream = response.GetResponseStream())
|
|
{
|
|
using (Stream memoryStream = new MemoryStream((int)response.ContentLength))
|
|
{
|
|
responseStream.CopyTo(memoryStream);
|
|
memoryStream.Position = 0;
|
|
|
|
BitmapDecoder decoder = BitmapDecoder.Create(memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
|
image = decoder.Frames[0];
|
|
|
|
string tilePath;
|
|
|
|
if (!string.IsNullOrEmpty(TileCacheDirectory) &&
|
|
!string.IsNullOrEmpty(TileLayerName) &&
|
|
(tilePath = TilePath(tile, decoder)) != null)
|
|
{
|
|
Directory.CreateDirectory(Path.GetDirectoryName(tilePath));
|
|
|
|
using (Stream fileStream = File.OpenWrite(tilePath))
|
|
{
|
|
memoryStream.Position = 0;
|
|
memoryStream.CopyTo(fileStream);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TraceInformation("{0} - Completed", tile.Uri);
|
|
}
|
|
catch (WebException exc)
|
|
{
|
|
if (exc.Status == WebExceptionStatus.ProtocolError)
|
|
{
|
|
TraceInformation("{0} - {1}", tile.Uri, ((HttpWebResponse)exc.Response).StatusCode);
|
|
}
|
|
else
|
|
{
|
|
TraceWarning("{0} - {1}", tile.Uri, exc.Status);
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
TraceWarning("{0} - {1}", tile.Uri, exc.Message);
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
private string TileDirectory(Tile tile)
|
|
{
|
|
return Path.Combine(TileCacheDirectory, TileLayerName, tile.ZoomLevel.ToString(), tile.XIndex.ToString());
|
|
}
|
|
|
|
private string TilePath(Tile tile, BitmapDecoder decoder)
|
|
{
|
|
string extension;
|
|
|
|
if (decoder is PngBitmapDecoder)
|
|
{
|
|
extension = "png";
|
|
}
|
|
else if (decoder is JpegBitmapDecoder)
|
|
{
|
|
extension = "jpg";
|
|
}
|
|
else if (decoder is BmpBitmapDecoder)
|
|
{
|
|
extension = "bmp";
|
|
}
|
|
else if (decoder is GifBitmapDecoder)
|
|
{
|
|
extension = "gif";
|
|
}
|
|
else if (decoder is TiffBitmapDecoder)
|
|
{
|
|
extension = "tif";
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return Path.Combine(TileDirectory(tile), string.Format("{0}.{1}", tile.Y, extension));
|
|
}
|
|
|
|
private static void TraceWarning(string format, params object[] args)
|
|
{
|
|
System.Diagnostics.Trace.TraceWarning("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
|
|
}
|
|
|
|
private static void TraceInformation(string format, params object[] args)
|
|
{
|
|
#if DEBUG
|
|
System.Diagnostics.Trace.TraceInformation("[{0:00}] {1}", Thread.CurrentThread.ManagedThreadId, string.Format(format, args));
|
|
#endif
|
|
}
|
|
}
|
|
}
|