namespace GMap.NET.Internals { using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using GMap.NET.Projections; using System.IO; using GMap.NET.MapProviders; using System.ComponentModel; #if PocketPC using OpenNETCF.ComponentModel; using OpenNETCF.Threading; using Thread=OpenNETCF.Threading.Thread2; #endif /// /// internal map control core /// internal class Core : IDisposable { public PointLatLng position; public GPoint positionPixel; public GPoint renderOffset; public GPoint centerTileXYLocation; public GPoint centerTileXYLocationLast; public GPoint dragPoint; public GPoint compensationOffset; public GPoint mouseDown; public GPoint mouseCurrent; public GPoint mouseLastZoom; public MouseWheelZoomType MouseWheelZoomType = MouseWheelZoomType.MousePositionAndCenter; public PointLatLng? LastLocationInBounds = null; public bool VirtualSizeEnabled = false; public GSize sizeOfMapArea; public GSize minOfTiles; public GSize maxOfTiles; public GRect tileRect; public GRect tileRectBearing; //public GRect currentRegion; public float bearing = 0; public bool IsRotated = false; public bool fillEmptyTiles = true; public TileMatrix Matrix = new TileMatrix(); public List tileDrawingList = new List(); public FastReaderWriterLock tileDrawingListLock = new FastReaderWriterLock(); public readonly Stack tileLoadQueue = new Stack(); #if !PocketPC static readonly int GThreadPoolSize = 5; #else static readonly int GThreadPoolSize = 2; #endif DateTime LastTileLoadStart = DateTime.Now; DateTime LastTileLoadEnd = DateTime.Now; internal volatile bool IsStarted = false; int zoom; internal double scaleX = 1; internal double scaleY = 1; internal int maxZoom = 2; internal int minZoom = 2; internal int Width; internal int Height; internal int pxRes100m; // 100 meters internal int pxRes1000m; // 1km internal int pxRes10km; // 10km internal int pxRes100km; // 100km internal int pxRes1000km; // 1000km internal int pxRes5000km; // 5000km /// /// is user dragging map /// public bool IsDragging = false; public Core() { Provider = EmptyProvider.Instance; } /// /// map zoom /// public int Zoom { get { return zoom; } set { if(zoom != value && !IsDragging) { zoom = value; minOfTiles = Provider.Projection.GetTileMatrixMinXY(value); maxOfTiles = Provider.Projection.GetTileMatrixMaxXY(value); positionPixel = Provider.Projection.FromLatLngToPixel(Position, value); if(IsStarted) { Monitor.Enter(tileLoadQueue); try { tileLoadQueue.Clear(); } finally { Monitor.Exit(tileLoadQueue); } Matrix.ClearLevelsBelove(zoom - LevelsKeepInMemmory); Matrix.ClearLevelsAbove(zoom + LevelsKeepInMemmory); lock(FailedLoads) { FailedLoads.Clear(); RaiseEmptyTileError = true; } GoToCurrentPositionOnZoom(); UpdateBounds(); if(OnMapZoomChanged != null) { OnMapZoomChanged(); } } } } } /// /// current marker position in pixel coordinates /// public GPoint PositionPixel { get { return positionPixel; } } /// /// current marker position /// public PointLatLng Position { get { return position; } set { position = value; positionPixel = Provider.Projection.FromLatLngToPixel(value, Zoom); if(IsStarted) { if(!IsDragging) { GoToCurrentPosition(); } if(OnCurrentPositionChanged != null) OnCurrentPositionChanged(position); } } } public GMapProvider provider; public GMapProvider Provider { get { return provider; } set { if(provider == null || !provider.Equals(value)) { provider = value; if(!provider.IsInitialized) { provider.IsInitialized = true; provider.OnInitialized(); } if(Provider.Projection != null) { tileRect = new GRect(GPoint.Empty, Provider.Projection.TileSize); tileRectBearing = tileRect; if(IsRotated) { tileRectBearing.Inflate(1, 1); } minOfTiles = Provider.Projection.GetTileMatrixMinXY(Zoom); maxOfTiles = Provider.Projection.GetTileMatrixMaxXY(Zoom); positionPixel = Provider.Projection.FromLatLngToPixel(Position, Zoom); } if(IsStarted) { CancelAsyncTasks(); OnMapSizeChanged(Width, Height); ReloadMap(); if(minZoom < provider.MinZoom) { minZoom = provider.MinZoom; } //if(provider.MaxZoom.HasValue && maxZoom > provider.MaxZoom) //{ // maxZoom = provider.MaxZoom.Value; //} zoomToArea = true; if(provider.Area.HasValue && !provider.Area.Value.Contains(Position)) { SetZoomToFitRect(provider.Area.Value); zoomToArea = false; } if(OnMapTypeChanged != null) { OnMapTypeChanged(value); } } } } } internal bool zoomToArea = true; /// /// sets zoom to max to fit rect /// /// /// public bool SetZoomToFitRect(RectLatLng rect) { int mmaxZoom = GetMaxZoomToFitRect(rect); if(mmaxZoom > 0) { PointLatLng center = new PointLatLng(rect.Lat - (rect.HeightLat / 2), rect.Lng + (rect.WidthLng / 2)); Position = center; if(mmaxZoom > maxZoom) { mmaxZoom = maxZoom; } if(Zoom != mmaxZoom) { Zoom = (int)mmaxZoom; } return true; } return false; } /// /// is polygons enabled /// public bool PolygonsEnabled = true; /// /// is routes enabled /// public bool RoutesEnabled = true; /// /// is markers enabled /// public bool MarkersEnabled = true; /// /// can user drag map /// public bool CanDragMap = true; /// /// retry count to get tile /// #if !PocketPC public int RetryLoadTile = 0; #else public int RetryLoadTile = 1; #endif /// /// how many levels of tiles are staying decompresed in memory /// #if !PocketPC public int LevelsKeepInMemmory = 5; #else public int LevelsKeepInMemmory = 1; #endif /// /// map render mode /// public RenderMode RenderMode = RenderMode.GDI_PLUS; /// /// occurs when current position is changed /// public event PositionChanged OnCurrentPositionChanged; /// /// occurs when tile set load is complete /// public event TileLoadComplete OnTileLoadComplete; /// /// occurs when tile set is starting to load /// public event TileLoadStart OnTileLoadStart; /// /// occurs on empty tile displayed /// public event EmptyTileError OnEmptyTileError; /// /// occurs on map drag /// public event MapDrag OnMapDrag; /// /// occurs on map zoom changed /// public event MapZoomChanged OnMapZoomChanged; /// /// occurs on map type changed /// public event MapTypeChanged OnMapTypeChanged; readonly List GThreadPool = new List(); // ^ // should be only one pool for multiply controls, any ideas how to fix? //static readonly List GThreadPool = new List(); // windows forms or wpf internal string SystemType; internal static int instances = 0; BackgroundWorker invalidator; public BackgroundWorker OnMapOpen() { if(!IsStarted) { int x = Interlocked.Increment(ref instances); Debug.WriteLine("OnMapOpen: " + x); IsStarted = true; if(x == 1) { GMaps.Instance.noMapInstances = false; } GoToCurrentPosition(); invalidator = new BackgroundWorker(); invalidator.WorkerSupportsCancellation = true; invalidator.WorkerReportsProgress = true; invalidator.DoWork += new DoWorkEventHandler(invalidatorWatch); invalidator.RunWorkerAsync(); //if(x == 1) //{ // first control shown //} } return invalidator; } public void OnMapClose() { Dispose(); } internal readonly object invalidationLock = new object(); internal DateTime lastInvalidation = DateTime.Now; void invalidatorWatch(object sender, DoWorkEventArgs e) { var w = sender as BackgroundWorker; TimeSpan span = TimeSpan.FromMilliseconds(111); int spanMs = (int)span.TotalMilliseconds; bool skiped = false; TimeSpan delta; DateTime now = DateTime.Now; while(Refresh != null && (!skiped && Refresh.WaitOne() || (Refresh.WaitOne(spanMs, false) || true))) { if(w.CancellationPending) break; now = DateTime.Now; lock(invalidationLock) { delta = now - lastInvalidation; } if(delta > span) { lock(invalidationLock) { lastInvalidation = now; } skiped = false; w.ReportProgress(1); Debug.WriteLine("Invalidate delta: " + (int)delta.TotalMilliseconds + "ms"); } else { skiped = true; } } } public void UpdateCenterTileXYLocation() { PointLatLng center = FromLocalToLatLng(Width / 2, Height / 2); GPoint centerPixel = Provider.Projection.FromLatLngToPixel(center, Zoom); centerTileXYLocation = Provider.Projection.FromPixelToTileXY(centerPixel); } public int vWidth = 800; public int vHeight = 400; public void OnMapSizeChanged(int width, int height) { this.Width = width; this.Height = height; if(IsRotated) { #if !PocketPC int diag = (int)Math.Round(Math.Sqrt(Width * Width + Height * Height) / Provider.Projection.TileSize.Width, MidpointRounding.AwayFromZero); #else int diag = (int) Math.Round(Math.Sqrt(Width * Width + Height * Height) / Provider.Projection.TileSize.Width); #endif sizeOfMapArea.Width = 1 + (diag / 2); sizeOfMapArea.Height = 1 + (diag / 2); } else { sizeOfMapArea.Width = 1 + (Width / Provider.Projection.TileSize.Width) / 2; sizeOfMapArea.Height = 1 + (Height / Provider.Projection.TileSize.Height) / 2; } Debug.WriteLine("OnMapSizeChanged, w: " + width + ", h: " + height + ", size: " + sizeOfMapArea); if(IsStarted) { UpdateBounds(); GoToCurrentPosition(); } } /// /// gets current map view top/left coordinate, width in Lng, height in Lat /// /// public RectLatLng ViewArea { get { if(Provider.Projection != null) { var p = FromLocalToLatLng(0, 0); var p2 = FromLocalToLatLng(Width, Height); return RectLatLng.FromLTRB(p.Lng, p.Lat, p2.Lng, p2.Lat); } return RectLatLng.Empty; } } /// /// gets lat/lng from local control coordinates /// /// /// /// public PointLatLng FromLocalToLatLng(long x, long y) { GPoint p = new GPoint(x, y); p.OffsetNegative(renderOffset); p.Offset(compensationOffset); return Provider.Projection.FromPixelToLatLng(p, Zoom); } /// /// return local coordinates from lat/lng /// /// /// public GPoint FromLatLngToLocal(PointLatLng latlng) { GPoint pLocal = Provider.Projection.FromLatLngToPixel(latlng, Zoom); pLocal.Offset(renderOffset); pLocal.OffsetNegative(compensationOffset); return pLocal; } /// /// gets max zoom level to fit rectangle /// /// /// public int GetMaxZoomToFitRect(RectLatLng rect) { int zoom = minZoom; if(rect.HeightLat == 0 || rect.WidthLng == 0) { zoom = maxZoom / 2; } else { for(int i = (int)zoom; i <= maxZoom; i++) { GPoint p1 = Provider.Projection.FromLatLngToPixel(rect.LocationTopLeft, i); GPoint p2 = Provider.Projection.FromLatLngToPixel(rect.LocationRightBottom, i); if(((p2.X - p1.X) <= Width + 10) && (p2.Y - p1.Y) <= Height + 10) { zoom = i; } else { break; } } } return zoom; } /// /// initiates map dragging /// /// public void BeginDrag(GPoint pt) { dragPoint.X = pt.X - renderOffset.X; dragPoint.Y = pt.Y - renderOffset.Y; IsDragging = true; } /// /// ends map dragging /// public void EndDrag() { IsDragging = false; mouseDown = GPoint.Empty; Refresh.Set(); } /// /// reloads map /// public void ReloadMap() { if(IsStarted) { Debug.WriteLine("------------------"); okZoom = 0; skipOverZoom = 0; Monitor.Enter(tileLoadQueue); try { tileLoadQueue.Clear(); } finally { Monitor.Exit(tileLoadQueue); } Matrix.ClearAllLevels(); lock(FailedLoads) { FailedLoads.Clear(); RaiseEmptyTileError = true; } Refresh.Set(); UpdateBounds(); } else { throw new Exception("Please, do not call ReloadMap before form is loaded, it's useless"); } } /// /// moves current position into map center /// public void GoToCurrentPosition() { compensationOffset = positionPixel; // TODO: fix // reset stuff renderOffset = GPoint.Empty; dragPoint = GPoint.Empty; //var dd = new GPoint(-(CurrentPositionGPixel.X - Width / 2), -(CurrentPositionGPixel.Y - Height / 2)); //dd.Offset(compensationOffset); var d = new GPoint(Width / 2, Height / 2); this.Drag(d); } public bool MouseWheelZooming = false; /// /// moves current position into map center /// internal void GoToCurrentPositionOnZoom() { compensationOffset = positionPixel; // TODO: fix // reset stuff renderOffset = GPoint.Empty; dragPoint = GPoint.Empty; // goto location and centering if(MouseWheelZooming) { if(MouseWheelZoomType != MouseWheelZoomType.MousePositionWithoutCenter) { GPoint pt = new GPoint(-(positionPixel.X - Width / 2), -(positionPixel.Y - Height / 2)); pt.Offset(compensationOffset); renderOffset.X = pt.X - dragPoint.X; renderOffset.Y = pt.Y - dragPoint.Y; } else // without centering { renderOffset.X = -positionPixel.X - dragPoint.X; renderOffset.Y = -positionPixel.Y - dragPoint.Y; renderOffset.Offset(mouseLastZoom); renderOffset.Offset(compensationOffset); } } else // use current map center { mouseLastZoom = GPoint.Empty; GPoint pt = new GPoint(-(positionPixel.X - Width / 2), -(positionPixel.Y - Height / 2)); pt.Offset(compensationOffset); renderOffset.X = pt.X - dragPoint.X; renderOffset.Y = pt.Y - dragPoint.Y; } UpdateCenterTileXYLocation(); } /// /// darg map by offset in pixels /// /// public void DragOffset(GPoint offset) { renderOffset.Offset(offset); UpdateCenterTileXYLocation(); if(centerTileXYLocation != centerTileXYLocationLast) { centerTileXYLocationLast = centerTileXYLocation; UpdateBounds(); } { LastLocationInBounds = Position; IsDragging = true; Position = FromLocalToLatLng((int)Width / 2, (int)Height / 2); IsDragging = false; } if(OnMapDrag != null) { OnMapDrag(); } } /// /// drag map /// /// public void Drag(GPoint pt) { renderOffset.X = pt.X - dragPoint.X; renderOffset.Y = pt.Y - dragPoint.Y; UpdateCenterTileXYLocation(); if(centerTileXYLocation != centerTileXYLocationLast) { centerTileXYLocationLast = centerTileXYLocation; UpdateBounds(); } if(IsDragging) { LastLocationInBounds = Position; Position = FromLocalToLatLng((int)Width / 2, (int)Height / 2); if(OnMapDrag != null) { OnMapDrag(); } } } /// /// cancels tile loaders and bounds checker /// public void CancelAsyncTasks() { if(IsStarted) { Monitor.Enter(tileLoadQueue); try { tileLoadQueue.Clear(); } finally { Monitor.Exit(tileLoadQueue); } } } bool RaiseEmptyTileError = false; internal Dictionary FailedLoads = new Dictionary(); internal static readonly int WaitForTileLoadThreadTimeout = 5 * 1000 * 60; // 5 min. byte loadWaitCount = 0; volatile int okZoom = 0; volatile int skipOverZoom = 0; // tile consumer thread void ProcessLoadTask() { LoadTask? task = null; long lastTileLoadTimeMs; bool stop = false; #if !PocketPC Thread ct = Thread.CurrentThread; string ctid = "Thread[" + ct.ManagedThreadId + "]"; #else int ctid = 0; #endif while(!stop && IsStarted) { task = null; Monitor.Enter(tileLoadQueue); try { while(tileLoadQueue.Count == 0) { Debug.WriteLine(ctid + " - Wait " + loadWaitCount + " - " + DateTime.Now.TimeOfDay); if(++loadWaitCount >= GThreadPoolSize) { loadWaitCount = 0; #region -- last thread takes action -- { LastTileLoadEnd = DateTime.Now; lastTileLoadTimeMs = (long)(LastTileLoadEnd - LastTileLoadStart).TotalMilliseconds; } #region -- clear stuff-- if(IsStarted) { GMaps.Instance.MemoryCache.RemoveOverload(); tileDrawingListLock.AcquireReaderLock(); try { Matrix.ClearLevelAndPointsNotIn(Zoom, tileDrawingList); } finally { tileDrawingListLock.ReleaseReaderLock(); } } #endregion UpdateGroundResolution(); #if UseGC GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); #endif Debug.WriteLine(ctid + " - OnTileLoadComplete: " + lastTileLoadTimeMs + "ms, MemoryCacheSize: " + GMaps.Instance.MemoryCache.Size + "MB"); if(OnTileLoadComplete != null) { OnTileLoadComplete(lastTileLoadTimeMs); } #endregion } if(!IsStarted || false == Monitor.Wait(tileLoadQueue, WaitForTileLoadThreadTimeout, false) || !IsStarted) { stop = true; break; } } if(IsStarted && !stop || tileLoadQueue.Count > 0) { task = tileLoadQueue.Pop(); } } finally { Monitor.Exit(tileLoadQueue); } if(task.HasValue && IsStarted) { try { #region -- execute -- var m = Matrix.GetTileWithReadLock(task.Value.Zoom, task.Value.Pos); if(!m.NotEmpty) { Debug.WriteLine(ctid + " - try load: " + task); Tile t = new Tile(task.Value.Zoom, task.Value.Pos); foreach(var tl in provider.Overlays) { int retry = 0; do { PureImage img = null; Exception ex = null; if(!provider.MaxZoom.HasValue || task.Value.Zoom <= provider.MaxZoom) { if(skipOverZoom == 0 || task.Value.Zoom <= skipOverZoom) { // tile number inversion(BottomLeft -> TopLeft) if(tl.InvertedAxisY) { img = GMaps.Instance.GetImageFrom(tl, new GPoint(task.Value.Pos.X, maxOfTiles.Height - task.Value.Pos.Y), task.Value.Zoom, out ex); } else // ok { img = GMaps.Instance.GetImageFrom(tl, task.Value.Pos, task.Value.Zoom, out ex); } } } if(img != null && ex == null) { if(okZoom < task.Value.Zoom) { okZoom = task.Value.Zoom; skipOverZoom = 0; Debug.WriteLine("skipOverZoom disabled, okZoom: " + okZoom); } } else if(ex != null) { if(skipOverZoom != okZoom) { if(ex.Message.Contains("(404) Not Found")) { skipOverZoom = okZoom; Debug.WriteLine("skipOverZoom enabled: " + skipOverZoom); } } } // check for parent tiles if not found if(img == null && okZoom > 0 && fillEmptyTiles && Provider.Projection is MercatorProjection) { int zoomOffset = task.Value.Zoom > okZoom ? task.Value.Zoom - okZoom : 1; long Ix = 0; GPoint parentTile = GPoint.Empty; while(img == null && zoomOffset < task.Value.Zoom) { Ix = (long)Math.Pow(2, zoomOffset); parentTile = new GMap.NET.GPoint((task.Value.Pos.X / Ix), (task.Value.Pos.Y / Ix)); img = GMaps.Instance.GetImageFrom(tl, parentTile, task.Value.Zoom - zoomOffset++, out ex); } if(img != null) { // offsets in quadrant long Xoff = Math.Abs(task.Value.Pos.X - (parentTile.X * Ix)); long Yoff = Math.Abs(task.Value.Pos.Y - (parentTile.Y * Ix)); img.IsParent = true; img.Ix = Ix; img.Xoff = Xoff; img.Yoff = Yoff; // wpf //var geometry = new RectangleGeometry(new Rect(Core.tileRect.X + 0.6, Core.tileRect.Y + 0.6, Core.tileRect.Width + 0.6, Core.tileRect.Height + 0.6)); //var parentImgRect = new Rect(Core.tileRect.X - Core.tileRect.Width * Xoff + 0.6, Core.tileRect.Y - Core.tileRect.Height * Yoff + 0.6, Core.tileRect.Width * Ix + 0.6, Core.tileRect.Height * Ix + 0.6); // gdi+ //System.Drawing.Rectangle dst = new System.Drawing.Rectangle((int)Core.tileRect.X, (int)Core.tileRect.Y, (int)Core.tileRect.Width, (int)Core.tileRect.Height); //System.Drawing.RectangleF srcRect = new System.Drawing.RectangleF((float)(Xoff * (img.Img.Width / Ix)), (float)(Yoff * (img.Img.Height / Ix)), (img.Img.Width / Ix), (img.Img.Height / Ix)); } } if(img != null) { Debug.WriteLine(ctid + " - tile loaded: " + img.Data.Length / 1024 + "KB, " + task); { t.AddOverlay(img); } break; } else { if(ex != null) { lock(FailedLoads) { if(!FailedLoads.ContainsKey(task.Value)) { FailedLoads.Add(task.Value, ex); if(OnEmptyTileError != null) { if(!RaiseEmptyTileError) { RaiseEmptyTileError = true; OnEmptyTileError(task.Value.Zoom, task.Value.Pos); } } } } } if(RetryLoadTile > 0) { Debug.WriteLine(ctid + " - ProcessLoadTask: " + task + " -> empty tile, retry " + retry); { Thread.Sleep(1111); } } } } while(++retry < RetryLoadTile); } if(t.HasAnyOverlays && IsStarted) { Matrix.SetTile(t); } else { t.Dispose(); } } #endregion } catch(Exception ex) { Debug.WriteLine(ctid + " - ProcessLoadTask: " + ex.ToString()); } finally { if(Refresh != null) { Refresh.Set(); } } } } #if !PocketPC Monitor.Enter(tileLoadQueue); try { Debug.WriteLine("Quit - " + ct.Name); lock(GThreadPool) { GThreadPool.Remove(ct); } } finally { Monitor.Exit(tileLoadQueue); } #endif } public AutoResetEvent Refresh = new AutoResetEvent(false); public bool updatingBounds = false; /// /// updates map bounds /// void UpdateBounds() { if(!IsStarted || Provider.Equals(EmptyProvider.Instance)) { return; } updatingBounds = true; tileDrawingListLock.AcquireWriterLock(); try { #region -- find tiles around -- tileDrawingList.Clear(); for(long i = (int)Math.Floor(-sizeOfMapArea.Width * scaleX), countI = (int)Math.Ceiling(sizeOfMapArea.Width * scaleX); i <= countI; i++) { for (long j = (int)Math.Floor(-sizeOfMapArea.Height * scaleY), countJ = (int)Math.Ceiling(sizeOfMapArea.Height * scaleY); j <= countJ; j++) { GPoint p = centerTileXYLocation; p.X += i; p.Y += j; #if ContinuesMap // ---------------------------- if(p.X < minOfTiles.Width) { p.X += (maxOfTiles.Width + 1); } if(p.X > maxOfTiles.Width) { p.X -= (maxOfTiles.Width + 1); } // ---------------------------- #endif if(p.X >= minOfTiles.Width && p.Y >= minOfTiles.Height && p.X <= maxOfTiles.Width && p.Y <= maxOfTiles.Height) { DrawTile dt = new DrawTile() { PosXY = p, PosPixel = new GPoint(p.X * tileRect.Width, p.Y * tileRect.Height), DistanceSqr = (centerTileXYLocation.X - p.X) * (centerTileXYLocation.X - p.X) + (centerTileXYLocation.Y - p.Y) * (centerTileXYLocation.Y - p.Y) }; if(!tileDrawingList.Contains(dt)) { tileDrawingList.Add(dt); } } } } if(GMaps.Instance.ShuffleTilesOnLoad) { Stuff.Shuffle(tileDrawingList); } else { tileDrawingList.Sort(); } #endregion } finally { tileDrawingListLock.ReleaseWriterLock(); } Monitor.Enter(tileLoadQueue); try { tileDrawingListLock.AcquireReaderLock(); try { foreach(DrawTile p in tileDrawingList) { LoadTask task = new LoadTask(p.PosXY, Zoom); { if(!tileLoadQueue.Contains(task)) { tileLoadQueue.Push(task); } } } } finally { tileDrawingListLock.ReleaseReaderLock(); } #region -- starts loader threads if needed -- lock(GThreadPool) { while(GThreadPool.Count < GThreadPoolSize) { Thread t = new Thread(new ThreadStart(ProcessLoadTask)); { t.Name = "TileLoader: " + GThreadPool.Count; t.IsBackground = true; t.Priority = ThreadPriority.BelowNormal; } GThreadPool.Add(t); Debug.WriteLine("add " + t.Name + " to GThreadPool"); t.Start(); } } #endregion { LastTileLoadStart = DateTime.Now; Debug.WriteLine("OnTileLoadStart - at zoom " + Zoom + ", time: " + LastTileLoadStart.TimeOfDay); } loadWaitCount = 0; Monitor.PulseAll(tileLoadQueue); } finally { Monitor.Exit(tileLoadQueue); } updatingBounds = false; if(OnTileLoadStart != null) { OnTileLoadStart(); } } /// /// updates ground resolution info /// void UpdateGroundResolution() { double rez = Provider.Projection.GetGroundResolution(Zoom, Position.Lat); pxRes100m = (int)(100.0 / rez); // 100 meters pxRes1000m = (int)(1000.0 / rez); // 1km pxRes10km = (int)(10000.0 / rez); // 10km pxRes100km = (int)(100000.0 / rez); // 100km pxRes1000km = (int)(1000000.0 / rez); // 1000km pxRes5000km = (int)(5000000.0 / rez); // 5000km } #region IDisposable Members ~Core() { Dispose(false); } void Dispose(bool disposing) { if(IsStarted) { if(invalidator != null) { invalidator.CancelAsync(); invalidator.DoWork -= new DoWorkEventHandler(invalidatorWatch); invalidator.Dispose(); invalidator = null; } if(Refresh != null) { Refresh.Set(); Refresh.Close(); Refresh = null; } int x = Interlocked.Decrement(ref instances); Debug.WriteLine("OnMapClose: " + x); CancelAsyncTasks(); IsStarted = false; if(Matrix != null) { Matrix.Dispose(); Matrix = null; } if(FailedLoads != null) { lock(FailedLoads) { FailedLoads.Clear(); RaiseEmptyTileError = false; } FailedLoads = null; } // cancel waiting loaders Monitor.Enter(tileLoadQueue); try { Monitor.PulseAll(tileLoadQueue); tileDrawingList.Clear(); } finally { Monitor.Exit(tileLoadQueue); } lock(GThreadPool) { #if PocketPC Debug.WriteLine("waiting until loaders are stopped..."); while(GThreadPool.Count > 0) { var t = GThreadPool[0]; if (t.State != ThreadState.Stopped) { var tr = t.Join(1111); Debug.WriteLine(t.Name + ", " + t.State); if (!tr) { continue; } else { GThreadPool.Remove(t); } } else { GThreadPool.Remove(t); } } Thread.Sleep(1111); #endif } if(tileDrawingListLock != null) { tileDrawingListLock.Dispose(); tileDrawingListLock = null; tileDrawingList = null; } if(x == 0) { #if DEBUG GMaps.Instance.CancelTileCaching(); #endif GMaps.Instance.noMapInstances = true; GMaps.Instance.WaitForCache.Set(); if(disposing) { GMaps.Instance.MemoryCache.Clear(); } } } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion } }