Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2498 - 1

2
namespace GMap.NET.Internals
3
{
4
    using System;
5
    using System.Collections.Generic;
6
    using System.Diagnostics;
7
    using System.Threading;
8
    using GMap.NET.Projections;
9
    using System.IO;
10
    using GMap.NET.MapProviders;
11
    using System.ComponentModel;
12
 
13
#if NET40
14
    using System.Collections.Concurrent;
15
    using System.Threading.Tasks;
16
#endif
17
 
18
#if PocketPC
19
   using OpenNETCF.ComponentModel;
20
   using OpenNETCF.Threading;
21
   using Thread=OpenNETCF.Threading.Thread2;
22
#endif
23
 
24
    /// <summary>
25
    /// internal map control core
26
    /// </summary>
27
    internal class Core : IDisposable
28
    {
29
        public PointLatLng position;
30
        public GPoint positionPixel;
31
 
32
        public GPoint renderOffset;
33
        public GPoint centerTileXYLocation;
34
        public GPoint centerTileXYLocationLast;
35
        public GPoint dragPoint;
36
        public GPoint compensationOffset;
37
 
38
        public GPoint mouseDown;
39
        public GPoint mouseCurrent;
40
        public GPoint mouseLastZoom;
41
 
42
        public MouseWheelZoomType MouseWheelZoomType = MouseWheelZoomType.MousePositionAndCenter;
43
        public bool MouseWheelZoomEnabled = true;
44
 
45
        public PointLatLng? LastLocationInBounds = null;
46
        public bool VirtualSizeEnabled = false;
47
 
48
        public GSize sizeOfMapArea;
49
        public GSize minOfTiles;
50
        public GSize maxOfTiles;
51
 
52
        public GRect tileRect;
53
        public GRect tileRectBearing;
54
        //public GRect currentRegion;
55
        public float bearing = 0;
56
        public bool IsRotated = false;
57
 
58
        public bool fillEmptyTiles = true;
59
 
60
        public TileMatrix Matrix = new TileMatrix();
61
 
62
        public List<DrawTile> tileDrawingList = new List<DrawTile>();
63
        public FastReaderWriterLock tileDrawingListLock = new FastReaderWriterLock();
64
 
65
#if !NET40
66
        public readonly Stack<LoadTask> tileLoadQueue = new Stack<LoadTask>();
67
#endif
68
 
69
#if !PocketPC
70
        static readonly int GThreadPoolSize = 4;
71
#else
72
        static readonly int GThreadPoolSize = 2;
73
#endif
74
 
75
        DateTime LastTileLoadStart = DateTime.Now;
76
        DateTime LastTileLoadEnd = DateTime.Now;
77
        internal volatile bool IsStarted = false;
78
        int zoom;
79
 
80
        internal double scaleX = 1;
81
        internal double scaleY = 1;
82
 
83
        internal int maxZoom = 2;
84
        internal int minZoom = 2;
85
        internal int Width;
86
        internal int Height;
87
 
88
        internal int pxRes100m;  // 100 meters
89
        internal int pxRes1000m;  // 1km  
90
        internal int pxRes10km; // 10km
91
        internal int pxRes100km; // 100km
92
        internal int pxRes1000km; // 1000km
93
        internal int pxRes5000km; // 5000km
94
 
95
        /// <summary>
96
        /// is user dragging map
97
        /// </summary>
98
        public bool IsDragging = false;
99
 
100
        public Core()
101
        {
102
            Provider = EmptyProvider.Instance;
103
        }
104
 
105
        /// <summary>
106
        /// map zoom
107
        /// </summary>
108
        public int Zoom
109
        {
110
            get
111
            {
112
                return zoom;
113
            }
114
            set
115
            {
116
                if (zoom != value && !IsDragging)
117
                {
118
                    zoom = value;
119
 
120
                    minOfTiles = Provider.Projection.GetTileMatrixMinXY(value);
121
                    maxOfTiles = Provider.Projection.GetTileMatrixMaxXY(value);
122
 
123
                    positionPixel = Provider.Projection.FromLatLngToPixel(Position, value);
124
 
125
                    if (IsStarted)
126
                    {
127
                        CancelAsyncTasks();
128
 
129
                        Matrix.ClearLevelsBelove(zoom - LevelsKeepInMemmory);
130
                        Matrix.ClearLevelsAbove(zoom + LevelsKeepInMemmory);
131
 
132
                        lock (FailedLoads)
133
                        {
134
                            FailedLoads.Clear();
135
                            RaiseEmptyTileError = true;
136
                        }
137
 
138
                        GoToCurrentPositionOnZoom();
139
                        UpdateBounds();
140
 
141
                        if (OnMapZoomChanged != null)
142
                        {
143
                            OnMapZoomChanged();
144
                        }
145
                    }
146
                }
147
            }
148
        }
149
 
150
        /// <summary>
151
        /// current marker position in pixel coordinates
152
        /// </summary>
153
        public GPoint PositionPixel
154
        {
155
            get
156
            {
157
                return positionPixel;
158
            }
159
        }
160
 
161
        /// <summary>
162
        /// current marker position
163
        /// </summary>
164
        public PointLatLng Position
165
        {
166
            get
167
            {
168
 
169
                return position;
170
            }
171
            set
172
            {
173
                position = value;
174
                positionPixel = Provider.Projection.FromLatLngToPixel(value, Zoom);
175
 
176
                if (IsStarted)
177
                {
178
                    if (!IsDragging)
179
                    {
180
                        GoToCurrentPosition();
181
                    }
182
 
183
                    if (OnCurrentPositionChanged != null)
184
                        OnCurrentPositionChanged(position);
185
                }
186
            }
187
        }
188
 
189
        public GMapProvider provider;
190
        public GMapProvider Provider
191
        {
192
            get
193
            {
194
                return provider;
195
            }
196
            set
197
            {
198
                if (provider == null || !provider.Equals(value))
199
                {
200
                    bool diffProjection = (provider == null || provider.Projection != value.Projection);
201
 
202
                    provider = value;
203
 
204
                    if (!provider.IsInitialized)
205
                    {
206
                        provider.IsInitialized = true;
207
                        provider.OnInitialized();
208
                    }
209
 
210
                    if (provider.Projection != null && diffProjection)
211
                    {
212
                        tileRect = new GRect(GPoint.Empty, Provider.Projection.TileSize);
213
                        tileRectBearing = tileRect;
214
                        if (IsRotated)
215
                        {
216
                            tileRectBearing.Inflate(1, 1);
217
                        }
218
 
219
                        minOfTiles = Provider.Projection.GetTileMatrixMinXY(Zoom);
220
                        maxOfTiles = Provider.Projection.GetTileMatrixMaxXY(Zoom);
221
                        positionPixel = Provider.Projection.FromLatLngToPixel(Position, Zoom);
222
                    }
223
 
224
                    if (IsStarted)
225
                    {
226
                        CancelAsyncTasks();
227
                        if (diffProjection)
228
                        {
229
                            OnMapSizeChanged(Width, Height);
230
                        }
231
                        ReloadMap();
232
 
233
                        if (minZoom < provider.MinZoom)
234
                        {
235
                            minZoom = provider.MinZoom;
236
                        }
237
 
238
                        //if(provider.MaxZoom.HasValue && maxZoom > provider.MaxZoom)
239
                        //{
240
                        //   maxZoom = provider.MaxZoom.Value;
241
                        //}
242
 
243
                        zoomToArea = true;
244
 
245
                        if (provider.Area.HasValue && !provider.Area.Value.Contains(Position))
246
                        {
247
                            SetZoomToFitRect(provider.Area.Value);
248
                            zoomToArea = false;
249
                        }
250
 
251
                        if (OnMapTypeChanged != null)
252
                        {
253
                            OnMapTypeChanged(value);
254
                        }
255
                    }
256
                }
257
            }
258
        }
259
 
260
        internal bool zoomToArea = true;
261
 
262
        /// <summary>
263
        /// sets zoom to max to fit rect
264
        /// </summary>
265
        /// <param name="rect"></param>
266
        /// <returns></returns>
267
        public bool SetZoomToFitRect(RectLatLng rect)
268
        {
269
            int mmaxZoom = GetMaxZoomToFitRect(rect);
270
            if (mmaxZoom > 0)
271
            {
272
                PointLatLng center = new PointLatLng(rect.Lat - (rect.HeightLat / 2), rect.Lng + (rect.WidthLng / 2));
273
                Position = center;
274
 
275
                if (mmaxZoom > maxZoom)
276
                {
277
                    mmaxZoom = maxZoom;
278
                }
279
 
280
                if (Zoom != mmaxZoom)
281
                {
282
                    Zoom = (int)mmaxZoom;
283
                }
284
 
285
                return true;
286
            }
287
            return false;
288
        }
289
 
290
        /// <summary>
291
        /// is polygons enabled
292
        /// </summary>
293
        public bool PolygonsEnabled = true;
294
 
295
        /// <summary>
296
        /// is routes enabled
297
        /// </summary>
298
        public bool RoutesEnabled = true;
299
 
300
        /// <summary>
301
        /// is markers enabled
302
        /// </summary>
303
        public bool MarkersEnabled = true;
304
 
305
        /// <summary>
306
        /// can user drag map
307
        /// </summary>
308
        public bool CanDragMap = true;
309
 
310
        /// <summary>
311
        /// retry count to get tile 
312
        /// </summary>
313
#if !PocketPC
314
        public int RetryLoadTile = 0;
315
#else
316
      public int RetryLoadTile = 1;
317
#endif
318
 
319
        /// <summary>
320
        /// how many levels of tiles are staying decompresed in memory
321
        /// </summary>
322
#if !PocketPC
323
        public int LevelsKeepInMemmory = 5;
324
#else
325
      public int LevelsKeepInMemmory = 1;
326
#endif
327
 
328
        /// <summary>
329
        /// map render mode
330
        /// </summary>
331
        public RenderMode RenderMode = RenderMode.GDI_PLUS;
332
 
333
        /// <summary>
334
        /// occurs when current position is changed
335
        /// </summary>
336
        public event PositionChanged OnCurrentPositionChanged;
337
 
338
        /// <summary>
339
        /// occurs when tile set load is complete
340
        /// </summary>
341
        public event TileLoadComplete OnTileLoadComplete;
342
 
343
        /// <summary>
344
        /// occurs when tile set is starting to load
345
        /// </summary>
346
        public event TileLoadStart OnTileLoadStart;
347
 
348
        /// <summary>
349
        /// occurs on empty tile displayed
350
        /// </summary>
351
        public event EmptyTileError OnEmptyTileError;
352
 
353
        /// <summary>
354
        /// occurs on map drag
355
        /// </summary>
356
        public event MapDrag OnMapDrag;
357
 
358
        /// <summary>
359
        /// occurs on map zoom changed
360
        /// </summary>
361
        public event MapZoomChanged OnMapZoomChanged;
362
 
363
        /// <summary>
364
        /// occurs on map type changed
365
        /// </summary>
366
        public event MapTypeChanged OnMapTypeChanged;
367
 
368
        readonly List<Thread> GThreadPool = new List<Thread>();
369
        // ^
370
        // should be only one pool for multiply controls, any ideas how to fix?
371
        //static readonly List<Thread> GThreadPool = new List<Thread>();
372
 
373
        // windows forms or wpf
374
        internal string SystemType;
375
 
376
        internal static int instances = 0;
377
 
378
        BackgroundWorker invalidator;
379
 
380
        public BackgroundWorker OnMapOpen()
381
        {
382
            if (!IsStarted)
383
            {
384
                int x = Interlocked.Increment(ref instances);
385
                Debug.WriteLine("OnMapOpen: " + x);
386
 
387
                IsStarted = true;
388
 
389
                if (x == 1)
390
                {
391
                    GMaps.Instance.noMapInstances = false;
392
                }
393
 
394
                GoToCurrentPosition();
395
 
396
                invalidator = new BackgroundWorker();
397
                invalidator.WorkerSupportsCancellation = true;
398
                invalidator.WorkerReportsProgress = true;
399
                invalidator.DoWork += new DoWorkEventHandler(invalidatorWatch);
400
                invalidator.RunWorkerAsync();
401
 
402
                //if(x == 1)
403
                //{
404
                // first control shown
405
                //}
406
            }
407
            return invalidator;
408
        }
409
 
410
        public void OnMapClose()
411
        {
412
            Dispose();
413
        }
414
 
415
        internal readonly object invalidationLock = new object();
416
        internal DateTime lastInvalidation = DateTime.Now;
417
 
418
        void invalidatorWatch(object sender, DoWorkEventArgs e)
419
        {
420
            var w = sender as BackgroundWorker;
421
 
422
            TimeSpan span = TimeSpan.FromMilliseconds(111);
423
            int spanMs = (int)span.TotalMilliseconds;
424
            bool skiped = false;
425
            TimeSpan delta;
426
            DateTime now = DateTime.Now;
427
 
428
            while (Refresh != null && (!skiped && Refresh.WaitOne() || (Refresh.WaitOne(spanMs, false) || true)))
429
            {
430
                if (w.CancellationPending)
431
                    break;
432
 
433
                now = DateTime.Now;
434
                lock (invalidationLock)
435
                {
436
                    delta = now - lastInvalidation;
437
                }
438
 
439
                if (delta > span)
440
                {
441
                    lock (invalidationLock)
442
                    {
443
                        lastInvalidation = now;
444
                    }
445
                    skiped = false;
446
 
447
                    w.ReportProgress(1);
448
                    Debug.WriteLine("Invalidate delta: " + (int)delta.TotalMilliseconds + "ms");
449
                }
450
                else
451
                {
452
                    skiped = true;
453
                }
454
            }
455
        }
456
 
457
        public void UpdateCenterTileXYLocation()
458
        {
459
            PointLatLng center = FromLocalToLatLng(Width / 2, Height / 2);
460
            GPoint centerPixel = Provider.Projection.FromLatLngToPixel(center, Zoom);
461
            centerTileXYLocation = Provider.Projection.FromPixelToTileXY(centerPixel);
462
        }
463
 
464
        public int vWidth = 800;
465
        public int vHeight = 400;
466
 
467
        public void OnMapSizeChanged(int width, int height)
468
        {
469
            this.Width = width;
470
            this.Height = height;
471
 
472
            if (IsRotated)
473
            {
474
#if !PocketPC
475
                int diag = (int)Math.Round(Math.Sqrt(Width * Width + Height * Height) / Provider.Projection.TileSize.Width, MidpointRounding.AwayFromZero);
476
#else
477
            int diag = (int) Math.Round(Math.Sqrt(Width * Width + Height * Height) / Provider.Projection.TileSize.Width);
478
#endif
479
                sizeOfMapArea.Width = 1 + (diag / 2);
480
                sizeOfMapArea.Height = 1 + (diag / 2);
481
            }
482
            else
483
            {
484
                sizeOfMapArea.Width = 1 + (Width / Provider.Projection.TileSize.Width) / 2;
485
                sizeOfMapArea.Height = 1 + (Height / Provider.Projection.TileSize.Height) / 2;
486
            }
487
 
488
            Debug.WriteLine("OnMapSizeChanged, w: " + width + ", h: " + height + ", size: " + sizeOfMapArea);
489
 
490
            if (IsStarted)
491
            {
492
                UpdateBounds();
493
                GoToCurrentPosition();
494
            }
495
        }
496
 
497
        /// <summary>
498
        /// gets current map view top/left coordinate, width in Lng, height in Lat
499
        /// </summary>
500
        /// <returns></returns>
501
        public RectLatLng ViewArea
502
        {
503
            get
504
            {
505
                if (Provider.Projection != null)
506
                {
507
                    var p = FromLocalToLatLng(0, 0);
508
                    var p2 = FromLocalToLatLng(Width, Height);
509
 
510
                    return RectLatLng.FromLTRB(p.Lng, p.Lat, p2.Lng, p2.Lat);
511
                }
512
                return RectLatLng.Empty;
513
            }
514
        }
515
 
516
        /// <summary>
517
        /// gets lat/lng from local control coordinates
518
        /// </summary>
519
        /// <param name="x"></param>
520
        /// <param name="y"></param>
521
        /// <returns></returns>
522
        public PointLatLng FromLocalToLatLng(long x, long y)
523
        {
524
            GPoint p = new GPoint(x, y);
525
            p.OffsetNegative(renderOffset);
526
            p.Offset(compensationOffset);
527
 
528
            return Provider.Projection.FromPixelToLatLng(p, Zoom);
529
        }
530
 
531
        /// <summary>
532
        /// return local coordinates from lat/lng
533
        /// </summary>
534
        /// <param name="latlng"></param>
535
        /// <returns></returns>
536
        public GPoint FromLatLngToLocal(PointLatLng latlng)
537
        {
538
            GPoint pLocal = Provider.Projection.FromLatLngToPixel(latlng, Zoom);
539
            pLocal.Offset(renderOffset);
540
            pLocal.OffsetNegative(compensationOffset);
541
            return pLocal;
542
        }
543
 
544
        /// <summary>
545
        /// gets max zoom level to fit rectangle
546
        /// </summary>
547
        /// <param name="rect"></param>
548
        /// <returns></returns>
549
        public int GetMaxZoomToFitRect(RectLatLng rect)
550
        {
551
            int zoom = minZoom;
552
 
553
            if (rect.HeightLat == 0 || rect.WidthLng == 0)
554
            {
555
                zoom = maxZoom / 2;
556
            }
557
            else
558
            {
559
                for (int i = (int)zoom; i <= maxZoom; i++)
560
                {
561
                    GPoint p1 = Provider.Projection.FromLatLngToPixel(rect.LocationTopLeft, i);
562
                    GPoint p2 = Provider.Projection.FromLatLngToPixel(rect.LocationRightBottom, i);
563
 
564
                    if (((p2.X - p1.X) <= Width + 10) && (p2.Y - p1.Y) <= Height + 10)
565
                    {
566
                        zoom = i;
567
                    }
568
                    else
569
                    {
570
                        break;
571
                    }
572
                }
573
            }
574
 
575
            return zoom;
576
        }
577
 
578
        /// <summary>
579
        /// initiates map dragging
580
        /// </summary>
581
        /// <param name="pt"></param>
582
        public void BeginDrag(GPoint pt)
583
        {
584
            dragPoint.X = pt.X - renderOffset.X;
585
            dragPoint.Y = pt.Y - renderOffset.Y;
586
            IsDragging = true;
587
        }
588
 
589
        /// <summary>
590
        /// ends map dragging
591
        /// </summary>
592
        public void EndDrag()
593
        {
594
            IsDragging = false;
595
            mouseDown = GPoint.Empty;
596
 
597
            Refresh.Set();
598
        }
599
 
600
        /// <summary>
601
        /// reloads map
602
        /// </summary>
603
        public void ReloadMap()
604
        {
605
            if (IsStarted)
606
            {
607
                Debug.WriteLine("------------------");
608
 
609
                okZoom = 0;
610
                skipOverZoom = 0;
611
 
612
                CancelAsyncTasks();
613
 
614
                Matrix.ClearAllLevels();
615
 
616
                lock (FailedLoads)
617
                {
618
                    FailedLoads.Clear();
619
                    RaiseEmptyTileError = true;
620
                }
621
 
622
                Refresh.Set();
623
 
624
                UpdateBounds();
625
            }
626
            else
627
            {
628
                throw new Exception("Please, do not call ReloadMap before form is loaded, it's useless");
629
            }
630
        }
631
 
632
        /// <summary>
633
        /// moves current position into map center
634
        /// </summary>
635
        public void GoToCurrentPosition()
636
        {
637
            compensationOffset = positionPixel; // TODO: fix
638
 
639
            // reset stuff
640
            renderOffset = GPoint.Empty;
641
            dragPoint = GPoint.Empty;
642
 
643
            //var dd = new GPoint(-(CurrentPositionGPixel.X - Width / 2), -(CurrentPositionGPixel.Y - Height / 2));
644
            //dd.Offset(compensationOffset);
645
 
646
            var d = new GPoint(Width / 2, Height / 2);
647
 
648
            this.Drag(d);
649
        }
650
 
651
        public bool MouseWheelZooming = false;
652
 
653
        /// <summary>
654
        /// moves current position into map center
655
        /// </summary>
656
        internal void GoToCurrentPositionOnZoom()
657
        {
658
            compensationOffset = positionPixel; // TODO: fix
659
 
660
            // reset stuff
661
            renderOffset = GPoint.Empty;
662
            dragPoint = GPoint.Empty;
663
 
664
            // goto location and centering
665
            if (MouseWheelZooming)
666
            {
667
                if (MouseWheelZoomType != MouseWheelZoomType.MousePositionWithoutCenter)
668
                {
669
                    GPoint pt = new GPoint(-(positionPixel.X - Width / 2), -(positionPixel.Y - Height / 2));
670
                    pt.Offset(compensationOffset);
671
                    renderOffset.X = pt.X - dragPoint.X;
672
                    renderOffset.Y = pt.Y - dragPoint.Y;
673
                }
674
                else // without centering
675
                {
676
                    renderOffset.X = -positionPixel.X - dragPoint.X;
677
                    renderOffset.Y = -positionPixel.Y - dragPoint.Y;
678
                    renderOffset.Offset(mouseLastZoom);
679
                    renderOffset.Offset(compensationOffset);
680
                }
681
            }
682
            else // use current map center
683
            {
684
                mouseLastZoom = GPoint.Empty;
685
 
686
                GPoint pt = new GPoint(-(positionPixel.X - Width / 2), -(positionPixel.Y - Height / 2));
687
                pt.Offset(compensationOffset);
688
                renderOffset.X = pt.X - dragPoint.X;
689
                renderOffset.Y = pt.Y - dragPoint.Y;
690
            }
691
 
692
            UpdateCenterTileXYLocation();
693
        }
694
 
695
        /// <summary>
696
        /// darg map by offset in pixels
697
        /// </summary>
698
        /// <param name="offset"></param>
699
        public void DragOffset(GPoint offset)
700
        {
701
            renderOffset.Offset(offset);
702
 
703
            UpdateCenterTileXYLocation();
704
 
705
            if (centerTileXYLocation != centerTileXYLocationLast)
706
            {
707
                centerTileXYLocationLast = centerTileXYLocation;
708
                UpdateBounds();
709
            }
710
 
711
            {
712
                LastLocationInBounds = Position;
713
 
714
                IsDragging = true;
715
                Position = FromLocalToLatLng((int)Width / 2, (int)Height / 2);
716
                IsDragging = false;
717
            }
718
 
719
            if (OnMapDrag != null)
720
            {
721
                OnMapDrag();
722
            }
723
        }
724
 
725
        /// <summary>
726
        /// drag map
727
        /// </summary>
728
        /// <param name="pt"></param>
729
        public void Drag(GPoint pt)
730
        {
731
            renderOffset.X = pt.X - dragPoint.X;
732
            renderOffset.Y = pt.Y - dragPoint.Y;
733
 
734
            UpdateCenterTileXYLocation();
735
 
736
            if (centerTileXYLocation != centerTileXYLocationLast)
737
            {
738
                centerTileXYLocationLast = centerTileXYLocation;
739
                UpdateBounds();
740
            }
741
 
742
            if (IsDragging)
743
            {
744
                LastLocationInBounds = Position;
745
                Position = FromLocalToLatLng((int)Width / 2, (int)Height / 2);
746
 
747
                if (OnMapDrag != null)
748
                {
749
                    OnMapDrag();
750
                }
751
            }
752
        }
753
 
754
        /// <summary>
755
        /// cancels tile loaders and bounds checker
756
        /// </summary>
757
        public void CancelAsyncTasks()
758
        {
759
            if (IsStarted)
760
            {
761
#if NET40
762
                //TODO: clear loading
763
#else
764
                Monitor.Enter(tileLoadQueue);
765
                try
766
                {
767
                    tileLoadQueue.Clear();
768
                }
769
                finally
770
                {
771
                    Monitor.Exit(tileLoadQueue);
772
                }
773
#endif
774
            }
775
        }
776
 
777
        bool RaiseEmptyTileError = false;
778
        internal Dictionary<LoadTask, Exception> FailedLoads = new Dictionary<LoadTask, Exception>(new LoadTaskComparer());
779
 
780
        internal static readonly int WaitForTileLoadThreadTimeout = 5 * 1000 * 60; // 5 min.
781
 
782
        volatile int okZoom = 0;
783
        volatile int skipOverZoom = 0;
784
 
785
#if NET40
786
        static readonly BlockingCollection<LoadTask> tileLoadQueue4 = new BlockingCollection<LoadTask>(new ConcurrentStack<LoadTask>());
787
        static List<Task> tileLoadQueue4Tasks;
788
        static int loadWaitCount = 0;
789
        void AddLoadTask(LoadTask t)
790
        {
791
            if (tileLoadQueue4Tasks == null)
792
            {
793
                lock (tileLoadQueue4)
794
                {
795
                    if (tileLoadQueue4Tasks == null)
796
                    {
797
                        tileLoadQueue4Tasks = new List<Task>();
798
 
799
                        while (tileLoadQueue4Tasks.Count < GThreadPoolSize)
800
                        {
801
                            Debug.WriteLine("creating ProcessLoadTask: " + tileLoadQueue4Tasks.Count);
802
 
803
                            tileLoadQueue4Tasks.Add(Task.Factory.StartNew(delegate ()
804
                            {
805
                                string ctid = "ProcessLoadTask[" + Thread.CurrentThread.ManagedThreadId + "]";
806
                                Thread.CurrentThread.Name = ctid;
807
 
808
                                Debug.WriteLine(ctid + ": started");
809
                                do
810
                                {
811
                                    if (tileLoadQueue4.Count == 0)
812
                                    {
813
                                        Debug.WriteLine(ctid + ": ready");
814
 
815
                                        if (Interlocked.Increment(ref loadWaitCount) >= GThreadPoolSize)
816
                                        {
817
                                            Interlocked.Exchange(ref loadWaitCount, 0);
818
                                            OnLoadComplete(ctid);
819
                                        }
820
                                    }
821
                                    ProcessLoadTask(tileLoadQueue4.Take(), ctid);
822
                                }
823
                                while (!tileLoadQueue4.IsAddingCompleted);
824
 
825
                                Debug.WriteLine(ctid + ": exit");
826
 
827
                            }, TaskCreationOptions.LongRunning));
828
                        }
829
                    }
830
                }
831
            }
832
            tileLoadQueue4.Add(t);
833
        }
834
#else
835
        byte loadWaitCount = 0;
836
 
837
        void tileLoadThread()
838
        {
839
            LoadTask? task = null;
840
            bool stop = false;
841
 
842
#if !PocketPC
843
            Thread ct = Thread.CurrentThread;
844
            string ctid = "Thread[" + ct.ManagedThreadId + "]";
845
#else
846
            int ctid = 0;
847
#endif
848
            while (!stop && IsStarted)
849
            {
850
                task = null;
851
 
852
                Monitor.Enter(tileLoadQueue);
853
                try
854
                {
855
                    while (tileLoadQueue.Count == 0)
856
                    {
857
                        Debug.WriteLine(ctid + " - Wait " + loadWaitCount + " - " + DateTime.Now.TimeOfDay);
858
 
859
                        if (++loadWaitCount >= GThreadPoolSize)
860
                        {
861
                            loadWaitCount = 0;
862
                            OnLoadComplete(ctid);
863
                        }
864
 
865
                        if (!IsStarted || false == Monitor.Wait(tileLoadQueue, WaitForTileLoadThreadTimeout, false) || !IsStarted)
866
                        {
867
                            stop = true;
868
                            break;
869
                        }
870
                    }
871
 
872
                    if (IsStarted && !stop || tileLoadQueue.Count > 0)
873
                    {
874
                        task = tileLoadQueue.Pop();
875
                    }
876
                }
877
                finally
878
                {
879
                    Monitor.Exit(tileLoadQueue);
880
                }
881
 
882
                if (task.HasValue && IsStarted)
883
                {
884
                    ProcessLoadTask(task.Value, ctid);
885
                }
886
            }
887
 
888
#if !PocketPC
889
            Monitor.Enter(tileLoadQueue);
890
            try
891
            {
892
                Debug.WriteLine("Quit - " + ct.Name);
893
                lock (GThreadPool)
894
                {
895
                    GThreadPool.Remove(ct);
896
                }
897
            }
898
            finally
899
            {
900
                Monitor.Exit(tileLoadQueue);
901
            }
902
#endif
903
        }
904
#endif
905
 
906
        static void ProcessLoadTask(LoadTask task, string ctid)
907
        {
908
            try
909
            {
910
                #region -- execute --
911
 
912
                var m = task.Core.Matrix.GetTileWithReadLock(task.Zoom, task.Pos);
913
                if (!m.NotEmpty)
914
                {
915
                    Debug.WriteLine(ctid + " - try load: " + task);
916
 
917
                    Tile t = new Tile(task.Zoom, task.Pos);
918
 
919
                    foreach (var tl in task.Core.provider.Overlays)
920
                    {
921
                        int retry = 0;
922
                        do
923
                        {
924
                            PureImage img = null;
925
                            Exception ex = null;
926
 
927
                            if (task.Zoom >= task.Core.provider.MinZoom && (!task.Core.provider.MaxZoom.HasValue || task.Zoom <= task.Core.provider.MaxZoom))
928
                            {
929
                                if (task.Core.skipOverZoom == 0 || task.Zoom <= task.Core.skipOverZoom)
930
                                {
931
                                    // tile number inversion(BottomLeft -> TopLeft)
932
                                    if (tl.InvertedAxisY)
933
                                    {
934
                                        img = GMaps.Instance.GetImageFrom(tl, new GPoint(task.Pos.X, task.Core.maxOfTiles.Height - task.Pos.Y), task.Zoom, out ex);
935
                                    }
936
                                    else // ok
937
                                    {
938
                                        img = GMaps.Instance.GetImageFrom(tl, task.Pos, task.Zoom, out ex);
939
                                    }
940
                                }
941
                            }
942
 
943
                            if (img != null && ex == null)
944
                            {
945
                                if (task.Core.okZoom < task.Zoom)
946
                                {
947
                                    task.Core.okZoom = task.Zoom;
948
                                    task.Core.skipOverZoom = 0;
949
                                    Debug.WriteLine("skipOverZoom disabled, okZoom: " + task.Core.okZoom);
950
                                }
951
                            }
952
                            else if (ex != null)
953
                            {
954
                                if ((task.Core.skipOverZoom != task.Core.okZoom) && (task.Zoom > task.Core.okZoom))
955
                                {
956
                                    if (ex.Message.Contains("(404) Not Found"))
957
                                    {
958
                                        task.Core.skipOverZoom = task.Core.okZoom;
959
                                        Debug.WriteLine("skipOverZoom enabled: " + task.Core.skipOverZoom);
960
                                    }
961
                                }
962
                            }
963
 
964
                            // check for parent tiles if not found
965
                            if (img == null && task.Core.okZoom > 0 && task.Core.fillEmptyTiles && task.Core.Provider.Projection is MercatorProjection)
966
                            {
967
                                int zoomOffset = task.Zoom > task.Core.okZoom ? task.Zoom - task.Core.okZoom : 1;
968
                                long Ix = 0;
969
                                GPoint parentTile = GPoint.Empty;
970
 
971
                                while (img == null && zoomOffset < task.Zoom)
972
                                {
973
                                    Ix = (long)Math.Pow(2, zoomOffset);
974
                                    parentTile = new GMap.NET.GPoint((task.Pos.X / Ix), (task.Pos.Y / Ix));
975
                                    img = GMaps.Instance.GetImageFrom(tl, parentTile, task.Zoom - zoomOffset++, out ex);
976
                                }
977
 
978
                                if (img != null)
979
                                {
980
                                    // offsets in quadrant
981
                                    long Xoff = Math.Abs(task.Pos.X - (parentTile.X * Ix));
982
                                    long Yoff = Math.Abs(task.Pos.Y - (parentTile.Y * Ix));
983
 
984
                                    img.IsParent = true;
985
                                    img.Ix = Ix;
986
                                    img.Xoff = Xoff;
987
                                    img.Yoff = Yoff;
988
 
989
                                    // wpf
990
                                    //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));
991
                                    //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);
992
 
993
                                    // gdi+
994
                                    //System.Drawing.Rectangle dst = new System.Drawing.Rectangle((int)Core.tileRect.X, (int)Core.tileRect.Y, (int)Core.tileRect.Width, (int)Core.tileRect.Height);
995
                                    //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));
996
                                }
997
                            }
998
 
999
                            if (img != null)
1000
                            {
1001
                                Debug.WriteLine(ctid + " - tile loaded: " + img.Data.Length / 1024 + "KB, " + task);
1002
                                {
1003
                                    t.AddOverlay(img);
1004
                                }
1005
                                break;
1006
                            }
1007
                            else
1008
                            {
1009
                                if (ex != null)
1010
                                {
1011
                                    lock (task.Core.FailedLoads)
1012
                                    {
1013
                                        if (!task.Core.FailedLoads.ContainsKey(task))
1014
                                        {
1015
                                            task.Core.FailedLoads.Add(task, ex);
1016
 
1017
                                            if (task.Core.OnEmptyTileError != null)
1018
                                            {
1019
                                                if (!task.Core.RaiseEmptyTileError)
1020
                                                {
1021
                                                    task.Core.RaiseEmptyTileError = true;
1022
                                                    task.Core.OnEmptyTileError(task.Zoom, task.Pos);
1023
                                                }
1024
                                            }
1025
                                        }
1026
                                    }
1027
                                }
1028
 
1029
                                if (task.Core.RetryLoadTile > 0)
1030
                                {
1031
                                    Debug.WriteLine(ctid + " - ProcessLoadTask: " + task + " -> empty tile, retry " + retry);
1032
                                    {
1033
                                        Thread.Sleep(1111);
1034
                                    }
1035
                                }
1036
                            }
1037
                        }
1038
                        while (++retry < task.Core.RetryLoadTile);
1039
                    }
1040
 
1041
                    if (t.HasAnyOverlays && task.Core.IsStarted)
1042
                    {
1043
                        task.Core.Matrix.SetTile(t);
1044
                    }
1045
                    else
1046
                    {
1047
                        t.Dispose();
1048
                    }
1049
                }
1050
 
1051
                #endregion
1052
            }
1053
            catch (Exception ex)
1054
            {
1055
                Debug.WriteLine(ctid + " - ProcessLoadTask: " + ex.ToString());
1056
            }
1057
            finally
1058
            {
1059
                if (task.Core.Refresh != null)
1060
                {
1061
                    task.Core.Refresh.Set();
1062
                }
1063
            }
1064
        }
1065
 
1066
        void OnLoadComplete(string ctid)
1067
        {
1068
            LastTileLoadEnd = DateTime.Now;
1069
            long lastTileLoadTimeMs = (long)(LastTileLoadEnd - LastTileLoadStart).TotalMilliseconds;
1070
 
1071
            #region -- clear stuff--
1072
            if (IsStarted)
1073
            {
1074
                GMaps.Instance.MemoryCache.RemoveOverload();
1075
 
1076
                tileDrawingListLock.AcquireReaderLock();
1077
                try
1078
                {
1079
                    Matrix.ClearLevelAndPointsNotIn(Zoom, tileDrawingList);
1080
                }
1081
                finally
1082
                {
1083
                    tileDrawingListLock.ReleaseReaderLock();
1084
                }
1085
            }
1086
            #endregion
1087
 
1088
            UpdateGroundResolution();
1089
#if UseGC
1090
            GC.Collect();
1091
            GC.WaitForPendingFinalizers();
1092
            GC.Collect();
1093
#endif
1094
            Debug.WriteLine(ctid + " - OnTileLoadComplete: " + lastTileLoadTimeMs + "ms, MemoryCacheSize: " + GMaps.Instance.MemoryCache.Size + "MB");
1095
 
1096
            if (OnTileLoadComplete != null)
1097
            {
1098
                OnTileLoadComplete(lastTileLoadTimeMs);
1099
            }
1100
        }
1101
 
1102
        public AutoResetEvent Refresh = new AutoResetEvent(false);
1103
 
1104
        public bool updatingBounds = false;
1105
 
1106
        /// <summary>
1107
        /// updates map bounds
1108
        /// </summary>
1109
        void UpdateBounds()
1110
        {
1111
            if (!IsStarted || Provider.Equals(EmptyProvider.Instance))
1112
            {
1113
                return;
1114
            }
1115
 
1116
            updatingBounds = true;
1117
 
1118
            tileDrawingListLock.AcquireWriterLock();
1119
            try
1120
            {
1121
                #region -- find tiles around --
1122
                tileDrawingList.Clear();
1123
 
1124
                for (long i = (int)Math.Floor(-sizeOfMapArea.Width * scaleX), countI = (int)Math.Ceiling(sizeOfMapArea.Width * scaleX); i <= countI; i++)
1125
                {
1126
                    for (long j = (int)Math.Floor(-sizeOfMapArea.Height * scaleY), countJ = (int)Math.Ceiling(sizeOfMapArea.Height * scaleY); j <= countJ; j++)
1127
                    {
1128
                        GPoint p = centerTileXYLocation;
1129
                        p.X += i;
1130
                        p.Y += j;
1131
 
1132
#if ContinuesMap
1133
               // ----------------------------
1134
               if(p.X < minOfTiles.Width)
1135
               {
1136
                  p.X += (maxOfTiles.Width + 1);
1137
               }
1138
 
1139
               if(p.X > maxOfTiles.Width)
1140
               {
1141
                  p.X -= (maxOfTiles.Width + 1);
1142
               }
1143
               // ----------------------------
1144
#endif
1145
 
1146
                        if (p.X >= minOfTiles.Width && p.Y >= minOfTiles.Height && p.X <= maxOfTiles.Width && p.Y <= maxOfTiles.Height)
1147
                        {
1148
                            DrawTile dt = new DrawTile()
1149
                            {
1150
                                PosXY = p,
1151
                                PosPixel = new GPoint(p.X * tileRect.Width, p.Y * tileRect.Height),
1152
                                DistanceSqr = (centerTileXYLocation.X - p.X) * (centerTileXYLocation.X - p.X) + (centerTileXYLocation.Y - p.Y) * (centerTileXYLocation.Y - p.Y)
1153
                            };
1154
 
1155
                            if (!tileDrawingList.Contains(dt))
1156
                            {
1157
                                tileDrawingList.Add(dt);
1158
                            }
1159
                        }
1160
                    }
1161
                }
1162
 
1163
                if (GMaps.Instance.ShuffleTilesOnLoad)
1164
                {
1165
                    Stuff.Shuffle<DrawTile>(tileDrawingList);
1166
                }
1167
                else
1168
                {
1169
                    tileDrawingList.Sort();
1170
                }
1171
                #endregion
1172
            }
1173
            finally
1174
            {
1175
                tileDrawingListLock.ReleaseWriterLock();
1176
            }
1177
 
1178
#if NET40
1179
            Interlocked.Exchange(ref loadWaitCount, 0);
1180
#else
1181
            Monitor.Enter(tileLoadQueue);
1182
            try
1183
            {
1184
#endif
1185
            tileDrawingListLock.AcquireReaderLock();
1186
            try
1187
            {
1188
                foreach (DrawTile p in tileDrawingList)
1189
                {
1190
                    LoadTask task = new LoadTask(p.PosXY, Zoom, this);
1191
#if NET40
1192
                    AddLoadTask(task);
1193
#else
1194
                        {
1195
                            if (!tileLoadQueue.Contains(task))
1196
                            {
1197
                                tileLoadQueue.Push(task);
1198
                            }
1199
                        }
1200
#endif
1201
                }
1202
            }
1203
            finally
1204
            {
1205
                tileDrawingListLock.ReleaseReaderLock();
1206
            }
1207
 
1208
#if !NET40
1209
            #region -- starts loader threads if needed --
1210
 
1211
                lock (GThreadPool)
1212
                {
1213
                    while (GThreadPool.Count < GThreadPoolSize)
1214
                    {
1215
                        Thread t = new Thread(new ThreadStart(tileLoadThread));
1216
                        {
1217
                            t.Name = "TileLoader: " + GThreadPool.Count;
1218
                            t.IsBackground = true;
1219
                            t.Priority = ThreadPriority.BelowNormal;
1220
                        }
1221
                        GThreadPool.Add(t);
1222
 
1223
                        Debug.WriteLine("add " + t.Name + " to GThreadPool");
1224
 
1225
                        t.Start();
1226
                    }
1227
                }
1228
            #endregion
1229
#endif
1230
            {
1231
                LastTileLoadStart = DateTime.Now;
1232
                Debug.WriteLine("OnTileLoadStart - at zoom " + Zoom + ", time: " + LastTileLoadStart.TimeOfDay);
1233
            }
1234
#if !NET40
1235
                loadWaitCount = 0;
1236
                Monitor.PulseAll(tileLoadQueue);
1237
            }
1238
            finally
1239
            {
1240
                Monitor.Exit(tileLoadQueue);
1241
            }
1242
#endif
1243
            updatingBounds = false;
1244
 
1245
            if (OnTileLoadStart != null)
1246
            {
1247
                OnTileLoadStart();
1248
            }
1249
        }
1250
 
1251
        /// <summary>
1252
        /// updates ground resolution info
1253
        /// </summary>
1254
        void UpdateGroundResolution()
1255
        {
1256
            double rez = Provider.Projection.GetGroundResolution(Zoom, Position.Lat);
1257
            pxRes100m = (int)(100.0 / rez); // 100 meters
1258
            pxRes1000m = (int)(1000.0 / rez); // 1km  
1259
            pxRes10km = (int)(10000.0 / rez); // 10km
1260
            pxRes100km = (int)(100000.0 / rez); // 100km
1261
            pxRes1000km = (int)(1000000.0 / rez); // 1000km
1262
            pxRes5000km = (int)(5000000.0 / rez); // 5000km
1263
        }
1264
 
1265
        #region IDisposable Members
1266
 
1267
        ~Core()
1268
        {
1269
            Dispose(false);
1270
        }
1271
 
1272
        void Dispose(bool disposing)
1273
        {
1274
            if (IsStarted)
1275
            {
1276
                if (invalidator != null)
1277
                {
1278
                    invalidator.CancelAsync();
1279
                    invalidator.DoWork -= new DoWorkEventHandler(invalidatorWatch);
1280
                    invalidator.Dispose();
1281
                    invalidator = null;
1282
                }
1283
 
1284
                if (Refresh != null)
1285
                {
1286
                    Refresh.Set();
1287
                    Refresh.Close();
1288
                    Refresh = null;
1289
                }
1290
 
1291
                int x = Interlocked.Decrement(ref instances);
1292
                Debug.WriteLine("OnMapClose: " + x);
1293
 
1294
                CancelAsyncTasks();
1295
                IsStarted = false;
1296
 
1297
                if (Matrix != null)
1298
                {
1299
                    Matrix.Dispose();
1300
                    Matrix = null;
1301
                }
1302
 
1303
                if (FailedLoads != null)
1304
                {
1305
                    lock (FailedLoads)
1306
                    {
1307
                        FailedLoads.Clear();
1308
                        RaiseEmptyTileError = false;
1309
                    }
1310
                    FailedLoads = null;
1311
                }
1312
 
1313
                tileDrawingListLock.AcquireWriterLock();
1314
                try
1315
                {
1316
                    tileDrawingList.Clear();
1317
                }
1318
                finally
1319
                {
1320
                    tileDrawingListLock.ReleaseWriterLock();
1321
                }
1322
 
1323
#if NET40
1324
                //TODO: maybe
1325
#else
1326
                // cancel waiting loaders
1327
                Monitor.Enter(tileLoadQueue);
1328
                try
1329
                {
1330
                    Monitor.PulseAll(tileLoadQueue);
1331
                }
1332
                finally
1333
                {
1334
                    Monitor.Exit(tileLoadQueue);
1335
                }
1336
 
1337
                lock (GThreadPool)
1338
                {
1339
#if PocketPC
1340
                Debug.WriteLine("waiting until loaders are stopped...");
1341
                while(GThreadPool.Count > 0)
1342
                {
1343
                    var t = GThreadPool[0];
1344
 
1345
                    if (t.State != ThreadState.Stopped)
1346
                    {
1347
                        var tr = t.Join(1111);
1348
 
1349
                        Debug.WriteLine(t.Name + ", " + t.State);
1350
 
1351
                        if (!tr)
1352
                        {
1353
                            continue;
1354
                        }
1355
                        else
1356
                        {
1357
                            GThreadPool.Remove(t);
1358
                        }
1359
                    }
1360
                    else
1361
                    {
1362
                        GThreadPool.Remove(t);
1363
                    }
1364
                }
1365
                Thread.Sleep(1111);
1366
#endif
1367
                }
1368
#endif
1369
 
1370
                if (tileDrawingListLock != null)
1371
                {
1372
                    tileDrawingListLock.Dispose();
1373
                    tileDrawingListLock = null;
1374
                    tileDrawingList = null;
1375
                }
1376
 
1377
                if (x == 0)
1378
                {
1379
#if DEBUG
1380
                    GMaps.Instance.CancelTileCaching();
1381
#endif
1382
                    GMaps.Instance.noMapInstances = true;
1383
                    GMaps.Instance.WaitForCache.Set();
1384
                    if (disposing)
1385
                    {
1386
                        GMaps.Instance.MemoryCache.Clear();
1387
                    }
1388
                }
1389
            }
1390
        }
1391
 
1392
        public void Dispose()
1393
        {
1394
            Dispose(true);
1395
            GC.SuppressFinalize(this);
1396
        }
1397
 
1398
        #endregion
1399
    }
1400
}