namespace GMap.NET
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using GMap.NET.CacheProviders;
using GMap.NET.Internals;
using GMap.NET.MapProviders;
#if PocketPC
using OpenNETCF.ComponentModel;
using OpenNETCF.Threading;
using Thread=OpenNETCF.Threading.Thread2;
#endif
///
/// maps manager
///
public class GMaps : Singleton
{
///
/// tile access mode
///
public AccessMode Mode = AccessMode.ServerAndCache;
///
/// is map ussing cache for routing
///
public bool UseRouteCache = true;
///
/// is map using cache for geocoder
///
public bool UseGeocoderCache = true;
///
/// is map using cache for directions
///
public bool UseDirectionsCache = true;
///
/// is map using cache for placemarks
///
public bool UsePlacemarkCache = true;
///
/// is map ussing cache for other url
///
public bool UseUrlCache = true;
///
/// is map using memory cache for tiles
///
public bool UseMemoryCache = true;
///
/// primary cache provider, by default: ultra fast SQLite!
///
public PureImageCache PrimaryCache
{
get
{
return Cache.Instance.ImageCache;
}
set
{
Cache.Instance.ImageCache = value;
}
}
///
/// secondary cache provider, by default: none,
/// use it if you have server in your local network
///
public PureImageCache SecondaryCache
{
get
{
return Cache.Instance.ImageCacheSecond;
}
set
{
Cache.Instance.ImageCacheSecond = value;
}
}
///
/// MemoryCache provider
///
public readonly MemoryCache MemoryCache = new MemoryCache();
///
/// load tiles in random sequence
///
public bool ShuffleTilesOnLoad = false;
///
/// tile queue to cache
///
readonly Queue tileCacheQueue = new Queue();
bool? isRunningOnMono;
///
/// return true if running on mono
///
///
public bool IsRunningOnMono
{
get
{
if(!isRunningOnMono.HasValue)
{
try
{
isRunningOnMono = (Type.GetType("Mono.Runtime") != null);
return isRunningOnMono.Value;
}
catch
{
}
}
else
{
return isRunningOnMono.Value;
}
return false;
}
}
///
/// cache worker
///
Thread CacheEngine;
internal readonly AutoResetEvent WaitForCache = new AutoResetEvent(false);
public GMaps()
{
#region singleton check
if(Instance != null)
{
throw (new Exception("You have tried to create a new singleton class where you should have instanced it. Replace your \"new class()\" with \"class.Instance\""));
}
#endregion
ServicePointManager.DefaultConnectionLimit = 5;
}
#if !PocketPC
///
/// triggers dynamic sqlite loading,
/// call this before you use sqlite for other reasons than caching maps
///
public void SQLitePing()
{
#if SQLite
#if !MONO
SQLitePureImageCache.Ping();
#endif
#endif
}
#endif
#region -- Stuff --
#if !PocketPC
///
/// exports current map cache to GMDB file
/// if file exsist only new records will be added
/// otherwise file will be created and all data exported
///
///
///
public bool ExportToGMDB(string file)
{
#if SQLite
if(PrimaryCache is SQLitePureImageCache)
{
StringBuilder db = new StringBuilder((PrimaryCache as SQLitePureImageCache).GtileCache);
db.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}Data.gmdb", GMapProvider.LanguageStr, Path.DirectorySeparatorChar);
return SQLitePureImageCache.ExportMapDataToDB(db.ToString(), file);
}
#endif
return false;
}
///
/// imports GMDB file to current map cache
/// only new records will be added
///
///
///
public bool ImportFromGMDB(string file)
{
#if SQLite
if(PrimaryCache is GMap.NET.CacheProviders.SQLitePureImageCache)
{
StringBuilder db = new StringBuilder((PrimaryCache as SQLitePureImageCache).GtileCache);
db.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}Data.gmdb", GMapProvider.LanguageStr, Path.DirectorySeparatorChar);
return SQLitePureImageCache.ExportMapDataToDB(file, db.ToString());
}
#endif
return false;
}
#if SQLite
///
/// optimizes map database, *.gmdb
///
/// database file name or null to optimize current user db
///
public bool OptimizeMapDb(string file)
{
if(PrimaryCache is GMap.NET.CacheProviders.SQLitePureImageCache)
{
if(string.IsNullOrEmpty(file))
{
StringBuilder db = new StringBuilder((PrimaryCache as SQLitePureImageCache).GtileCache);
db.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}Data.gmdb", GMapProvider.LanguageStr, Path.DirectorySeparatorChar);
return SQLitePureImageCache.VacuumDb(db.ToString());
}
else
{
return SQLitePureImageCache.VacuumDb(file);
}
}
return false;
}
#endif
#endif
///
/// enqueueens tile to cache
///
///
void EnqueueCacheTask(CacheQueueItem task)
{
lock(tileCacheQueue)
{
if(!tileCacheQueue.Contains(task))
{
Debug.WriteLine("EnqueueCacheTask: " + task);
tileCacheQueue.Enqueue(task);
if(CacheEngine != null && CacheEngine.IsAlive)
{
WaitForCache.Set();
}
#if PocketPC
else if(CacheEngine == null || CacheEngine.State == ThreadState.Stopped || CacheEngine.State == ThreadState.Unstarted)
#else
else if(CacheEngine == null || CacheEngine.ThreadState == System.Threading.ThreadState.Stopped || CacheEngine.ThreadState == System.Threading.ThreadState.Unstarted)
#endif
{
CacheEngine = null;
CacheEngine = new Thread(new ThreadStart(CacheEngineLoop));
CacheEngine.Name = "CacheEngine";
CacheEngine.IsBackground = false;
CacheEngine.Priority = ThreadPriority.Lowest;
abortCacheLoop = false;
CacheEngine.Start();
}
}
}
}
volatile bool abortCacheLoop = false;
internal volatile bool noMapInstances = false;
public TileCacheComplete OnTileCacheComplete;
public TileCacheStart OnTileCacheStart;
public TileCacheProgress OnTileCacheProgress;
///
/// immediately stops background tile caching, call it if you want fast exit the process
///
public void CancelTileCaching()
{
Debug.WriteLine("CancelTileCaching...");
abortCacheLoop = true;
lock(tileCacheQueue)
{
tileCacheQueue.Clear();
WaitForCache.Set();
}
}
int readingCache = 0;
///
/// delays writing tiles to cache while performing reads
///
public volatile bool CacheOnIdleRead = true;
///
/// disables delay between saving tiles into database/cache
///
public volatile bool BoostCacheEngine = false;
///
/// live for cache ;}
///
///
///
void CacheEngineLoop()
{
Debug.WriteLine("CacheEngine: start");
int left = 0;
if(OnTileCacheStart != null)
{
OnTileCacheStart();
}
bool startEvent = false;
while(!abortCacheLoop)
{
try
{
CacheQueueItem? task = null;
lock(tileCacheQueue)
{
left = tileCacheQueue.Count;
if(left > 0)
{
task = tileCacheQueue.Dequeue();
}
}
if(task.HasValue)
{
if(startEvent)
{
startEvent = false;
if(OnTileCacheStart != null)
{
OnTileCacheStart();
}
}
if(OnTileCacheProgress != null)
{
OnTileCacheProgress(left);
}
#region -- save --
// check if stream wasn't disposed somehow
if(task.Value.Img != null)
{
Debug.WriteLine("CacheEngine[" + left + "]: storing tile " + task.Value + ", " + task.Value.Img.Length / 1024 + "kB...");
if((task.Value.CacheType & CacheUsage.First) == CacheUsage.First && PrimaryCache != null)
{
if(CacheOnIdleRead)
{
while(Interlocked.Decrement(ref readingCache) > 0)
{
Thread.Sleep(1000);
}
}
PrimaryCache.PutImageToCache(task.Value.Img, task.Value.Tile.Type, task.Value.Tile.Pos, task.Value.Tile.Zoom);
}
if((task.Value.CacheType & CacheUsage.Second) == CacheUsage.Second && SecondaryCache != null)
{
if(CacheOnIdleRead)
{
while(Interlocked.Decrement(ref readingCache) > 0)
{
Thread.Sleep(1000);
}
}
SecondaryCache.PutImageToCache(task.Value.Img, task.Value.Tile.Type, task.Value.Tile.Pos, task.Value.Tile.Zoom);
}
task.Value.Clear();
if(!BoostCacheEngine)
{
#if PocketPC
Thread.Sleep(3333);
#else
Thread.Sleep(333);
#endif
}
}
else
{
Debug.WriteLine("CacheEngineLoop: skip, tile disposed to early -> " + task.Value);
}
task = null;
#endregion
}
else
{
if(!startEvent)
{
startEvent = true;
if(OnTileCacheComplete != null)
{
OnTileCacheComplete();
}
}
if(abortCacheLoop || noMapInstances || !WaitForCache.WaitOne(33333, false) || noMapInstances)
{
break;
}
}
}
#if !PocketPC
catch(AbandonedMutexException)
{
break;
}
#endif
catch(Exception ex)
{
Debug.WriteLine("CacheEngineLoop: " + ex.ToString());
}
}
Debug.WriteLine("CacheEngine: stop");
if(!startEvent)
{
if(OnTileCacheComplete != null)
{
OnTileCacheComplete();
}
}
}
class StringWriterExt : StringWriter
{
public StringWriterExt(IFormatProvider info)
: base(info)
{
}
public override Encoding Encoding
{
get
{
return Encoding.UTF8;
}
}
}
public string SerializeGPX(gpxType targetInstance)
{
string retVal = string.Empty;
using(StringWriterExt writer = new StringWriterExt(CultureInfo.InvariantCulture))
{
XmlSerializer serializer = new XmlSerializer(targetInstance.GetType());
serializer.Serialize(writer, targetInstance);
retVal = writer.ToString();
}
return retVal;
}
public gpxType DeserializeGPX(string objectXml)
{
gpxType retVal = null;
using(StringReader stringReader = new StringReader(objectXml))
{
XmlTextReader xmlReader = new XmlTextReader(stringReader);
XmlSerializer serializer = new XmlSerializer(typeof(gpxType));
retVal = serializer.Deserialize(xmlReader) as gpxType;
xmlReader.Close();
}
return retVal;
}
///
/// exports gps data to gpx file
///
/// gps data
/// file to export
/// true if success
public bool ExportGPX(IEnumerable> log, string gpxFile)
{
try
{
gpxType gpx = new gpxType();
{
gpx.creator = "GMap.NET - http://greatmaps.codeplex.com";
gpx.trk = new trkType[1];
gpx.trk[0] = new trkType();
}
var sessions = new List>(log);
gpx.trk[0].trkseg = new trksegType[sessions.Count];
int sesid = 0;
foreach(var session in sessions)
{
trksegType seg = new trksegType();
{
seg.trkpt = new wptType[session.Count];
}
gpx.trk[0].trkseg[sesid++] = seg;
for(int i = 0; i < session.Count; i++)
{
var point = session[i];
wptType t = new wptType();
{
#region -- set values --
t.lat = new decimal(point.Position.Lat);
t.lon = new decimal(point.Position.Lng);
t.time = point.TimeUTC;
t.timeSpecified = true;
if(point.FixType != FixType.Unknown)
{
t.fix = (point.FixType == FixType.XyD ? fixType.Item2d : fixType.Item3d);
t.fixSpecified = true;
}
if(point.SeaLevelAltitude.HasValue)
{
t.ele = new decimal(point.SeaLevelAltitude.Value);
t.eleSpecified = true;
}
if(point.EllipsoidAltitude.HasValue)
{
t.geoidheight = new decimal(point.EllipsoidAltitude.Value);
t.geoidheightSpecified = true;
}
if(point.VerticalDilutionOfPrecision.HasValue)
{
t.vdopSpecified = true;
t.vdop = new decimal(point.VerticalDilutionOfPrecision.Value);
}
if(point.HorizontalDilutionOfPrecision.HasValue)
{
t.hdopSpecified = true;
t.hdop = new decimal(point.HorizontalDilutionOfPrecision.Value);
}
if(point.PositionDilutionOfPrecision.HasValue)
{
t.pdopSpecified = true;
t.pdop = new decimal(point.PositionDilutionOfPrecision.Value);
}
if(point.SatelliteCount.HasValue)
{
t.sat = point.SatelliteCount.Value.ToString();
}
#endregion
}
seg.trkpt[i] = t;
}
}
sessions.Clear();
#if !PocketPC
File.WriteAllText(gpxFile, SerializeGPX(gpx), Encoding.UTF8);
#else
using(StreamWriter w = File.CreateText(gpxFile))
{
w.Write(SerializeGPX(gpx));
w.Close();
}
#endif
}
catch(Exception ex)
{
Debug.WriteLine("ExportGPX: " + ex.ToString());
return false;
}
return true;
}
#endregion
///
/// gets image from tile server
///
///
///
///
///
public PureImage GetImageFrom(GMapProvider provider, GPoint pos, int zoom, out Exception result)
{
PureImage ret = null;
result = null;
try
{
var rtile = new RawTile(provider.DbId, pos, zoom);
// let't check memmory first
if(UseMemoryCache)
{
var m = MemoryCache.GetTileFromMemoryCache(rtile);
if(m != null)
{
if(GMapProvider.TileImageProxy != null)
{
ret = GMapProvider.TileImageProxy.FromArray(m);
if(ret == null)
{
#if DEBUG
Debug.WriteLine("Image disposed in MemoryCache o.O, should never happen ;} " + new RawTile(provider.DbId, pos, zoom));
if(Debugger.IsAttached)
{
Debugger.Break();
}
#endif
m = null;
}
}
}
}
if(ret == null)
{
if(Mode != AccessMode.ServerOnly)
{
if(PrimaryCache != null)
{
// hold writer for 5s
if(CacheOnIdleRead)
{
Interlocked.Exchange(ref readingCache, 5);
}
ret = PrimaryCache.GetImageFromCache(provider.DbId, pos, zoom);
if(ret != null)
{
if(UseMemoryCache)
{
MemoryCache.AddTileToMemoryCache(rtile, ret.Data.GetBuffer());
}
return ret;
}
}
if(SecondaryCache != null)
{
// hold writer for 5s
if(CacheOnIdleRead)
{
Interlocked.Exchange(ref readingCache, 5);
}
ret = SecondaryCache.GetImageFromCache(provider.DbId, pos, zoom);
if(ret != null)
{
if(UseMemoryCache)
{
MemoryCache.AddTileToMemoryCache(rtile, ret.Data.GetBuffer());
}
EnqueueCacheTask(new CacheQueueItem(rtile, ret.Data.GetBuffer(), CacheUsage.First));
return ret;
}
}
}
if(Mode != AccessMode.CacheOnly)
{
ret = provider.GetTileImage(pos, zoom);
{
// Enqueue Cache
if(ret != null)
{
if(UseMemoryCache)
{
MemoryCache.AddTileToMemoryCache(rtile, ret.Data.GetBuffer());
}
if(Mode != AccessMode.ServerOnly)
{
EnqueueCacheTask(new CacheQueueItem(rtile, ret.Data.GetBuffer(), CacheUsage.Both));
}
}
}
}
else
{
result = noDataException;
}
}
}
catch(Exception ex)
{
result = ex;
ret = null;
Debug.WriteLine("GetImageFrom: " + ex.ToString());
}
return ret;
}
readonly Exception noDataException = new Exception("No data in local tile cache...");
}
}