0,0 → 1,1400 |
|
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 |
} |
} |