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..."); } }