0,0 → 1,849 |
|
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; |
using System.Reflection; |
|
#if PocketPC |
using OpenNETCF.ComponentModel; |
using OpenNETCF.Threading; |
using Thread=OpenNETCF.Threading.Thread2; |
#endif |
|
/// <summary> |
/// maps manager |
/// </summary> |
public class GMaps : Singleton<GMaps> |
{ |
/// <summary> |
/// tile access mode |
/// </summary> |
public AccessMode Mode = AccessMode.ServerAndCache; |
|
/// <summary> |
/// is map ussing cache for routing |
/// </summary> |
public bool UseRouteCache = true; |
|
/// <summary> |
/// is map using cache for geocoder |
/// </summary> |
public bool UseGeocoderCache = true; |
|
/// <summary> |
/// is map using cache for directions |
/// </summary> |
public bool UseDirectionsCache = true; |
|
/// <summary> |
/// is map using cache for placemarks |
/// </summary> |
public bool UsePlacemarkCache = true; |
|
/// <summary> |
/// is map ussing cache for other url |
/// </summary> |
public bool UseUrlCache = true; |
|
/// <summary> |
/// is map using memory cache for tiles |
/// </summary> |
public bool UseMemoryCache = true; |
|
/// <summary> |
/// primary cache provider, by default: ultra fast SQLite! |
/// </summary> |
public PureImageCache PrimaryCache |
{ |
get |
{ |
return Cache.Instance.ImageCache; |
} |
set |
{ |
Cache.Instance.ImageCache = value; |
} |
} |
|
/// <summary> |
/// secondary cache provider, by default: none, |
/// use it if you have server in your local network |
/// </summary> |
public PureImageCache SecondaryCache |
{ |
get |
{ |
return Cache.Instance.ImageCacheSecond; |
} |
set |
{ |
Cache.Instance.ImageCacheSecond = value; |
} |
} |
|
/// <summary> |
/// MemoryCache provider |
/// </summary> |
public readonly MemoryCache MemoryCache = new MemoryCache(); |
|
/// <summary> |
/// load tiles in random sequence |
/// </summary> |
public bool ShuffleTilesOnLoad = false; |
|
/// <summary> |
/// tile queue to cache |
/// </summary> |
readonly Queue<CacheQueueItem> tileCacheQueue = new Queue<CacheQueueItem>(); |
|
bool? isRunningOnMono; |
|
/// <summary> |
/// return true if running on mono |
/// </summary> |
/// <returns></returns> |
public bool IsRunningOnMono |
{ |
get |
{ |
if(!isRunningOnMono.HasValue) |
{ |
try |
{ |
isRunningOnMono = (Type.GetType("Mono.Runtime") != null); |
return isRunningOnMono.Value; |
} |
catch |
{ |
} |
} |
else |
{ |
return isRunningOnMono.Value; |
} |
return false; |
} |
} |
|
/// <summary> |
/// cache worker |
/// </summary> |
Thread CacheEngine; |
|
internal readonly AutoResetEvent WaitForCache = new AutoResetEvent(false); |
|
#if !PocketPC |
static GMaps() |
{ |
if (GMapProvider.TileImageProxy == null) |
{ |
try |
{ |
AppDomain d = AppDomain.CurrentDomain; |
var AssembliesLoaded = d.GetAssemblies(); |
|
Assembly l = null; |
|
foreach (var a in AssembliesLoaded) |
{ |
if (a.FullName.Contains("GMap.NET.WindowsForms") || a.FullName.Contains("GMap.NET.WindowsPresentation")) |
{ |
l = a; |
break; |
} |
} |
|
if (l == null) |
{ |
var jj = Assembly.GetExecutingAssembly().Location; |
var hh = Path.GetDirectoryName(jj); |
var f1 = hh + Path.DirectorySeparatorChar + "GMap.NET.WindowsForms.dll"; |
var f2 = hh + Path.DirectorySeparatorChar + "GMap.NET.WindowsPresentation.dll"; |
if (File.Exists(f1)) |
{ |
l = Assembly.LoadFile(f1); |
} |
else if (File.Exists(f2)) |
{ |
l = Assembly.LoadFile(f2); |
} |
} |
|
if (l != null) |
{ |
Type t = null; |
|
if (l.FullName.Contains("GMap.NET.WindowsForms")) |
{ |
t = l.GetType("GMap.NET.WindowsForms.GMapImageProxy"); |
} |
else if (l.FullName.Contains("GMap.NET.WindowsPresentation")) |
{ |
t = l.GetType("GMap.NET.WindowsPresentation.GMapImageProxy"); |
} |
|
if (t != null) |
{ |
t.InvokeMember("Enable", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, null); |
} |
} |
} |
catch (Exception ex) |
{ |
Debug.WriteLine("GMaps, try set TileImageProxy failed: " + ex.Message); |
} |
} |
} |
#endif |
|
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 |
/// <summary> |
/// triggers dynamic sqlite loading, |
/// call this before you use sqlite for other reasons than caching maps |
/// </summary> |
public void SQLitePing() |
{ |
#if SQLite |
#if !MONO |
SQLitePureImageCache.Ping(); |
#endif |
#endif |
} |
#endif |
|
#region -- Stuff -- |
|
#if !PocketPC |
|
/// <summary> |
/// 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 |
/// </summary> |
/// <param name="file"></param> |
/// <returns></returns> |
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; |
} |
|
/// <summary> |
/// imports GMDB file to current map cache |
/// only new records will be added |
/// </summary> |
/// <param name="file"></param> |
/// <returns></returns> |
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 |
|
/// <summary> |
/// optimizes map database, *.gmdb |
/// </summary> |
/// <param name="file">database file name or null to optimize current user db</param> |
/// <returns></returns> |
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 |
|
/// <summary> |
/// enqueueens tile to cache |
/// </summary> |
/// <param name="task"></param> |
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; |
|
/// <summary> |
/// immediately stops background tile caching, call it if you want fast exit the process |
/// </summary> |
public void CancelTileCaching() |
{ |
Debug.WriteLine("CancelTileCaching..."); |
|
abortCacheLoop = true; |
lock(tileCacheQueue) |
{ |
tileCacheQueue.Clear(); |
WaitForCache.Set(); |
} |
} |
|
int readingCache = 0; |
volatile bool cacheOnIdleRead = true; |
|
/// <summary> |
/// delays writing tiles to cache while performing reads |
/// </summary> |
public bool CacheOnIdleRead |
{ |
get |
{ |
return cacheOnIdleRead; |
} |
set |
{ |
cacheOnIdleRead = value; |
} |
} |
|
volatile bool boostCacheEngine = false; |
|
/// <summary> |
/// disables delay between saving tiles into database/cache |
/// </summary> |
public bool BoostCacheEngine |
{ |
get |
{ |
return boostCacheEngine; |
} |
set |
{ |
boostCacheEngine = value; |
} |
} |
|
/// <summary> |
/// live for cache ;} |
/// </summary> |
/// <param name="sender"></param> |
/// <param name="e"></param> |
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; |
} |
|
/// <summary> |
/// exports gps data to gpx file |
/// </summary> |
/// <param name="log">gps data</param> |
/// <param name="gpxFile">file to export</param> |
/// <returns>true if success</returns> |
public bool ExportGPX(IEnumerable<List<GpsLog>> 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<List<GpsLog>>(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 |
|
/// <summary> |
/// gets image from tile server |
/// </summary> |
/// <param name="provider"></param> |
/// <param name="pos"></param> |
/// <param name="zoom"></param> |
/// <returns></returns> |
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 && !provider.BypassCache) |
{ |
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 && !provider.BypassCache) |
{ |
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..."); |
|
#if !PocketPC |
TileHttpHost host; |
|
/// <summary> |
/// turns on tile host |
/// </summary> |
/// <param name="port"></param> |
public void EnableTileHost(int port) |
{ |
if(host == null) |
{ |
host = new TileHttpHost(); |
} |
host.Start(port); |
} |
|
/// <summary> |
/// turns off tile host |
/// </summary> |
/// <param name="port"></param> |
public void DisableTileHost() |
{ |
if (host != null) |
{ |
host.Stop(); |
} |
} |
#endif |
} |
} |