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 NET40
using System.Collections.Concurrent;
using System.Threading.Tasks;
#endif
#if PocketPC
using OpenNETCF.ComponentModel;
using OpenNETCF.Threading;
using Thread=OpenNETCF
.Threading.Thread2;
#endif
/// <summary>
/// internal map control core
/// </summary>
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 bool MouseWheelZoomEnabled
= true;
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
<DrawTile
> tileDrawingList
= new List
<DrawTile
>();
public FastReaderWriterLock tileDrawingListLock
= new FastReaderWriterLock
();
#if !NET40
public readonly Stack
<LoadTask
> tileLoadQueue
= new Stack
<LoadTask
>();
#endif
#if !PocketPC
static readonly int GThreadPoolSize
= 4;
#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
/// <summary>
/// is user dragging map
/// </summary>
public bool IsDragging
= false;
public Core
()
{
Provider
= EmptyProvider
.Instance;
}
/// <summary>
/// map zoom
/// </summary>
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
)
{
CancelAsyncTasks
();
Matrix
.ClearLevelsBelove(zoom
- LevelsKeepInMemmory
);
Matrix
.ClearLevelsAbove(zoom
+ LevelsKeepInMemmory
);
lock (FailedLoads
)
{
FailedLoads
.Clear();
RaiseEmptyTileError
= true;
}
GoToCurrentPositionOnZoom
();
UpdateBounds
();
if (OnMapZoomChanged
!= null)
{
OnMapZoomChanged
();
}
}
}
}
}
/// <summary>
/// current marker position in pixel coordinates
/// </summary>
public GPoint PositionPixel
{
get
{
return positionPixel
;
}
}
/// <summary>
/// current marker position
/// </summary>
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))
{
bool diffProjection
= (provider
== null || provider
.Projection != value.Projection);
provider
= value;
if (!provider
.IsInitialized)
{
provider
.IsInitialized = true;
provider
.OnInitialized();
}
if (provider
.Projection != null && diffProjection
)
{
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
();
if (diffProjection
)
{
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;
/// <summary>
/// sets zoom to max to fit rect
/// </summary>
/// <param name="rect"></param>
/// <returns></returns>
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;
}
/// <summary>
/// is polygons enabled
/// </summary>
public bool PolygonsEnabled
= true;
/// <summary>
/// is routes enabled
/// </summary>
public bool RoutesEnabled
= true;
/// <summary>
/// is markers enabled
/// </summary>
public bool MarkersEnabled
= true;
/// <summary>
/// can user drag map
/// </summary>
public bool CanDragMap
= true;
/// <summary>
/// retry count to get tile
/// </summary>
#if !PocketPC
public int RetryLoadTile
= 0;
#else
public int RetryLoadTile
= 1;
#endif
/// <summary>
/// how many levels of tiles are staying decompresed in memory
/// </summary>
#if !PocketPC
public int LevelsKeepInMemmory
= 5;
#else
public int LevelsKeepInMemmory
= 1;
#endif
/// <summary>
/// map render mode
/// </summary>
public RenderMode RenderMode
= RenderMode
.GDI_PLUS;
/// <summary>
/// occurs when current position is changed
/// </summary>
public event PositionChanged OnCurrentPositionChanged
;
/// <summary>
/// occurs when tile set load is complete
/// </summary>
public event TileLoadComplete OnTileLoadComplete
;
/// <summary>
/// occurs when tile set is starting to load
/// </summary>
public event TileLoadStart OnTileLoadStart
;
/// <summary>
/// occurs on empty tile displayed
/// </summary>
public event EmptyTileError OnEmptyTileError
;
/// <summary>
/// occurs on map drag
/// </summary>
public event MapDrag OnMapDrag
;
/// <summary>
/// occurs on map zoom changed
/// </summary>
public event MapZoomChanged OnMapZoomChanged
;
/// <summary>
/// occurs on map type changed
/// </summary>
public event MapTypeChanged OnMapTypeChanged
;
readonly List
<Thread
> GThreadPool
= new List
<Thread
>();
// ^
// should be only one pool for multiply controls, any ideas how to fix?
//static readonly List<Thread> GThreadPool = new List<Thread>();
// 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
();
}
}
/// <summary>
/// gets current map view top/left coordinate, width in Lng, height in Lat
/// </summary>
/// <returns></returns>
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;
}
}
/// <summary>
/// gets lat/lng from local control coordinates
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
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
);
}
/// <summary>
/// return local coordinates from lat/lng
/// </summary>
/// <param name="latlng"></param>
/// <returns></returns>
public GPoint FromLatLngToLocal
(PointLatLng latlng
)
{
GPoint pLocal
= Provider
.Projection.FromLatLngToPixel(latlng, Zoom
);
pLocal
.Offset(renderOffset
);
pLocal
.OffsetNegative(compensationOffset
);
return pLocal
;
}
/// <summary>
/// gets max zoom level to fit rectangle
/// </summary>
/// <param name="rect"></param>
/// <returns></returns>
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
;
}
/// <summary>
/// initiates map dragging
/// </summary>
/// <param name="pt"></param>
public void BeginDrag
(GPoint pt
)
{
dragPoint
.X = pt
.X - renderOffset
.X;
dragPoint
.Y = pt
.Y - renderOffset
.Y;
IsDragging
= true;
}
/// <summary>
/// ends map dragging
/// </summary>
public void EndDrag
()
{
IsDragging
= false;
mouseDown
= GPoint
.Empty;
Refresh
.Set();
}
/// <summary>
/// reloads map
/// </summary>
public void ReloadMap
()
{
if (IsStarted
)
{
Debug
.WriteLine("------------------");
okZoom
= 0;
skipOverZoom
= 0;
CancelAsyncTasks
();
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");
}
}
/// <summary>
/// moves current position into map center
/// </summary>
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;
/// <summary>
/// moves current position into map center
/// </summary>
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
();
}
/// <summary>
/// darg map by offset in pixels
/// </summary>
/// <param name="offset"></param>
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
();
}
}
/// <summary>
/// drag map
/// </summary>
/// <param name="pt"></param>
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
();
}
}
}
/// <summary>
/// cancels tile loaders and bounds checker
/// </summary>
public void CancelAsyncTasks
()
{
if (IsStarted
)
{
#if NET40
//TODO: clear loading
#else
Monitor
.Enter(tileLoadQueue
);
try
{
tileLoadQueue
.Clear();
}
finally
{
Monitor
.Exit(tileLoadQueue
);
}
#endif
}
}
bool RaiseEmptyTileError
= false;
internal Dictionary
<LoadTask, Exception
> FailedLoads
= new Dictionary
<LoadTask, Exception
>(new LoadTaskComparer
());
internal static readonly int WaitForTileLoadThreadTimeout
= 5 * 1000 * 60; // 5 min.
volatile int okZoom
= 0;
volatile int skipOverZoom
= 0;
#if NET40
static readonly BlockingCollection
<LoadTask
> tileLoadQueue4
= new BlockingCollection
<LoadTask
>(new ConcurrentStack
<LoadTask
>());
static List
<Task
> tileLoadQueue4Tasks
;
static int loadWaitCount
= 0;
void AddLoadTask
(LoadTask t
)
{
if (tileLoadQueue4Tasks
== null)
{
lock (tileLoadQueue4
)
{
if (tileLoadQueue4Tasks
== null)
{
tileLoadQueue4Tasks
= new List
<Task
>();
while (tileLoadQueue4Tasks
.Count < GThreadPoolSize
)
{
Debug
.WriteLine("creating ProcessLoadTask: " + tileLoadQueue4Tasks
.Count);
tileLoadQueue4Tasks
.Add(Task
.Factory.StartNew(delegate ()
{
string ctid
= "ProcessLoadTask[" + Thread
.CurrentThread.ManagedThreadId + "]";
Thread
.CurrentThread.Name = ctid
;
Debug
.WriteLine(ctid
+ ": started");
do
{
if (tileLoadQueue4
.Count == 0)
{
Debug
.WriteLine(ctid
+ ": ready");
if (Interlocked
.Increment(ref loadWaitCount
) >= GThreadPoolSize
)
{
Interlocked
.Exchange(ref loadWaitCount,
0);
OnLoadComplete
(ctid
);
}
}
ProcessLoadTask
(tileLoadQueue4
.Take(), ctid
);
}
while (!tileLoadQueue4
.IsAddingCompleted);
Debug
.WriteLine(ctid
+ ": exit");
}, TaskCreationOptions
.LongRunning));
}
}
}
}
tileLoadQueue4
.Add(t
);
}
#else
byte loadWaitCount
= 0;
void tileLoadThread
()
{
LoadTask
? task
= null;
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;
OnLoadComplete
(ctid
);
}
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
)
{
ProcessLoadTask
(task
.Value, ctid
);
}
}
#if !PocketPC
Monitor
.Enter(tileLoadQueue
);
try
{
Debug
.WriteLine("Quit - " + ct
.Name);
lock (GThreadPool
)
{
GThreadPool
.Remove(ct
);
}
}
finally
{
Monitor
.Exit(tileLoadQueue
);
}
#endif
}
#endif
static void ProcessLoadTask
(LoadTask task,
string ctid
)
{
try
{
#region -- execute --
var m
= task
.Core.Matrix.GetTileWithReadLock(task
.Zoom, task
.Pos);
if (!m
.NotEmpty)
{
Debug
.WriteLine(ctid
+ " - try load: " + task
);
Tile t
= new Tile
(task
.Zoom, task
.Pos);
foreach (var tl
in task
.Core.provider.Overlays)
{
int retry
= 0;
do
{
PureImage img
= null;
Exception ex
= null;
if (task
.Zoom >= task
.Core.provider.MinZoom && (!task
.Core.provider.MaxZoom.HasValue || task
.Zoom <= task
.Core.provider.MaxZoom))
{
if (task
.Core.skipOverZoom == 0 || task
.Zoom <= task
.Core.skipOverZoom)
{
// tile number inversion(BottomLeft -> TopLeft)
if (tl
.InvertedAxisY)
{
img
= GMaps
.Instance.GetImageFrom(tl,
new GPoint
(task
.Pos.X, task
.Core.maxOfTiles.Height - task
.Pos.Y), task
.Zoom,
out ex
);
}
else // ok
{
img
= GMaps
.Instance.GetImageFrom(tl, task
.Pos, task
.Zoom,
out ex
);
}
}
}
if (img
!= null && ex
== null)
{
if (task
.Core.okZoom < task
.Zoom)
{
task
.Core.okZoom = task
.Zoom;
task
.Core.skipOverZoom = 0;
Debug
.WriteLine("skipOverZoom disabled, okZoom: " + task
.Core.okZoom);
}
}
else if (ex
!= null)
{
if ((task
.Core.skipOverZoom != task
.Core.okZoom) && (task
.Zoom > task
.Core.okZoom))
{
if (ex
.Message.Contains("(404) Not Found"))
{
task
.Core.skipOverZoom = task
.Core.okZoom;
Debug
.WriteLine("skipOverZoom enabled: " + task
.Core.skipOverZoom);
}
}
}
// check for parent tiles if not found
if (img
== null && task
.Core.okZoom > 0 && task
.Core.fillEmptyTiles && task
.Core.Provider.Projection is MercatorProjection
)
{
int zoomOffset
= task
.Zoom > task
.Core.okZoom ? task
.Zoom - task
.Core.okZoom : 1;
long Ix
= 0;
GPoint parentTile
= GPoint
.Empty;
while (img
== null && zoomOffset
< task
.Zoom)
{
Ix
= (long)Math
.Pow(2, zoomOffset
);
parentTile
= new GMap
.NET.GPoint((task
.Pos.X / Ix
),
(task
.Pos.Y / Ix
));
img
= GMaps
.Instance.GetImageFrom(tl, parentTile, task
.Zoom - zoomOffset
++,
out ex
);
}
if (img
!= null)
{
// offsets in quadrant
long Xoff
= Math
.Abs(task
.Pos.X - (parentTile
.X * Ix
));
long Yoff
= Math
.Abs(task
.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 (task
.Core.FailedLoads)
{
if (!task
.Core.FailedLoads.ContainsKey(task
))
{
task
.Core.FailedLoads.Add(task, ex
);
if (task
.Core.OnEmptyTileError != null)
{
if (!task
.Core.RaiseEmptyTileError)
{
task
.Core.RaiseEmptyTileError = true;
task
.Core.OnEmptyTileError(task
.Zoom, task
.Pos);
}
}
}
}
}
if (task
.Core.RetryLoadTile > 0)
{
Debug
.WriteLine(ctid
+ " - ProcessLoadTask: " + task
+ " -> empty tile, retry " + retry
);
{
Thread
.Sleep(1111);
}
}
}
}
while (++retry
< task
.Core.RetryLoadTile);
}
if (t
.HasAnyOverlays && task
.Core.IsStarted)
{
task
.Core.Matrix.SetTile(t
);
}
else
{
t
.Dispose();
}
}
#endregion
}
catch (Exception ex
)
{
Debug
.WriteLine(ctid
+ " - ProcessLoadTask: " + ex
.ToString());
}
finally
{
if (task
.Core.Refresh != null)
{
task
.Core.Refresh.Set();
}
}
}
void OnLoadComplete
(string ctid
)
{
LastTileLoadEnd
= DateTime
.Now;
long 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
);
}
}
public AutoResetEvent Refresh
= new AutoResetEvent
(false);
public bool updatingBounds
= false;
/// <summary>
/// updates map bounds
/// </summary>
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<DrawTile
>(tileDrawingList
);
}
else
{
tileDrawingList
.Sort();
}
#endregion
}
finally
{
tileDrawingListLock
.ReleaseWriterLock();
}
#if NET40
Interlocked
.Exchange(ref loadWaitCount,
0);
#else
Monitor
.Enter(tileLoadQueue
);
try
{
#endif
tileDrawingListLock
.AcquireReaderLock();
try
{
foreach (DrawTile p
in tileDrawingList
)
{
LoadTask task
= new LoadTask
(p
.PosXY, Zoom,
this);
#if NET40
AddLoadTask
(task
);
#else
{
if (!tileLoadQueue
.Contains(task
))
{
tileLoadQueue
.Push(task
);
}
}
#endif
}
}
finally
{
tileDrawingListLock
.ReleaseReaderLock();
}
#if !NET40
#region -- starts loader threads if needed --
lock (GThreadPool
)
{
while (GThreadPool
.Count < GThreadPoolSize
)
{
Thread t
= new Thread
(new ThreadStart
(tileLoadThread
));
{
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
#endif
{
LastTileLoadStart
= DateTime
.Now;
Debug
.WriteLine("OnTileLoadStart - at zoom " + Zoom
+ ", time: " + LastTileLoadStart
.TimeOfDay);
}
#if !NET40
loadWaitCount
= 0;
Monitor
.PulseAll(tileLoadQueue
);
}
finally
{
Monitor
.Exit(tileLoadQueue
);
}
#endif
updatingBounds
= false;
if (OnTileLoadStart
!= null)
{
OnTileLoadStart
();
}
}
/// <summary>
/// updates ground resolution info
/// </summary>
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;
}
tileDrawingListLock
.AcquireWriterLock();
try
{
tileDrawingList
.Clear();
}
finally
{
tileDrawingListLock
.ReleaseWriterLock();
}
#if NET40
//TODO: maybe
#else
// cancel waiting loaders
Monitor
.Enter(tileLoadQueue
);
try
{
Monitor
.PulseAll(tileLoadQueue
);
}
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
}
#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
}
}