1 /***************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  AIS Decoder Object
5  * Author:   David Register
6  *
7  ***************************************************************************
8  *   Copyright (C) 2010 by David S. Register                               *
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  *   This program is distributed in the hope that it will be useful,       *
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
18  *   GNU General Public License for more details.                          *
19  *                                                                         *
20  *   You should have received a copy of the GNU General Public License     *
21  *   along with this program; if not, write to the                         *
22  *   Free Software Foundation, Inc.,                                       *
23  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
24  ***************************************************************************
25  */
26 
27 #ifdef __MINGW32__
28 #undef IPV6STRICT    // mingw FTBS fix:  missing struct ip_mreq
29 #include <windows.h>
30 #endif
31 
32 #include "wx/wx.h"
33 #include "wx/tokenzr.h"
34 #include "wx/datetime.h"
35 #include <wx/wfstream.h>
36 #include <wx/imaglist.h>
37 
38 #include <stdlib.h>
39 #include <math.h>
40 #include <time.h>
41 
42 #include "cutil.h"
43 #include "FontMgr.h"
44 #include "dychart.h"
45 #include "ais.h"
46 #include "chart1.h"
47 #include "navutil.h"        // for Select
48 #include "georef.h"
49 #include "styles.h"
50 #include "datastream.h"
51 #include "Select.h"
52 #include "AIS_Decoder.h"
53 #include "AIS_Target_Data.h"
54 #include "AISTargetAlertDialog.h"
55 #include "AISTargetQueryDialog.h"
56 #include "wx28compat.h"
57 #include "OCPNPlatform.h"
58 #include "chcanv.h"
59 
60 extern  double          gLat, gLon, gSog, gCog;
61 extern MyFrame          *gFrame;
62 extern OCPNPlatform     *g_Platform;
63 
64 int                      g_ais_cog_predictor_width;
65 extern AIS_Decoder              *g_pAIS;
66 extern AISTargetAlertDialog      *g_pais_alert_dialog_active;
67 extern AISTargetQueryDialog      *g_pais_query_dialog_active;
68 
69 //    AIS Global configuration
70 extern bool             g_bShowCOG;
71 extern double           g_ShowCOG_Mins;
72 extern bool             g_bHideMoored;
73 extern double           g_ShowMoored_Kts;
74 extern bool             g_bAISShowTracks;
75 extern bool             g_bShowAreaNotices;
76 extern bool             g_bDrawAISSize;
77 extern bool             g_bDrawAISRealtime;
78 extern double           g_AIS_RealtPred_Kts;
79 extern bool             g_bShowAISName;
80 extern int              g_Show_Target_Name_Scale;
81 extern bool             g_bInlandEcdis;
82 
83 
84 
85 extern int              g_ais_alert_dialog_x, g_ais_alert_dialog_y;
86 extern int              g_ais_alert_dialog_sx, g_ais_alert_dialog_sy;
87 
88 extern bool             g_bShowScaled;
89 
90 int                     g_ShowScaled_Num;
91 int                     ImportanceSwitchPoint = 100;
92 int                     g_ScaledNumWeightSOG;
93 int                     g_ScaledNumWeightCPA;
94 int                     g_ScaledNumWeightTCPA;
95 int                     g_ScaledNumWeightRange;
96 int                     g_ScaledNumWeightSizeOfT;
97 int                     g_ScaledSizeMinimal;
98 
99 
100 extern ArrayOfMMSIProperties   g_MMSI_Props_Array;
101 
102 extern float            g_ShipScaleFactorExp;
103 
104 
105 float AISImportanceSwitchPoint = 0.0;
106 
107 #if !defined(NAN)
108 static const long long lNaN = 0xfff8000000000000;
109 #define NAN (*(double*)&lNaN)
110 #endif
111 
112 #include <wx/listimpl.cpp>
113 WX_DEFINE_LIST(AISTargetTrackList);
114 
ais_get_status(int index)115 wxString ais_get_status(int index)
116 {
117     static const wxString ais_status[] = {
118         _("Underway"),
119         _("At Anchor"),
120         _("Not Under Command"),
121         _("Restricted Manoeuvrability"),
122         _("Constrained by draught"),
123         _("Moored"),
124         _("Aground"),
125         _("Engaged in Fishing"),
126         _("Under way sailing"),
127         _("High Speed Craft"),
128         _("Wing In Ground Effect"),
129         _("Power-driven vessel towing astern (regional use)"),
130         _("Power-driven vessel pushing ahead or towing alongside (regional use)"),
131         _("Reserved 13"),
132         _("Reserved 14"),
133         _("Undefined"),
134         _("AtoN Virtual"),
135         _("AtoN Virtual (On Position)"),
136         _("AtoN Virtual (Off Position)"),
137         _("AtoN Real"),
138         _("AtoN Real (On Position)"),
139         _("AtoN Real(Off Position)")
140     };
141 
142     return ais_status[index];
143 }
144 
ais_get_type(int index)145 wxString ais_get_type(int index)
146 {
147 	static const wxString ais_type[] = {
148         _("Vessel Fishing"),             //30        0
149         _("Vessel Towing"),              //31        1
150         _("Vessel Towing, Long"),        //32        2
151         _("Vessel Dredging"),            //33        3
152         _("Vessel Diving"),              //34        4
153         _("Military Vessel"),            //35        5
154         _("Sailing Vessel"),             //36        6
155         _("Pleasure craft"),             //37        7
156         _("High Speed Craft"),           //4x        8
157         _("Pilot Vessel"),               //50        9
158         _("Search and Rescue Vessel"),   //51        10
159         _("Tug"),                        //52        11
160         _("Port Tender"),                //53        12
161         _("Pollution Control Vessel"),   //54        13
162         _("Law Enforcement Vessel"),     //55        14
163         _("Medical Transport"),          //58        15
164         _("Passenger Ship"),             //6x        16
165         _("Cargo Ship"),                 //7x        17
166         _("Tanker"),                     //8x        18
167         _("Unknown"),                    //          19
168         _("Unspecified"),                //00        20
169         _("Reference Point"),            //01        21
170         _("RACON"),                      //02        22
171         _("Fixed Structure"),            //03        23
172         _("Spare"),                      //04        24
173         _("Light"),                      //05        25
174         _("Light w/Sectors"),            //06        26
175         _("Leading Light Front"),        //07        27
176         _("Leading Light Rear"),         //08        28
177         _("Cardinal N Beacon"),          //09        29
178         _("Cardinal E Beacon"),          //10        30
179         _("Cardinal S Beacon"),          //11        31
180         _("Cardinal W Beacon"),          //12        32
181         _("Beacon, Port Hand"),          //13        33
182         _("Beacon, Starboard Hand"),     //14        34
183         _("Beacon, Preferred Channel Port Hand"),         //15        35
184         _("Beacon, Preferred Channel Starboard Hand"),    //16        36
185         _("Beacon, Isolated Danger"),    //17        37
186         _("Beacon, Safe Water"),         //18        38
187         _("Beacon, Special Mark"),       //19        39
188         _("Cardinal Mark N"),            //20        40
189         _("Cardinal Mark E"),            //21        41
190         _("Cardinal Mark S"),            //22        42
191         _("Cardinal Mark W"),            //23        43
192         _("Port Hand Mark"),             //24        44
193         _("Starboard Hand Mark"),        //25        45
194         _("Preferred Channel Port Hand"),      //26        46
195         _("Preferred Channel Starboard Hand"), //27        47
196         _("Isolated Danger"),            //28        48
197         _("Safe Water"),                 //29        49
198         _("Special Mark"),               //30        50
199         _("Light Vessel/Rig"),           //31        51
200         _("GpsGate Buddy"),              //xx        52
201         _("Position Report"),            //xx        53
202         _("Distress"),                   //xx        54
203         _("ARPA radar target"),          //xx        55
204         _("APRS Position Report")        //xx        56
205 	};
206 
207 	return ais_type[index];
208 }
209 
ais_get_short_type(int index)210 wxString ais_get_short_type(int index)
211 {
212     static const wxString short_ais_type[] = {
213         _("F/V"),                  //30        0
214         _("Tow"),                  //31        1
215         _("Long Tow"),             //32        2
216         _("Dredge"),               //33        3
217         _("D/V"),                  //34        4
218         _("Mil/V"),                //35        5
219         _("S/V"),                  //36        6
220         _("Yat"),                  //37        7
221         _("HSC"),                  //4x        8
222         _("P/V"),                  //50        9
223         _("SAR/V"),                //51        10
224         _("Tug"),                  //52        11
225         _("Tender"),               //53        12
226         _("PC/V"),                 //54        13
227         _("LE/V"),                 //55        14
228         _("Med/V"),                //58        15
229         _("Pass/V"),               //6x        16
230         _("M/V"),                  //7x        17
231         _("M/T"),                  //8x        18
232         _("?"),                    //          19
233 
234         _("AtoN"),                 //00        20
235         _("Ref. Pt"),              //01        21
236         _("RACON"),                //02        22
237         _("Fix.Struct."),          //03        23
238         _("?"),                    //04        24
239         _("Lt"),                   //05        25
240         _("Lt sect."),             //06        26
241         _("Ldg Lt Front"),         //07        27
242         _("Ldg Lt Rear"),          //08        28
243         _("Card. N"),              //09        29
244         _("Card. E"),              //10        30
245         _("Card. S"),              //11        31
246         _("Card. W"),              //12        32
247         _("Port"),                 //13        33
248         _("Stbd"),                 //14        34
249         _("Pref. Chnl"),           //15        35
250         _("Pref. Chnl"),           //16        36
251         _("Isol. Dngr"),           //17        37
252         _("Safe Water"),           //18        38
253         _("Special"),              //19        39
254         _("Card. N"),              //20        40
255         _("Card. E"),              //21        41
256         _("Card. S"),              //22        42
257         _("Card. W"),              //23        43
258         _("Port Hand"),            //24        44
259         _("Stbd Hand"),            //25        45
260         _("Pref. Chnl"),           //26        46
261         _("Pref. Chnl"),           //27        47
262         _("Isol. Dngr"),           //28        48
263         _("Safe Water"),           //29        49
264         _("Special"),              //30        50
265         _("LtV/Rig"),              //31        51
266         _("Buddy"),                //xx        52
267         _("DSC"),                  //xx        53
268         _("Distress"),             //xx        54
269         _("ARPA"),                 //xx        55
270         _("APRS")                  //xx        56
271 	};
272 
273 	return short_ais_type[index];
274 }
275 
276 wxString ais8_001_22_notice_names[] = { // 128] = {
277     _("Caution Area: Marine mammals habitat (implies whales NOT observed)"), // 0 - WARNING: extra text by Kurt
278     _("Caution Area: Marine mammals in area - reduce speed"), // 1
279     _("Caution Area: Marine mammals in area - stay clear"), // 2
280     _("Caution Area: Marine mammals in area - report sightings"), // 3
281     _("Caution Area: Protected habitat - reduce speed"), // 4
282     _("Caution Area: Protected habitat - stay clear"), // 5
283     _("Caution Area: Protected habitat - no fishing or anchoring"), // 6
284     _("Caution Area: Derelicts (drifting objects)"), // 7
285     _("Caution Area: Traffic congestion"), // 8
286     _("Caution Area: Marine event"), // 9
287     _("Caution Area: Divers down"), // 10
288     _("Caution Area: Swim area"), // 11
289     _("Caution Area: Dredge operations"), // 12
290     _("Caution Area: Survey operations"), // 13
291     _("Caution Area: Underwater operation"), // 14
292     _("Caution Area: Seaplane operations"), // 15
293     _("Caution Area: Fishery - nets in water"), // 16
294     _("Caution Area: Cluster of fishing vessels"), // 17
295     _("Caution Area: Fairway closed"), // 18
296     _("Caution Area: Harbour closed"), // 19
297     _("Caution Area: Risk (define in Associated text field)"), // 20
298     _("Caution Area: Underwater vehicle operation"), // 21
299     _("(reserved for future use)"), // 22
300     _("Environmental Caution Area: Storm front (line squall)"), // 23
301     _("Environmental Caution Area: Hazardous sea ice"), // 24
302     _("Environmental Caution Area: Storm warning (storm cell or line of storms)"), // 25
303     _("Environmental Caution Area: High wind"), // 26
304     _("Environmental Caution Area: High waves"), // 27
305     _("Environmental Caution Area: Restricted visibility (fog, rain, etc.)"), // 28
306     _("Environmental Caution Area: Strong currents"), // 29
307     _("Environmental Caution Area: Heavy icing"), // 30
308     _("(reserved for future use)"), // 31
309     _("Restricted Area: Fishing prohibited"), // 32
310     _("Restricted Area: No anchoring."), // 33
311     _("Restricted Area: Entry approval required prior to transit"), // 34
312     _("Restricted Area: Entry prohibited"), // 35
313     _("Restricted Area: Active military OPAREA"), // 36
314     _("Restricted Area: Firing - danger area."), // 37
315     _("Restricted Area: Drifting Mines"), // 38
316     _("(reserved for future use)"), // 39
317     _("Anchorage Area: Anchorage open"), // 40
318     _("Anchorage Area: Anchorage closed"), // 41
319     _("Anchorage Area: Anchoring prohibited"), // 42
320     _("Anchorage Area: Deep draft anchorage"), // 43
321     _("Anchorage Area: Shallow draft anchorage"), // 44
322     _("Anchorage Area: Vessel transfer operations"), // 45
323     _("(reserved for future use)"), // 46
324     _("(reserved for future use)"), // 47
325     _("(reserved for future use)"), // 48
326     _("(reserved for future use)"), // 49
327     _("(reserved for future use)"), // 50
328     _("(reserved for future use)"), // 51
329     _("(reserved for future use)"), // 52
330     _("(reserved for future use)"), // 53
331     _("(reserved for future use)"), // 54
332     _("(reserved for future use)"), // 55
333     _("Security Alert - Level 1"), // 56
334     _("Security Alert - Level 2"), // 57
335     _("Security Alert - Level 3"), // 58
336     _("(reserved for future use)"), // 59
337     _("(reserved for future use)"), // 60
338     _("(reserved for future use)"), // 61
339     _("(reserved for future use)"), // 62
340     _("(reserved for future use)"), // 63
341     _("Distress Area: Vessel disabled and adrift"), // 64
342     _("Distress Area: Vessel sinking"), // 65
343     _("Distress Area: Vessel abandoning ship"), // 66
344     _("Distress Area: Vessel requests medical assistance"), // 67
345     _("Distress Area: Vessel flooding"), // 68
346     _("Distress Area: Vessel fire/explosion"), // 69
347     _("Distress Area: Vessel grounding"), // 70
348     _("Distress Area: Vessel collision"), // 71
349     _("Distress Area: Vessel listing/capsizing"), // 72
350     _("Distress Area: Vessel under assault"), // 73
351     _("Distress Area: Person overboard"), // 74
352     _("Distress Area: SAR area"), // 75
353     _("Distress Area: Pollution response area"), // 76
354     _("(reserved for future use)"), // 77
355     _("(reserved for future use)"), // 78
356     _("(reserved for future use)"), // 79
357     _("Instruction: Contact VTS at this point/juncture"), // 80
358     _("Instruction: Contact Port Administration at this point/juncture"), // 81
359     _("Instruction: Do not proceed beyond this point/juncture"), // 82
360     _("Instruction: Await instructions prior to proceeding beyond this point/juncture"), // 83
361     _("Proceed to this location - await instructions"), // 84
362     _("Clearance granted - proceed to berth"), // 85
363     _("(reserved for future use)"), // 86
364     _("(reserved for future use)"), // 87
365     _("Information: Pilot boarding position"), // 88
366     _("Information: Icebreaker waiting area"), // 89
367     _("Information: Places of refuge"), // 90
368     _("Information: Position of icebreakers"), // 91
369     _("Information: Location of response units"), // 92
370     _("VTS active target"), // 93
371     _("Rogue or suspicious vessel"), // 94
372     _("Vessel requesting non-distress assistance"), // 95
373     _("Chart Feature: Sunken vessel"), // 96
374     _("Chart Feature: Submerged object"), // 97
375     _("Chart Feature: Semi-submerged object"), // 98
376     _("Chart Feature: Shoal area"), // 99
377     _("Chart Feature: Shoal area due north"), // 100
378     _("Chart Feature: Shoal area due east"), // 101
379     _("Chart Feature: Shoal area due south"), // 102
380     _("Chart Feature: Shoal area due west"), // 103
381     _("Chart Feature: Channel obstruction"), // 104
382     _("Chart Feature: Reduced vertical clearance"), // 105
383     _("Chart Feature: Bridge closed"), // 106
384     _("Chart Feature: Bridge partially open"), // 107
385     _("Chart Feature: Bridge fully open"), // 108
386     _("(reserved for future use)"), // 109
387     _("(reserved for future use)"), // 110
388     _("(reserved for future use)"), // 111
389     _("Report from ship: Icing info"), // 112
390     _("(reserved for future use)"), // 113
391     _("Report from ship: Miscellaneous information - define in Associated text field"), // 114
392     _("(reserved for future use)"), // 115
393     _("(reserved for future use)"), // 116
394     _("(reserved for future use)"), // 117
395     _("(reserved for future use)"), // 118
396     _("(reserved for future use)"), // 119
397     _("Route: Recommended route"), // 120
398     _("Route: Alternative route"), // 121
399     _("Route: Recommended route through ice"), // 122
400     _("(reserved for future use)"), // 123
401     _("(reserved for future use)"), // 124
402     _("Other - Define in associated text field"), // 125
403     _("Cancellation - cancel area as identified by Message Linkage ID"), // 126
404     _("Undefined (default)") //, // 127
405 };
406 
GetCanvasPointPix(ViewPort & vp,ChartCanvas * cp,double rlat,double rlon,wxPoint * r)407 static bool GetCanvasPointPix( ViewPort& vp, ChartCanvas *cp, double rlat, double rlon, wxPoint *r )
408 {
409      if (cp != NULL)
410      {
411           return cp->GetCanvasPointPix(rlat, rlon, r);
412      }
413      *r = vp.GetPixFromLL(rlat, rlon);
414      return true;
415 }
416 
417 
trimAISField(char * data)418 wxString trimAISField( char *data )
419 {
420     //  Clip any unused characters (@) from data
421 
422     wxString field = wxString::From8BitData( data );
423     while( field.Right( 1 ) == '@' || field.Right( 1 ) == ' ' )
424         field.RemoveLast();
425 
426     //  And remove any leading spaces to properly sort and display
427     field.Trim(false);
428 
429     return field;
430 }
431 
transrot(wxPoint pt,float sin_theta,float cos_theta,wxPoint offset=wxPoint (0,0))432 static wxPoint transrot( wxPoint pt, float sin_theta, float cos_theta, wxPoint offset=wxPoint(0,0) )
433 {
434     wxPoint ret;
435     float px = (float) ( pt.x * sin_theta ) + (float) ( pt.y * cos_theta );
436     float py = (float) ( pt.y * sin_theta ) - (float) ( pt.x * cos_theta );
437     ret.x = (int) wxRound( px );
438     ret.y = (int) wxRound( py );
439     ret.x += offset.x;
440     ret.y += offset.y;
441 
442     return ret;
443 }
444 
transrot_pts(int n,wxPoint * pt,float sin_theta,float cos_theta,wxPoint offset=wxPoint (0,0))445 static void transrot_pts( int n, wxPoint *pt, float sin_theta, float cos_theta, wxPoint offset=wxPoint(0,0) )
446 {
447     for(int i=0; i<n; i++)
448         pt[i] = transrot(pt[i], sin_theta, cos_theta, offset);
449 }
450 
AISDrawAreaNotices(ocpnDC & dc,ViewPort & vp,ChartCanvas * cp)451 void AISDrawAreaNotices( ocpnDC& dc, ViewPort& vp, ChartCanvas *cp )
452 {
453     if (cp == NULL) return;
454     if( !g_pAIS || !cp->GetShowAIS() || !g_bShowAreaNotices )
455         return;
456 
457     wxDateTime now = wxDateTime::Now();
458     now.MakeGMT();
459 
460     bool b_pens_set = false;
461     wxPen pen_save;
462     wxBrush brush_save;
463     wxColour yellow;
464     wxColour green;
465     wxPen pen;
466     wxBrush *yellow_brush = wxTheBrushList->FindOrCreateBrush( wxColour(0,0,0), wxBRUSHSTYLE_TRANSPARENT );
467     wxBrush *green_brush  = wxTheBrushList->FindOrCreateBrush( wxColour(0,0,0), wxBRUSHSTYLE_TRANSPARENT );;
468     wxBrush *brush;
469 
470     AIS_Target_Hash *current_targets = g_pAIS->GetAreaNoticeSourcesList();
471 
472     float vp_scale = vp.view_scale_ppm;
473 
474     for( AIS_Target_Hash::iterator target = current_targets->begin();
475             target != current_targets->end(); ++target ) {
476         AIS_Target_Data *target_data = target->second;
477         if( !target_data->area_notices.empty() ) {
478             if( !b_pens_set ) {
479                 pen_save = dc.GetPen();
480                 brush_save = dc.GetBrush();
481 
482                 yellow = GetGlobalColor( _T ( "YELO1" ) );
483                 yellow.Set( yellow.Red(), yellow.Green(), yellow.Blue(), 64 );
484 
485                 green = GetGlobalColor( _T ( "GREEN4" ) );
486                 green.Set( green.Red(), green.Green(), green.Blue(), 64 );
487 
488                 pen.SetColour( yellow );
489                 pen.SetWidth( 2 );
490 
491                 yellow_brush = wxTheBrushList->FindOrCreateBrush( yellow, wxBRUSHSTYLE_CROSSDIAG_HATCH );
492                 green_brush = wxTheBrushList->FindOrCreateBrush( green, wxBRUSHSTYLE_TRANSPARENT );
493                 brush = yellow_brush;
494 
495                 b_pens_set = true;
496             }
497 
498             for( AIS_Area_Notice_Hash::iterator ani = target_data->area_notices.begin();
499                     ani != target_data->area_notices.end(); ++ani ) {
500                 Ais8_001_22& area_notice = ani->second;
501 
502                 if( area_notice.expiry_time > now ) {
503                     std::vector<wxPoint> points;
504                     bool draw_polygon = false;
505 
506                     switch( area_notice.notice_type ) {
507                     case 0:
508                         pen.SetColour( green );
509                         brush = green_brush;
510                         break;
511                     case 1:
512                         pen.SetColour( yellow );
513                         brush = yellow_brush;
514                         break;
515                     default:
516                         pen.SetColour( yellow );
517                         brush = yellow_brush;
518                     }
519                     dc.SetPen( pen );
520                     dc.SetBrush( *brush );
521 
522                     for( Ais8_001_22_SubAreaList::iterator sa = area_notice.sub_areas.begin();
523                             sa != area_notice.sub_areas.end(); ++sa ) {
524                         switch( sa->shape ) {
525                         case AIS8_001_22_SHAPE_CIRCLE: {
526                             wxPoint target_point;
527                             GetCanvasPointPix(vp, cp, sa->latitude, sa->longitude, &target_point );
528                             points.push_back( target_point );
529                             if( sa->radius_m > 0.0 )
530                                 dc.DrawCircle( target_point, sa->radius_m * vp_scale );
531                             break;
532                         }
533                         case AIS8_001_22_SHAPE_POLYGON:
534                             draw_polygon = true;
535                             // FALL THROUGH
536                         case AIS8_001_22_SHAPE_POLYLINE: {
537                             double lat = sa->latitude;
538                             double lon = sa->longitude;
539                             for( int i = 0; i < 4; ++i ) {
540                                 ll_gc_ll( lat, lon, sa->angles[i], sa->dists_m[i] / 1852.0,
541                                           &lat, &lon );
542                                 wxPoint target_point;
543                                 GetCanvasPointPix(vp, cp, lat, lon, &target_point );
544                                 points.push_back( target_point );
545                             }
546                         }
547                         }
548                     }
549                     if( draw_polygon )
550                         dc.DrawPolygon( points.size(), &points.front() );
551                 }
552             }
553         }
554     }
555 
556     if( b_pens_set ) {
557         dc.SetPen( pen_save );
558         dc.SetBrush( brush_save );
559     }
560 
561 }
562 
TargetFrame(ocpnDC & dc,wxPen pen,int x,int y,int radius)563 static void TargetFrame( ocpnDC &dc, wxPen pen, int x, int y, int radius )
564 {
565     //    Constants?
566     int gap2 = 2 * radius / 6;
567 
568     wxPen pen_save = dc.GetPen();
569 
570     dc.SetPen( pen );
571 
572     dc.DrawLine( x - radius, y + gap2, x - radius, y + radius );
573     dc.DrawLine( x - radius, y + radius, x - gap2, y + radius );
574     dc.DrawLine( x + gap2, y + radius, x + radius, y + radius );
575     dc.DrawLine( x + radius, y + radius, x + radius, y + gap2 );
576     dc.DrawLine( x + radius, y - gap2, x + radius, y - radius );
577     dc.DrawLine( x + radius, y - radius, x + gap2, y - radius );
578     dc.DrawLine( x - gap2, y - radius, x - radius, y - radius );
579     dc.DrawLine( x - radius, y - radius, x - radius, y - gap2 );
580 
581     dc.SetPen( pen_save );
582 }
583 
AtoN_Diamond(ocpnDC & dc,int x,int y,int radius,AIS_Target_Data * td)584 static void AtoN_Diamond( ocpnDC &dc, int x, int y, int radius, AIS_Target_Data* td )
585 {
586 	//    Constants?
587     wxPen pen_save = dc.GetPen();
588 
589     wxPen aton_DrawPen;
590     wxPen aton_WhiteBorderPen;
591     wxBrush aton_Brush;
592 
593     int rad1a = radius / 2; //size off topmarks of AtoN
594     int rad2a = radius / 4;
595     int rad3a = rad1a - 1;// slightly smaller size off topmarks to look better for the eye
596 
597     //Set the Pen for what is needed
598     if( ( td->NavStatus == ATON_VIRTUAL_OFFPOSITION ) || ( td->NavStatus == ATON_REAL_OFFPOSITION ) )
599         aton_DrawPen = wxPen( GetGlobalColor( _T ( "URED" ) ), 2 );
600     else
601         aton_DrawPen = wxPen( GetGlobalColor( _T ( "UBLCK" ) ), 2 );
602 
603     bool b_virt = ( td->NavStatus == ATON_VIRTUAL )
604           | ( td->NavStatus == ATON_VIRTUAL_ONPOSITION )
605           | ( td->NavStatus == ATON_VIRTUAL_OFFPOSITION );
606 
607     if( b_virt )
608         aton_DrawPen.SetStyle(wxPENSTYLE_SHORT_DASH );
609 
610     aton_WhiteBorderPen = wxPen(GetGlobalColor( _T ( "CHWHT" ) ), aton_DrawPen.GetWidth()+2 );
611 
612 
613     //Draw Base Diamond. First with Thick White pen then custom pen io to get a white border around the line.
614     wxPoint diamond[5];
615                 diamond[0] = wxPoint(  radius, 0 );
616                 diamond[1] = wxPoint(  0, -radius );
617                 diamond[2] = wxPoint( -radius, 0 );
618                 diamond[3] = wxPoint(  0, radius );
619                 diamond[4] = wxPoint(  radius, 0 );
620             dc.SetPen( aton_WhiteBorderPen );
621             dc.DrawLines( 5, diamond, x, y );
622             dc.SetPen( aton_DrawPen );
623             dc.DrawLines( 5, diamond, x, y );
624 
625     aton_DrawPen = wxPen( GetGlobalColor( _T ( "UBLCK" ) ), 1 ); // Change drawing pen to Solid and width 1
626     aton_WhiteBorderPen = wxPen(GetGlobalColor( _T ( "CHWHT" ) ), aton_DrawPen.GetWidth()+2 );
627 
628     // draw cross inside
629     wxPoint cross[5];
630                 cross[0] = wxPoint(  -rad2a, 0 );
631                 cross[1] = wxPoint(  rad2a, 0 );
632                 cross[2] = wxPoint( 0, 0 );
633                 cross[3] = wxPoint(  0, rad2a );
634                 cross[4] = wxPoint(  0, -rad2a );
635             dc.SetPen( aton_WhiteBorderPen );
636             dc.DrawLines( 5, cross, x, y );
637             dc.SetPen( aton_DrawPen );
638             dc.DrawLines( 5, cross, x, y );
639 
640     wxPoint TriPointUp[4]; //Declare triangles here for multiple use
641                 TriPointUp[0] = wxPoint(  -rad1a, 0 );
642                 TriPointUp[1] = wxPoint(  rad1a, 0 );
643                 TriPointUp[2] = wxPoint(  0, -rad1a );
644                 TriPointUp[3] = wxPoint(  -rad1a, 0 );
645 
646     wxPoint TriPointDown[4]; //Declare triangles here for multiple use
647     TriPointDown[0] = wxPoint(  -rad1a, -rad1a );
648     TriPointDown[1] = wxPoint(  rad1a, -rad1a );
649                 TriPointDown[2] = wxPoint(  0, 0 );
650                 TriPointDown[3] = wxPoint(  -rad1a, -rad1a );
651 
652     wxPoint CircleOpen[16]; // Workaround to draw transparent circles
653                 CircleOpen[0] = wxPoint(  -1, 5 );
654                 CircleOpen[1] = wxPoint(  1, 5 );
655                 CircleOpen[2] = wxPoint(  3, 4 );
656                 CircleOpen[3] = wxPoint(  4, 3);
657                 CircleOpen[4] = wxPoint(  5, 1 );
658                 CircleOpen[5] = wxPoint(  5,-1  );
659                 CircleOpen[6] = wxPoint(  4,-3  );
660                 CircleOpen[7] = wxPoint(  3,-4  );
661                 CircleOpen[8] = wxPoint(  1,-5  );
662                 CircleOpen[9] = wxPoint( -1,-5 );
663                 CircleOpen[10] = wxPoint( -3,-4 );
664                 CircleOpen[11] = wxPoint( -4,-3 );
665                 CircleOpen[12] = wxPoint( -5,-1 );
666                 CircleOpen[13] = wxPoint( -4,3 );
667                 CircleOpen[14] = wxPoint( -3,4 );
668                 CircleOpen[15] = wxPoint( -1,5 );
669 
670     switch (td->ShipType ) {
671         case 9 :
672         case 20://Card. N
673             dc.SetPen( aton_WhiteBorderPen );
674             dc.DrawLines( 4, TriPointUp, x, y - radius -1);
675             dc.DrawLines( 4, TriPointUp, x, y - radius -rad1a-3);
676             dc.SetPen( aton_DrawPen );
677             dc.DrawLines( 4, TriPointUp, x, y - radius -1);
678             dc.DrawLines( 4, TriPointUp, x, y - radius -rad1a-3);
679             break;
680         case 10:
681         case 21: //Card E
682             dc.SetPen( aton_WhiteBorderPen );
683             dc.DrawLines( 4, TriPointDown, x, y - radius -1);
684             dc.DrawLines( 4, TriPointUp, x, y - radius -rad1a-3);
685             dc.SetPen( aton_DrawPen );
686             dc.DrawLines( 4, TriPointDown, x, y - radius -1);
687             dc.DrawLines( 4, TriPointUp, x, y - radius -rad1a-3);
688             break;
689         case 11:
690         case 22: //Card S
691             dc.SetPen( aton_WhiteBorderPen );
692             dc.DrawLines( 4, TriPointDown, x, y - radius -1);
693             dc.DrawLines( 4, TriPointDown, x, y - radius -rad1a-3);
694             dc.SetPen( aton_DrawPen );
695             dc.DrawLines( 4, TriPointDown, x, y - radius -1);
696             dc.DrawLines( 4, TriPointDown, x, y - radius -rad1a-3);
697             break;
698         case 12:
699         case 23: //Card W
700             dc.SetPen( aton_WhiteBorderPen );
701             dc.DrawLines( 4, TriPointUp, x, y - radius -1 );
702             dc.DrawLines( 4, TriPointDown, x, y - radius -rad1a-3);
703             dc.SetPen( aton_DrawPen );
704             dc.DrawLines( 4, TriPointUp, x, y - radius -1);
705             dc.DrawLines( 4, TriPointDown, x, y - radius -rad1a-3);
706             break;
707         case 13: //PortHand Beacon IALA-A
708         case 24: { //StarboardHand Beacon IALA-B
709            wxPoint aRect[5]; //Square topmark
710                 aRect[0] = wxPoint(  -rad3a, 0 );
711                 aRect[1] = wxPoint(  -rad3a, -rad3a-rad3a );
712                 aRect[2] = wxPoint(  rad3a, -rad3a-rad3a );
713                 aRect[3] = wxPoint(  rad3a, 0 );
714                 aRect[4] = wxPoint(  -rad3a, 0 );
715             dc.SetPen( aton_WhiteBorderPen );
716             dc.DrawLines( 5, aRect, x, y - radius-1 );
717             dc.SetPen( aton_DrawPen );
718             dc.DrawLines( 5, aRect , x, y - radius-1 );
719             }
720             break;
721         case 14: //StarboardHand Beacon IALA-A
722         case 25: //PortHand Beacon IALA-B
723             dc.SetPen( aton_WhiteBorderPen );
724             dc.DrawLines( 4, TriPointUp, x, y - radius );
725             dc.SetPen( aton_DrawPen );
726             dc.DrawLines( 4, TriPointUp, x, y - radius);
727             break;
728         case 17:
729         case 28: //Isolated danger
730             dc.SetPen( aton_WhiteBorderPen );
731             dc.DrawLines( 16, CircleOpen, x, y - radius -5);
732             dc.SetPen( aton_DrawPen );
733             dc.DrawLines( 16, CircleOpen, x, y - radius -5);
734             dc.SetPen( aton_WhiteBorderPen );
735             dc.DrawLines( 16, CircleOpen, x, y - radius -16);
736             dc.SetPen( aton_DrawPen );
737             dc.DrawLines( 16, CircleOpen, x, y - radius -16);
738             break;
739         case 18:
740         case 29: //Safe water
741             dc.SetPen( aton_WhiteBorderPen );
742             dc.DrawLines( 16, CircleOpen, x, y - radius -5);
743             dc.SetPen( aton_DrawPen );
744             dc.DrawLines( 16, CircleOpen, x, y - radius -5);
745             break;
746         case 19:
747         case 30:{ //Special Mark
748             cross[0] = wxPoint( -rad2a, -rad2a ); //reuse of cross array
749             cross[1] = wxPoint( rad2a, rad2a);
750             cross[2] = wxPoint( 0, 0 );
751             cross[3] = wxPoint( -rad2a, rad2a );
752             cross[4] = wxPoint( rad2a, -rad2a );
753             dc.SetPen( aton_WhiteBorderPen );
754             dc.DrawLines( 5, cross, x, y - radius-rad3a);
755             dc.SetPen( aton_DrawPen );
756             dc.DrawLines( 5, cross, x, y - radius-rad3a);
757             }
758             break;
759         default:
760         break;
761     }
762     dc.SetPen( pen_save );
763 }
764 
765 
Base_Square(ocpnDC & dc,wxPen pen,int x,int y,int radius)766 static void Base_Square( ocpnDC &dc, wxPen pen, int x, int y, int radius )
767 {
768     //    Constants?
769     int gap2 = 2 * radius / 6;
770     int pen_width = pen.GetWidth();
771 
772     wxPen pen_save = dc.GetPen();
773 
774     dc.SetPen( pen );   // draw square
775 
776     dc.DrawLine( x - radius, y - radius, x - radius, y + radius );
777     dc.DrawLine( x - radius, y + radius, x + radius, y + radius );
778     dc.DrawLine( x + radius, y + radius, x + radius, y - radius );
779     dc.DrawLine( x + radius, y - radius, x - radius, y - radius );
780 
781     if( pen_width > 1 ) {
782         pen_width -= 1;
783         pen.SetWidth( pen_width );
784     }    // draw cross inside
785 
786     dc.DrawLine( x - gap2, y, x + gap2, y );
787     dc.DrawLine( x, y - gap2, x, y + gap2 );
788 
789     dc.SetPen( pen_save );
790 }
791 
SART_Render(ocpnDC & dc,wxPen pen,int x,int y,int radius)792 static void SART_Render( ocpnDC &dc, wxPen pen, int x, int y, int radius )
793 {
794     //    Constants
795     int gap = ( radius * 12 ) / 10;
796     int pen_width = pen.GetWidth();
797 
798     wxPen pen_save = dc.GetPen();
799 
800     dc.SetPen( pen );
801 
802     wxBrush brush_save = dc.GetBrush();
803     wxBrush *ppBrush = wxTheBrushList->FindOrCreateBrush( wxColour( 0, 0, 0 ), wxBRUSHSTYLE_TRANSPARENT );
804     dc.SetBrush( *ppBrush );
805 
806     dc.DrawCircle( x, y, radius );
807 
808     if( pen_width > 1 ) {
809         pen_width -= 1;
810         pen.SetWidth( pen_width );
811     }    // draw cross inside
812 
813     dc.DrawLine( x - gap, y - gap, x + gap, y + gap );
814     dc.DrawLine( x - gap, y + gap, x + gap, y - gap );
815 
816     dc.SetBrush( brush_save );
817     dc.SetPen( pen_save );
818 }
819 
820 // spherical coordinates is sufficient for visually plotting with relatively small
821 // distances and about 6x faster than ll_gc_ll
spherical_ll_gc_ll(float lat,float lon,float brg,float dist,float * dlat,float * dlon)822 static void spherical_ll_gc_ll(float lat, float lon, float brg, float dist, float *dlat, float *dlon)
823 {
824     float angr = brg/180*M_PI;
825     float latr = lat*M_PI/180;
826     float D = dist/3443; // earth radius in nm
827     float sD = sinf(D), cD = cosf(D);
828     float sy = sinf(latr), cy = cosf(latr);
829     float sa = sinf(angr), ca = cosf(angr);
830 
831     *dlon = lon + asinf(sa*sD/cy) * 180/M_PI;
832     *dlat = asinf(sy*cD + cy*sD*ca) * 180/M_PI;
833 }
834 
835 //  Global static AIS target rendering metrics
836    float AIS_scale_factor;
837    float AIS_nominal_target_size_mm;
838    float AIS_nominal_icon_size_pixels;
839    float AIS_pix_factor;
840    float AIS_user_scale_factor;
841    double AIS_nominal_line_width_pix;
842 
843    float AIS_width_interceptbar_base;
844    float AIS_width_interceptbar_top;
845    float AIS_intercept_bar_circle_diameter;
846    float AIS_width_interceptline;
847    float AIS_width_cogpredictor_base;
848    float AIS_width_cogpredictor_line;
849    float AIS_width_target_outline;
850 
851 
AISSetMetrics()852 static void AISSetMetrics()
853 {
854     AIS_scale_factor = 1.0;
855 
856     //  Set the onscreen size of the symbol
857     //  Compensate for various display resolutions
858     //  Develop empirically, making a "diamond ATON" symbol about 4 mm square
859 
860     // By experience, it is found that specifying target size in pixels, then bounding rendered size
861     // for high or lo resolution displays, gives the best compromise.
862 
863     AIS_nominal_target_size_mm = 30.0 / g_Platform->GetDisplayDPmm();
864     // nominal_target_size_mm = gFrame->GetPrimaryCanvas()->GetDisplaySizeMM() / 60.0;
865 
866     AIS_nominal_target_size_mm = wxMin(AIS_nominal_target_size_mm, 10.0);
867     AIS_nominal_target_size_mm = wxMax(AIS_nominal_target_size_mm, 6.0);
868 
869     AIS_nominal_icon_size_pixels = wxMax(4.0, floor(g_Platform->GetDisplayDPmm() * AIS_nominal_target_size_mm));             // nominal size, but not less than 4 pixel
870     AIS_pix_factor = AIS_nominal_icon_size_pixels / 30.0;          // generic A/B icons are 30 units in size
871 
872     AIS_scale_factor *= AIS_pix_factor;
873 
874     AIS_user_scale_factor = g_ShipScaleFactorExp;
875     if( g_ShipScaleFactorExp > 1.0 )
876         AIS_user_scale_factor = (log(g_ShipScaleFactorExp) + 1.0) * 1.2;   // soften the scale factor a bit
877 
878     AIS_scale_factor *= AIS_user_scale_factor;
879 
880     //  Establish some graphic element line widths dependent on the platform display resolution
881     AIS_nominal_line_width_pix = wxMax(1.5, floor(g_Platform->GetDisplayDPmm() / 5.0));             //0.4 mm nominal, but not less than 1 pixel
882 
883     AIS_width_interceptbar_base = 3 * AIS_nominal_line_width_pix;
884     AIS_width_interceptbar_top = 1.5 * AIS_nominal_line_width_pix;
885     AIS_intercept_bar_circle_diameter = 4 * AIS_nominal_line_width_pix;
886     AIS_width_interceptline = 2 * AIS_nominal_line_width_pix;
887     AIS_width_cogpredictor_base = 3 * AIS_nominal_line_width_pix;
888     AIS_width_cogpredictor_line = 1.5 * AIS_nominal_line_width_pix;
889     AIS_width_target_outline = 1.2 * AIS_nominal_line_width_pix;
890 }
891 
AISDrawTarget(AIS_Target_Data * td,ocpnDC & dc,ViewPort & vp,ChartCanvas * cp)892 static void AISDrawTarget( AIS_Target_Data *td, ocpnDC& dc, ViewPort& vp, ChartCanvas *cp )
893 {
894     //      Target data must be valid
895     if( NULL == td ) return;
896 
897     //    Target is lost due to position report time-out, but still in Target List
898     if( td->b_lost ) return;
899 
900     //      Skip anchored/moored (interpreted as low speed) targets if requested
901     //      unless the target is NUC or AtoN, in which case it is always displayed.
902     if( ( g_bHideMoored ) && ( td->SOG <= g_ShowMoored_Kts )
903             && ( td->NavStatus != NOT_UNDER_COMMAND )
904             && ( ( td->Class == AIS_CLASS_A ) || ( td->Class == AIS_CLASS_B ) ) ) return;
905 
906     //      Target data position must have been valid once
907     if( !td->b_positionOnceValid ) return;
908 
909     // And we never draw ownship
910     if( td->b_OwnShip ) return;
911 
912     //    If target's speed is unavailable, use zero for further calculations
913     float target_sog = td->SOG;
914     if( (td->SOG > 102.2) && !td->b_SarAircraftPosnReport )
915         target_sog = 0.;
916 
917     int drawit = 0;
918     wxPoint TargetPoint, PredPoint;
919 
920     //   Always draw alert targets, even if they are off the screen
921     if( td->n_alert_state == AIS_ALERT_SET ) drawit++;
922     else
923     //    Is target in Vpoint?
924     if( vp.GetBBox().Contains( td->Lat,  td->Lon ))
925         drawit++;                       // yep
926     else
927     //  If AIS tracks are shown, is the first point of the track on-screen?
928     if( 1/*g_bAISShowTracks*/ && td->b_show_track ) {
929         wxAISTargetTrackListNode *node = td->m_ptrack->GetFirst();
930         if( node ) {
931             AISTargetTrackPoint *ptrack_point = node->GetData();
932             if( vp.GetBBox().Contains( ptrack_point->m_lat,  ptrack_point->m_lon ) )
933                 drawit++;
934         }
935     }
936 
937     //    Calculate AIS target Position Predictor, using global static variable for length of vector
938 
939     float pred_lat, pred_lon;
940     spherical_ll_gc_ll( td->Lat, td->Lon, td->COG, target_sog * g_ShowCOG_Mins / 60., &pred_lat, &pred_lon );
941 
942     //    Is predicted point in the VPoint?
943     if( vp.GetBBox().Contains( pred_lat,  pred_lon ) )
944         drawit++;                     // yep
945     else {
946         LLBBox box;
947         box.SetFromSegment(td->Lat, td->Lon, pred_lat, pred_lon);
948     // And one more test to catch the case where target COG line crosses the screen,
949     // but the target itself and its pred point are both off-screen
950         if( !vp.GetBBox().IntersectOut(box))
951             drawit++;
952     }
953 
954     //    Do the draw if conditions indicate
955     if( !drawit )
956         return;
957 
958     GetCanvasPointPix(vp, cp, td->Lat, td->Lon, &TargetPoint );
959     GetCanvasPointPix(vp, cp, pred_lat, pred_lon, &PredPoint );
960 
961     bool b_hdgValid = true;
962 
963     float theta = (float)-PI / 2.;
964     //    If the target reported a valid HDG, then use it for icon
965     if( (int) ( td->HDG ) != 511 ) {
966         theta = ( ( td->HDG - 90 ) * PI / 180. ) + vp.rotation;
967     } else {
968         b_hdgValid = false;   // tentative judgement
969 
970         if(!g_bInlandEcdis){
971             // question: why can we not compute similar to above using COG instead of HDG?
972             //  Calculate the relative angle for this chart orientation
973             //    Use a 100 pixel vector to calculate angle
974             float angle_distance_nm = ( 100. / vp.view_scale_ppm ) / 1852.;
975             float angle_lat, angle_lon;
976             spherical_ll_gc_ll( td->Lat, td->Lon, td->COG, angle_distance_nm, &angle_lat, &angle_lon );
977 
978             wxPoint AnglePoint;
979             GetCanvasPointPix(vp, cp, angle_lat, angle_lon, &AnglePoint );
980 
981             if( abs( AnglePoint.x - TargetPoint.x ) > 0 ) {
982                 if( target_sog > g_ShowMoored_Kts ){
983                     theta = atan2f(
984                         (double) ( AnglePoint.y - TargetPoint.y ),
985                         (double) ( AnglePoint.x - TargetPoint.x ) );
986                     b_hdgValid = true;
987                 }
988                 else
989                     theta = (float)-PI / 2.;
990             } else {
991                 if( AnglePoint.y > TargetPoint.y )
992                     theta = (float)PI / 2.;             // valid COG 180
993                 else{
994                     theta = (float)-PI / 2.;            //  valid COG 000 or speed is too low to resolve course
995                     if (td->SOG >= g_ShowMoored_Kts )            //  valid COG 000 or speed is too low to resolve course
996                         b_hdgValid = true;
997                 }
998             }
999         }
1000     }
1001 
1002     // only need to compute this once;
1003     float sin_theta = sinf( theta ), cos_theta = cosf( theta );
1004 
1005     wxDash dash_long[2];
1006     dash_long[0] = (int) ( 1.0 * gFrame->GetPrimaryCanvas()->GetPixPerMM() );  // Long dash  <---------+
1007     dash_long[1] = (int) ( 0.5 * gFrame->GetPrimaryCanvas()->GetPixPerMM() );  // Short gap            |
1008 
1009     int targetscale = 100;
1010     int idxCC = 0;
1011     if (cp !=NULL){
1012          idxCC = cp->m_canvasIndex;
1013 
1014          if (idxCC > AIS_TARGETDATA_MAX_CANVAS - 1) return; //If more then n canvasses do not draw AIS anymore as we are running out of array index
1015          if (cp->GetAttenAIS()) {
1016               if (td->NavStatus <= 15) { // NavStatus > 15 is AtoN, and we don want AtoN being counted for attenuation
1017                   //with one tick per second targets can slink from 100 to 50 in abt 25 seconds
1018                    if (td->importance < AISImportanceSwitchPoint) targetscale = td->last_scale[idxCC] - 2;
1019                    //growing from 50 till 100% goes faster in 10 seconds
1020                    if (td->importance > AISImportanceSwitchPoint) targetscale = td->last_scale[idxCC] + 5;
1021                    if (targetscale > 100) targetscale = 100;
1022                    if (targetscale < 50) targetscale = 50;
1023                    td->last_scale[idxCC] = targetscale;
1024               }
1025          }
1026     }
1027 
1028     //  Draw the icon rotated to the COG
1029     wxPoint ais_real_size[6];
1030     bool bcan_draw_size = true;
1031     if (g_bDrawAISSize)
1032     {
1033         if (td->DimA + td->DimB == 0 || td->DimC + td->DimD == 0)
1034         {
1035             bcan_draw_size = false;
1036         }
1037         else
1038         {
1039             double ref_lat, ref_lon;
1040             ll_gc_ll( td->Lat, td->Lon, 0, 100. / 1852., &ref_lat, &ref_lon );
1041             wxPoint2DDouble b_point = vp.GetDoublePixFromLL( td->Lat, td->Lon );
1042             wxPoint2DDouble r_point = vp.GetDoublePixFromLL( ref_lat, ref_lon );
1043             double ppm = r_point.GetDistance(b_point) / 100.;
1044             double offwid = (td->DimC + td->DimD) * ppm * 0.25;
1045             double offlen = (td->DimA + td->DimB) * ppm * 0.15;
1046             ais_real_size[0].x = -td->DimD * ppm;
1047             ais_real_size[0].y = -td->DimB * ppm;
1048             ais_real_size[1].x = -td->DimD * ppm;
1049             ais_real_size[1].y = td->DimA * ppm - offlen;
1050             ais_real_size[2].x = -td->DimD * ppm + offwid;
1051             ais_real_size[2].y = td->DimA * ppm;
1052             ais_real_size[3].x = td->DimC * ppm - offwid;
1053             ais_real_size[3].y = td->DimA * ppm;
1054             ais_real_size[4].x = td->DimC * ppm;
1055             ais_real_size[4].y = td->DimA * ppm - offlen;
1056             ais_real_size[5].x = td->DimC * ppm;
1057             ais_real_size[5].y = -td->DimB * ppm;
1058 
1059             if (ais_real_size[4].x - ais_real_size[0].x < 16 || ais_real_size[2].y - ais_real_size[0].y < 30)
1060                 bcan_draw_size = false; //drawing too small does not make sense
1061             else {
1062                 bcan_draw_size = true;
1063                 transrot_pts(6, ais_real_size, sin_theta, cos_theta);
1064             }
1065         }
1066     }
1067 
1068 
1069     wxPoint *iconPoints;
1070     int nPoints;
1071     wxPoint ais_quad_icon[4]={ wxPoint(-8, -6),  wxPoint(0, 24),  wxPoint(8, -6), wxPoint(0, -6) };
1072     wxPoint ais_octo_icon[8] = {
1073         wxPoint(4,8),
1074         wxPoint(8,4),
1075         wxPoint(8,-4),
1076         wxPoint(4,-8),
1077         wxPoint(-4,-8),
1078         wxPoint(-8,-4),
1079         wxPoint(-8,4),
1080         wxPoint(-4,8) };
1081 
1082      if(!g_bInlandEcdis){
1083         // to speed up we only calculate scale when not max or minimal
1084         if (targetscale == 50){
1085             ais_quad_icon[0] = wxPoint(-4, -3);
1086             ais_quad_icon[1] = wxPoint( 0, 12);
1087             ais_quad_icon[2] = wxPoint( 4, -3);
1088             ais_quad_icon[3] = wxPoint( 0, -3);
1089         }
1090         else if ( targetscale != 100) {
1091                 ais_quad_icon[0] = wxPoint((int)-8*targetscale/100, (int)-6*targetscale/100);
1092                 ais_quad_icon[1] = wxPoint( 0, (int)24*targetscale/100);
1093                 ais_quad_icon[2] = wxPoint( (int)8*targetscale/100, (int)-6*targetscale/100);
1094                 ais_quad_icon[3] = wxPoint( 0, (int)-6*targetscale/100);
1095         }
1096 
1097         //   If this is an AIS Class B target, so symbolize it differently
1098         if( td->Class == AIS_CLASS_B ) ais_quad_icon[3].y = 0;
1099         else if( td->Class == AIS_GPSG_BUDDY ) {
1100             ais_quad_icon[0] = wxPoint(-5, -12);
1101             ais_quad_icon[1] = wxPoint(-3,  12);
1102             ais_quad_icon[2] = wxPoint( 3,  12);
1103             ais_quad_icon[3] = wxPoint( 5, -12);
1104         }
1105         else if( td->Class == AIS_DSC ) {
1106             ais_quad_icon[0].y = 0;
1107             ais_quad_icon[1].y = 8;
1108             ais_quad_icon[2].y = 0;
1109             ais_quad_icon[3].y = -8;
1110         }
1111         else if( td->Class == AIS_APRS ) {
1112             ais_quad_icon[0] = wxPoint(-8, -8);
1113             ais_quad_icon[1] = wxPoint(-8,  8);
1114             ais_quad_icon[2] = wxPoint( 8,  8);
1115             ais_quad_icon[3] = wxPoint( 8, -8);
1116         }
1117 
1118         transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1119 
1120         nPoints = 4;
1121         iconPoints = ais_quad_icon;
1122 
1123     }
1124     else{                               // iENC
1125         if(b_hdgValid){
1126             transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1127             nPoints = 4;
1128             iconPoints = ais_quad_icon;
1129         }
1130         else{
1131             nPoints = 8;
1132             iconPoints = ais_octo_icon;
1133         }
1134     }
1135 
1136 
1137     wxColour UBLCK = GetGlobalColor( _T ( "UBLCK" ));
1138     dc.SetPen( wxPen( UBLCK ) );
1139 
1140     // Default color is green
1141     wxColour UINFG = GetGlobalColor( _T ( "UINFG" ));
1142     wxBrush target_brush = wxBrush( UINFG );
1143 
1144     // Euro Inland targets render slightly differently, unless in InlandENC mode
1145     if( td->b_isEuroInland && !g_bInlandEcdis)
1146         target_brush = wxBrush( GetGlobalColor( _T ( "TEAL1" ) ) );
1147 
1148     // Target name comes from cache
1149     if( td->b_nameFromCache )
1150         target_brush = wxBrush( GetGlobalColor( _T ( "GREEN5" ) ) );
1151 
1152     //and....
1153     wxColour URED = GetGlobalColor( _T ( "URED" ));
1154     if( !td->b_nameValid )
1155         target_brush = wxBrush( GetGlobalColor( _T ( "CHYLW" ) ) );
1156     if( ( td->Class == AIS_DSC ) && ( td->ShipType == 12 ) )                    // distress
1157         target_brush = wxBrush( URED );
1158     if( td->b_SarAircraftPosnReport )
1159         target_brush = wxBrush( UINFG );
1160 
1161     if( ( td->n_alert_state == AIS_ALERT_SET ) && ( td->bCPA_Valid ) )
1162         target_brush = wxBrush( URED );
1163 
1164     if( td->b_positionDoubtful ) target_brush = wxBrush( GetGlobalColor( _T ( "UINFF" ) ) );
1165 
1166     wxPen target_outline_pen( UBLCK, AIS_width_target_outline );
1167 
1168     //    Check for alarms here, maintained by AIS class timer tick
1169     if( ((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid)) || (td->b_show_AIS_CPA && (td->bCPA_Valid))) {
1170         //  Calculate the point of CPA for target
1171         double tcpa_lat, tcpa_lon;
1172         ll_gc_ll( td->Lat, td->Lon, td->COG, target_sog * td->TCPA / 60., &tcpa_lat, &tcpa_lon );
1173         wxPoint tCPAPoint;
1174         wxPoint TPoint = TargetPoint;
1175         GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint );
1176 
1177         //  Draw the intercept line from target
1178         ClipResult res = cohen_sutherland_line_clip_i( &TPoint.x, &TPoint.y, &tCPAPoint.x,
1179                                                        &tCPAPoint.y, 0, vp.pix_width, 0, vp.pix_height );
1180 
1181         if( res != Invisible ) {
1182             wxPen ppPen2( URED, AIS_width_cogpredictor_line, wxPENSTYLE_USER_DASH );
1183             ppPen2.SetDashes( 2, dash_long );
1184             dc.SetPen( ppPen2 );
1185 
1186             dc.StrokeLine( TPoint.x, TPoint.y, tCPAPoint.x, tCPAPoint.y );
1187         }
1188 
1189         //  Calculate the point of CPA for ownship
1190         double ocpa_lat, ocpa_lon;
1191 
1192         //  Detect and handle the case where ownship COG is undefined....
1193         if( std::isnan(gCog) || std::isnan( gSog ) ) {
1194             ocpa_lat = gLat;
1195             ocpa_lon = gLon;
1196         }
1197         else {
1198             ll_gc_ll( gLat, gLon, gCog, gSog * td->TCPA / 60., &ocpa_lat, &ocpa_lon );
1199         }
1200 
1201         wxPoint oCPAPoint;
1202 
1203         GetCanvasPointPix(vp, cp, ocpa_lat, ocpa_lon, &oCPAPoint );
1204         GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint );
1205 
1206         //        Save a copy of these unclipped points
1207         wxPoint oCPAPoint_unclipped = oCPAPoint;
1208         wxPoint tCPAPoint_unclipped = tCPAPoint;
1209 
1210         //  Draw a line from target CPA point to ownship CPA point
1211         ClipResult ores = cohen_sutherland_line_clip_i( &tCPAPoint.x, &tCPAPoint.y,
1212                                                         &oCPAPoint.x, &oCPAPoint.y, 0, vp.pix_width, 0, vp.pix_height );
1213 
1214         if( ores != Invisible ) {
1215             wxColour yellow = GetGlobalColor( _T ( "YELO1" ) );
1216             dc.SetPen( wxPen( yellow, AIS_width_interceptbar_base ) );
1217             dc.StrokeLine( tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y );
1218 
1219             wxPen ppPen2( URED, AIS_width_interceptbar_top, wxPENSTYLE_USER_DASH );
1220             ppPen2.SetDashes( 2, dash_long );
1221             dc.SetPen( ppPen2 );
1222             dc.StrokeLine( tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y );
1223 
1224             //        Draw little circles at the ends of the CPA alert line
1225             wxBrush br( GetGlobalColor( _T ( "BLUE3" ) ) );
1226             dc.SetBrush( br );
1227             dc.SetPen( wxPen( UBLCK, AIS_width_target_outline ) );
1228 
1229             //  Using the true ends, not the clipped ends
1230             dc.StrokeCircle( tCPAPoint_unclipped.x, tCPAPoint_unclipped.y, AIS_intercept_bar_circle_diameter );
1231             dc.StrokeCircle( oCPAPoint_unclipped.x, oCPAPoint_unclipped.y, AIS_intercept_bar_circle_diameter );
1232         }
1233 
1234         // Draw the intercept line from ownship
1235         wxPoint oShipPoint;
1236         GetCanvasPointPix(vp, cp, gLat, gLon, &oShipPoint );
1237         oCPAPoint = oCPAPoint_unclipped;    // recover the unclipped point
1238 
1239         ClipResult ownres = cohen_sutherland_line_clip_i ( &oShipPoint.x, &oShipPoint.y,
1240                                                            &oCPAPoint.x, &oCPAPoint.y,
1241                                                            0, vp.pix_width, 0, vp.pix_height );
1242 
1243         if ( ownres != Invisible ) {
1244             wxPen ppPen2 ( URED, AIS_width_interceptline, wxPENSTYLE_USER_DASH );
1245             ppPen2.SetDashes( 2, dash_long );
1246             dc.SetPen(ppPen2);
1247 
1248             dc.StrokeLine ( oShipPoint.x, oShipPoint.y, oCPAPoint.x, oCPAPoint.y );
1249         } //TR : till here
1250 
1251         dc.SetPen( wxPen( UBLCK ) );
1252         dc.SetBrush( wxBrush( URED ) );
1253     }
1254 
1255     //  Highlight the AIS target symbol if an alert dialog is currently open for it
1256     if (cp != NULL) {
1257          if (g_pais_alert_dialog_active && g_pais_alert_dialog_active->IsShown() && cp) {
1258               if (g_pais_alert_dialog_active->Get_Dialog_MMSI() == td->MMSI)
1259                    cp->JaggyCircle(dc, wxPen(URED, 2), TargetPoint.x, TargetPoint.y, 100);
1260          }
1261     }
1262 
1263     //  Highlight the AIS target symbol if a query dialog is currently open for it
1264     if( g_pais_query_dialog_active && g_pais_query_dialog_active->IsShown() ) {
1265         if( g_pais_query_dialog_active->GetMMSI() == td->MMSI )
1266             TargetFrame( dc, wxPen( UBLCK , 2 ), TargetPoint.x, TargetPoint.y, 25 );
1267     }
1268 
1269     //       Render the COG line if the speed is greater than moored speed defined by ais options dialog
1270     if( ( g_bShowCOG ) && ( target_sog > g_ShowMoored_Kts ) && td->b_active ) {
1271         int pixx = TargetPoint.x;
1272         int pixy = TargetPoint.y;
1273         int pixx1 = PredPoint.x;
1274         int pixy1 = PredPoint.y;
1275 
1276         //  Don't draw the COG line  and predictor point if zoomed far out.... or if target lost/inactive
1277         float l = sqrtf( powf( (float) ( PredPoint.x - TargetPoint.x ), 2 )
1278                          + powf( (float) ( PredPoint.y - TargetPoint.y ), 2 ) );
1279 
1280         if( l > 24 ) {
1281             ClipResult res = cohen_sutherland_line_clip_i( &pixx, &pixy, &pixx1, &pixy1, 0,
1282                                                            vp.pix_width, 0, vp.pix_height );
1283 
1284             if( res != Invisible ) {
1285                     //    Draw a wider coloured line
1286                     if (targetscale >= 75){
1287                         wxPen wide_pen( target_brush.GetColour(), AIS_width_cogpredictor_base );
1288                         dc.SetPen( wide_pen );
1289                         dc.StrokeLine( pixx, pixy, pixx1, pixy1 );
1290                     }
1291 
1292                     if( AIS_width_cogpredictor_base > 1 ) {
1293                         //    Draw narrow black line
1294                         wxPen narrow_pen( UBLCK, AIS_width_cogpredictor_line );
1295                         if( targetscale < 75 ){
1296                             narrow_pen.SetWidth(1);
1297                             narrow_pen.SetStyle(wxPENSTYLE_DOT);
1298                         }
1299                         dc.SetPen( narrow_pen );
1300                         dc.StrokeLine( pixx, pixy, pixx1, pixy1 );
1301                     }
1302 
1303                     if(dc.GetDC()) {
1304                         dc.SetBrush( target_brush );
1305                         if (targetscale >= 75)
1306                             dc.StrokeCircle( PredPoint.x, PredPoint.y, 5 );
1307                         else
1308                             dc.StrokeCircle( PredPoint.x, PredPoint.y, 2 );
1309                     } else {
1310 #ifdef ocpnUSE_GL
1311 
1312 #ifndef USE_ANDROID_GLES2
1313                         glPushMatrix();
1314                         glTranslated(pixx1, pixy1, 0);
1315                         glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1316                         // draw circle
1317                         float points[] = {0.0f, 5.0f, 2.5f, 4.330127f, 4.330127f, 2.5f, 5.0f,
1318                                       0, 4.330127f, -2.5f, 2.5f, -4.330127f, 0, -5.1f,
1319                                       -2.5f, -4.330127f, -4.330127f, -2.5f, -5.0f, 0,
1320                                       -4.330127f, 2.5f, -2.5f, 4.330127f, 0, 5.0f};
1321                         if (targetscale <= 75){
1322                             for (unsigned int i = 0; i<(sizeof points) / (sizeof *points); i++ )
1323                                points[i] =  points[i]/2;
1324                        }
1325 
1326                         wxColour c = target_brush.GetColour();
1327                         glColor3ub(c.Red(), c.Green(), c.Blue());
1328 
1329                         glBegin(GL_TRIANGLE_FAN);
1330                         for(unsigned int i=0; i<(sizeof points) / (sizeof *points); i+=2)
1331                             glVertex2i(points[i], points[i+1]);
1332                         glEnd();
1333 
1334                         glColor3ub(0, 0, 0);
1335                         glLineWidth( AIS_width_target_outline );
1336                         glBegin(GL_LINE_LOOP);
1337                         for(unsigned int i=0; i<(sizeof points) / (sizeof *points); i+=2)
1338                             glVertex2i(points[i], points[i+1]);
1339                         glEnd();
1340                         glPopMatrix();
1341 #else
1342                         double nominal_circle_size_pixels = wxMax(14.0, floor(g_Platform->GetDisplayDPmm() * 1));             //1.0 mm nominal diameter, but not less than 4 pixel
1343                         dc.SetBrush( target_brush );
1344                         dc.StrokeCircle( PredPoint.x, PredPoint.y, nominal_circle_size_pixels/2 );
1345 
1346 #endif
1347 #endif
1348                     }
1349             }
1350 
1351             //      Draw RateOfTurn Vector
1352             if( ( td->ROTAIS != 0 ) && ( td->ROTAIS != -128 ) && (!g_bShowScaled) ) {
1353                 float cog_angle = td->COG *PI/180.;
1354 
1355                 float theta2 = theta;           // ownship drawn angle
1356                  if (td->SOG >= g_ShowMoored_Kts )
1357                      theta2 = cog_angle - (PI / 2);    // actual cog angle
1358 
1359                 float nv = 10;
1360                  if( td->ROTAIS > 0 )
1361                       theta2 += (float)PI / 2;
1362                  else
1363                      theta2 -= (float)PI / 2;
1364 
1365                 int xrot = (int) round ( pixx1 + ( nv * cosf ( theta2 ) ) );
1366                 int yrot = (int) round ( pixy1 + ( nv * sinf ( theta2 ) ) );
1367                 dc.StrokeLine( pixx1, pixy1, xrot, yrot );
1368             }
1369         }
1370     }
1371 
1372     //        Actually Draw the target
1373     if( td->Class == AIS_ARPA ) {
1374         wxPen target_pen( UBLCK, 2 );
1375 
1376         dc.SetPen( target_pen );
1377         dc.SetBrush( target_brush );
1378 
1379         dc.StrokeCircle( TargetPoint.x, TargetPoint.y, 9 );
1380         dc.StrokeCircle( TargetPoint.x, TargetPoint.y, 1 );
1381         //        Draw the inactive cross-out line
1382         if( !td->b_active ) {
1383             dc.SetPen( wxPen( UBLCK, 2 ) );
1384             dc.StrokeLine( TargetPoint.x - 14, TargetPoint.y, TargetPoint.x + 14, TargetPoint.y );
1385             dc.SetPen( wxPen( UBLCK, 1 ) );
1386         }
1387     } else if( td->Class == AIS_ATON ) {                   // Aid to Navigation
1388         AtoN_Diamond( dc, TargetPoint.x, TargetPoint.y, 12, td );
1389     } else if( td->Class == AIS_BASE ) {                      // Base Station
1390         Base_Square( dc, wxPen( UBLCK , 2 ), TargetPoint.x, TargetPoint.y, 8 );
1391     } else if( td->Class == AIS_SART ) {                      // SART Target
1392         if( td->NavStatus == 14 )       // active
1393             SART_Render( dc, wxPen( URED , 2 ), TargetPoint.x, TargetPoint.y, 8 );
1394         else
1395             SART_Render( dc, wxPen( GetGlobalColor( _T ( "UGREN" ) ), 2 ),
1396                          TargetPoint.x, TargetPoint.y, 8 );
1397 
1398     } else if(td->b_SarAircraftPosnReport) {
1399         int airtype = (td->MMSI % 1000)/100; // xxxyyy5zz >> helicopter
1400         int ar = airtype == 5 ? 15 : 9;      // array size
1401         wxPoint SarIcon[15];
1402         wxPoint SarRot[15];
1403 
1404         if (airtype == 5) {
1405             SarIcon[0] = wxPoint(0, 9);
1406             SarIcon[1] = wxPoint(1, 1);
1407             SarIcon[2] = wxPoint(2, 1);
1408             SarIcon[3] = wxPoint(9, 8);
1409             SarIcon[4] = wxPoint(9, 7);
1410             SarIcon[5] = wxPoint(3, 0);
1411             SarIcon[6] = wxPoint(3, -5);
1412             SarIcon[7] = wxPoint(9, -12);
1413             SarIcon[8] = wxPoint(9, -13);
1414             SarIcon[9] = wxPoint(2, -5);
1415             SarIcon[10] = wxPoint(1, -15);
1416             SarIcon[11] = wxPoint(3, -16);
1417             SarIcon[12] = wxPoint(4, -18);
1418             SarIcon[13] = wxPoint(1, -18);
1419             SarIcon[14] = wxPoint(0, -19);
1420         }
1421         else {
1422             SarIcon[0] = wxPoint(0, 12);
1423             SarIcon[1] = wxPoint(4, 2);
1424             SarIcon[2] = wxPoint(16, -2);
1425             SarIcon[3] = wxPoint(16, -8);
1426             SarIcon[4] = wxPoint(4, -8);
1427             SarIcon[5] = wxPoint(3, -16);
1428             SarIcon[6] = wxPoint(10, -18);
1429             SarIcon[7] = wxPoint(10, -22);
1430             SarIcon[8] = wxPoint(0, -22);
1431         }
1432 
1433         // Draw icon as two halves
1434 
1435         //  First half
1436 
1437         for( int i = 0; i < ar; i++ )
1438             SarRot[i] = SarIcon[i];
1439         transrot_pts(ar, SarRot, sin_theta, cos_theta);
1440 
1441         wxPen tri_pen( target_brush.GetColour(), 1 );
1442         dc.SetPen( tri_pen );
1443         dc.SetBrush( target_brush );
1444 
1445         int mappings[7][3] = {{0, 1, 4}, {1, 2, 3}, {1, 3, 4}, {0, 4, 5}, {0, 5, 8}, {5, 6, 7}, {5, 7, 8}};
1446         for(int i=0; i<7; i++) {
1447             wxPoint ais_tri_icon[3];
1448             for(int j=0; j<3; j++)
1449                 ais_tri_icon[j] = SarRot[mappings[i][j]];
1450             dc.StrokePolygon( 3, ais_tri_icon, TargetPoint.x, TargetPoint.y );
1451         }
1452 
1453         dc.SetPen( target_outline_pen );
1454         dc.SetBrush( wxBrush( UBLCK, wxBRUSHSTYLE_TRANSPARENT ) );
1455         dc.StrokePolygon( ar, SarRot, TargetPoint.x, TargetPoint.y );
1456 
1457         // second half
1458 
1459         for( int i = 0; i < ar; i++ )
1460             SarRot[i] = wxPoint(-SarIcon[i].x, SarIcon[i].y); // mirror the icon (x -> -x)
1461 
1462         transrot_pts(ar, SarRot, sin_theta, cos_theta);
1463 
1464         dc.SetPen( tri_pen );
1465         dc.SetBrush( target_brush );
1466 
1467         for(int i=0; i<7; i++) {
1468             wxPoint ais_tri_icon[3];
1469             for(int j=0; j<3; j++)
1470                 ais_tri_icon[j] = SarRot[mappings[i][j]];
1471             dc.StrokePolygon( 3, ais_tri_icon, TargetPoint.x, TargetPoint.y );
1472         }
1473 
1474         dc.SetPen( target_outline_pen );
1475         dc.SetBrush( wxBrush( UBLCK, wxBRUSHSTYLE_TRANSPARENT ) );
1476         dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y );
1477 
1478     } else {         // ship class A or B or a Buddy or DSC
1479         wxPen target_pen( UBLCK, 1 );
1480         dc.SetPen( target_pen );
1481 
1482         wxPoint Point = TargetPoint;
1483         if (g_bDrawAISRealtime &&
1484             (td->Class == AIS_CLASS_A || td->Class == AIS_CLASS_B ) &&
1485              td->SOG > g_AIS_RealtPred_Kts &&
1486              td->SOG < 102.2) {
1487             wxDateTime now = wxDateTime::Now();
1488             now.MakeGMT();
1489             int target_age = now.GetTicks() - td->PositionReportTicks;
1490 
1491             float lat, lon;
1492             spherical_ll_gc_ll(td->Lat, td->Lon, td->COG, td->SOG*target_age/3600.0, &lat, &lon);
1493 
1494             GetCanvasPointPix(vp, cp, lat, lon, &Point );
1495 
1496             wxBrush realtime_brush = wxBrush( GetGlobalColor( "GREY1" ) );
1497             dc.SetBrush( realtime_brush );
1498             dc.StrokePolygon( nPoints, iconPoints, Point.x, Point.y, AIS_scale_factor);
1499         }
1500         dc.SetBrush( target_brush );
1501 
1502         if(dc.GetDC()) {
1503             dc.StrokePolygon( nPoints, iconPoints, TargetPoint.x, TargetPoint.y, AIS_scale_factor );
1504         } else {
1505 #ifdef ocpnUSE_GL
1506 #ifndef USE_ANDROID_GLES2
1507             wxColour c = target_brush.GetColour();
1508             glColor3ub(c.Red(), c.Green(), c.Blue());
1509 
1510             glPushMatrix();
1511             glTranslated(TargetPoint.x, TargetPoint.y, 0);
1512             glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1513 
1514             glBegin(GL_TRIANGLE_FAN);
1515 
1516             if(nPoints == 4){
1517                 glVertex2i(ais_quad_icon[3].x, ais_quad_icon[3].y);
1518                 glVertex2i(ais_quad_icon[0].x, ais_quad_icon[0].y);
1519                 glVertex2i(ais_quad_icon[1].x, ais_quad_icon[1].y);
1520                 glVertex2i(ais_quad_icon[2].x, ais_quad_icon[2].y);
1521             }
1522              else{
1523                  for(int i=0 ; i < 8 ; i++){
1524                      glVertex2i(iconPoints[i].x, iconPoints[i].y);
1525                  }
1526              }
1527 
1528             glEnd();
1529 #else
1530             dc.SetPen( target_outline_pen );
1531             dc.DrawPolygon( nPoints, iconPoints, TargetPoint.x, TargetPoint.y, AIS_scale_factor );
1532 #endif
1533 
1534             // Draw target outline, if not already done
1535             // Depending on platform  (wx) capabilities, draw the nicest lines possible
1536 #if wxUSE_GRAPHICS_CONTEXT
1537             glPopMatrix();
1538 
1539             dc.SetPen( target_outline_pen );
1540             dc.SetBrush( wxBrush( UBLCK, wxBRUSHSTYLE_TRANSPARENT ) );
1541             dc.StrokePolygon( nPoints, iconPoints, TargetPoint.x, TargetPoint.y, AIS_scale_factor );
1542 #else
1543             glLineWidth(AIS_width_target_outline);
1544 #ifndef USE_ANDROID_GLES2
1545             glColor3ub(UBLCK.Red(), UBLCK.Green(), UBLCK.Blue());
1546 
1547             glBegin(GL_LINE_LOOP);
1548             for(int i=0; i<nPoints; i++)
1549                 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1550             glEnd();
1551             glPopMatrix();
1552 
1553 #endif
1554 
1555 #endif
1556 
1557 
1558 #endif
1559         }
1560 
1561         if (g_bDrawAISSize && bcan_draw_size){
1562             dc.SetPen( target_outline_pen );
1563             dc.SetBrush( wxBrush( UBLCK, wxBRUSHSTYLE_TRANSPARENT ) );
1564             if(!g_bInlandEcdis){
1565                  dc.StrokePolygon( 6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0 );
1566             }
1567             else{
1568                 if(b_hdgValid){
1569                     dc.StrokePolygon( 6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0 );
1570                 }
1571             }
1572         }
1573 
1574         dc.SetBrush( wxBrush( GetGlobalColor( _T ( "SHIPS" ) ) ) );
1575         int navstatus = td->NavStatus;
1576 
1577         // HSC usually have correct ShipType but navstatus == 0...
1578         if( ( ( td->ShipType >= 40 ) && ( td->ShipType < 50 ) )
1579             && navstatus == UNDERWAY_USING_ENGINE ) navstatus = HSC;
1580 
1581         if(targetscale > 90){
1582             switch( navstatus ) {
1583             case MOORED:
1584             case AT_ANCHOR: {
1585                 dc.StrokeCircle( TargetPoint.x, TargetPoint.y, 4 );
1586                 break;
1587             }
1588             case RESTRICTED_MANOEUVRABILITY: {
1589                 wxPoint diamond[4];
1590                 diamond[0] = wxPoint(  4, 0 );
1591                 diamond[1] = wxPoint(  0, -6 );
1592                 diamond[2] = wxPoint( -4, 0 );
1593                 diamond[3] = wxPoint(  0, 6 );
1594                 dc.StrokePolygon( 4, diamond, TargetPoint.x, TargetPoint.y-11 );
1595                 dc.StrokeCircle( TargetPoint.x, TargetPoint.y, 4 );
1596                 dc.StrokeCircle( TargetPoint.x, TargetPoint.y-22, 4 );
1597                 break;
1598                 break;
1599             }
1600             case CONSTRAINED_BY_DRAFT: {
1601                 wxPoint can[4] = {wxPoint(-3, 0), wxPoint(3, 0), wxPoint(3, -16), wxPoint(-3, -16)};
1602                 dc.StrokePolygon( 4, can, TargetPoint.x, TargetPoint.y );
1603                 break;
1604             }
1605             case NOT_UNDER_COMMAND: {
1606                 dc.StrokeCircle( TargetPoint.x, TargetPoint.y, 4 );
1607                 dc.StrokeCircle( TargetPoint.x, TargetPoint.y-9, 4 );
1608                 break;
1609             }
1610             case FISHING: {
1611                 wxPoint tri[3];
1612                 tri[0] = wxPoint( -4, 0 );
1613                 tri[1] = wxPoint(  4, 0 );
1614                 tri[2] = wxPoint(  0, -9 );
1615                 dc.StrokePolygon( 3, tri, TargetPoint.x, TargetPoint.y );
1616                 tri[0] = wxPoint(  0, -9 );
1617                 tri[1] = wxPoint(  4, -18 );
1618                 tri[2] = wxPoint( -4, -18 );
1619                 dc.StrokePolygon( 3, tri, TargetPoint.x, TargetPoint.y );
1620                 break;
1621             }
1622             case AGROUND: {
1623                 dc.StrokeCircle( TargetPoint.x, TargetPoint.y, 4 );
1624                 dc.StrokeCircle( TargetPoint.x, TargetPoint.y-9, 4 );
1625                 dc.StrokeCircle( TargetPoint.x, TargetPoint.y-18, 4 );
1626                 break;
1627             }
1628             case HSC:
1629             case WIG: {
1630                 dc.SetBrush( target_brush );
1631 
1632                 wxPoint arrow1[3] = {wxPoint( -4, 20 ), wxPoint(  0, 27 ), wxPoint(  4, 20 )};
1633                 transrot_pts(3, arrow1, sin_theta, cos_theta, TargetPoint);
1634                 dc.StrokePolygon( 3, arrow1 );
1635 
1636                 wxPoint arrow2[3] = {wxPoint( -4, 27 ), wxPoint(  0, 34 ), wxPoint(  4, 27 )};
1637                 transrot_pts(3, arrow2, sin_theta, cos_theta, TargetPoint);
1638                 dc.StrokePolygon( 3, arrow2 );
1639                 break;
1640             }
1641             }
1642         }//end if (targetscale > 75)
1643 
1644         //        Draw the inactive cross-out line
1645         if( !td->b_active ) {
1646             wxPoint p1 = transrot( wxPoint( (int)-14*targetscale/100, 0 ), sin_theta, cos_theta, TargetPoint );
1647             wxPoint p2 = transrot( wxPoint( (int)14*targetscale/100, 0 ),  sin_theta, cos_theta, TargetPoint );
1648 
1649             dc.SetPen( wxPen( UBLCK, 2 ) );
1650             dc.StrokeLine( p1.x, p1.y, p2.x, p2.y );
1651         }
1652 
1653         //    European Inland AIS define a "stbd-stbd" meeting sign, a blue paddle.
1654         //    Symbolize it if set by most recent message
1655         //    Blue paddel is used while "not engaged"(1) or "engaged"(2)  (3 == "reserved")
1656         if( td->blue_paddle && td->blue_paddle < 3) {
1657             wxPoint ais_flag_icon[4];
1658             int penWidth = 2;
1659 
1660             if(g_bInlandEcdis){
1661                 if(b_hdgValid){
1662                     ais_flag_icon[0] = wxPoint( -4,4);
1663                     ais_flag_icon[1] = wxPoint( -4,11);
1664                     ais_flag_icon[2] = wxPoint( -11,11);
1665                     ais_flag_icon[3] = wxPoint( -11,4);
1666                     transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1667                 }
1668                 else{
1669                     ais_flag_icon[0] = wxPoint( TargetPoint.x-4, TargetPoint.y+4);
1670                     ais_flag_icon[1] = wxPoint( TargetPoint.x-4, TargetPoint.y-3);
1671                     ais_flag_icon[2] = wxPoint( TargetPoint.x+3, TargetPoint.y-3);
1672                     ais_flag_icon[3] = wxPoint( TargetPoint.x+3, TargetPoint.y+4);
1673                 }
1674 
1675                 dc.SetPen( wxPen( GetGlobalColor( _T ( "CHWHT" ) ), penWidth ) );
1676 
1677             }
1678             else{
1679                 ais_flag_icon[0] = wxPoint((int)-8*targetscale/100, (int)-6*targetscale/100);
1680                 ais_flag_icon[1] = wxPoint( (int)-2*targetscale/100, (int)18*targetscale/100);
1681                 ais_flag_icon[2] = wxPoint( (int)-2*targetscale/100, 0);
1682                 ais_flag_icon[3] = wxPoint( (int)-2*targetscale/100, (int)-6*targetscale/100);
1683                 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1684 
1685 
1686                 if(targetscale < 100)
1687                     penWidth = 1;
1688                 dc.SetPen( wxPen( GetGlobalColor( _T ( "CHWHT" ) ), penWidth ) );
1689 
1690             }
1691             if( td->blue_paddle == 1) {
1692                     ais_flag_icon[1] = ais_flag_icon[0];
1693                     ais_flag_icon[2] = ais_flag_icon[3];
1694                 }
1695 
1696             dc.SetBrush( wxBrush( GetGlobalColor( _T ( "UINFB" ) ) ) );
1697             dc.StrokePolygon( 4, ais_flag_icon);
1698         }
1699     }
1700 
1701     if ( (g_bShowAISName) && (targetscale > 75) ) {
1702         int true_scale_display = (int) (floor( vp.chart_scale / 100. ) * 100);
1703         if( true_scale_display < g_Show_Target_Name_Scale ) { // from which scale to display name
1704 
1705             wxString tgt_name = td->GetFullName();
1706             tgt_name = tgt_name.substr( 0, tgt_name.find( _T ( "Unknown" ), 0) );
1707 
1708             if ( tgt_name != wxEmptyString ) {
1709                 dc.SetFont( *FontMgr::Get().GetFont( _( "AIS Target Name" ), 12 ) );
1710                 dc.SetTextForeground( FontMgr::Get().GetFontColor( _( "AIS Target Name" ) ) );
1711 
1712                 int w, h;
1713                 dc.GetTextExtent( tgt_name, &w, &h );
1714 
1715                 if ( ( td->COG > 90 ) && ( td->COG < 180 ) )
1716                     dc.DrawText( tgt_name, TargetPoint.x+10, TargetPoint.y-h );
1717                 else
1718                     dc.DrawText( tgt_name, TargetPoint.x+10, TargetPoint.y+0.5*h );
1719 
1720             } //If name do not empty
1721         } // if scale
1722     }
1723 
1724     //  Draw tracks if enabled
1725     //  Check the Special MMSI Properties array
1726     bool b_noshow = false;
1727     bool b_forceshow = false;
1728     for(unsigned int i=0 ; i < g_MMSI_Props_Array.GetCount() ; i++){
1729         if(td->MMSI == g_MMSI_Props_Array[i]->MMSI ){
1730             MMSIProperties *props = g_MMSI_Props_Array[i];
1731             if( TRACKTYPE_NEVER == props->TrackType){
1732                 b_noshow = true;
1733                 break;
1734             }
1735             else if( TRACKTYPE_ALWAYS == props->TrackType){
1736                 b_forceshow = true;
1737                 break;
1738             }
1739             else
1740                 break;
1741         }
1742     }
1743 
1744     if( (!b_noshow && td->b_show_track) || b_forceshow ) {
1745 
1746         //  create vector of x-y points
1747         int TrackLength = td->m_ptrack->GetCount();
1748         int TrackPointCount;
1749         wxPoint *TrackPoints = 0;
1750         if (TrackLength > 1) {
1751             TrackPoints = new wxPoint[TrackLength];
1752             wxAISTargetTrackListNode *node = td->m_ptrack->GetFirst();
1753             for (TrackPointCount = 0; node && (TrackPointCount < TrackLength); TrackPointCount++) {
1754                 AISTargetTrackPoint *ptrack_point = node->GetData();
1755                 GetCanvasPointPix(vp, cp, ptrack_point->m_lat, ptrack_point->m_lon, &TrackPoints[TrackPointCount]);
1756                 node = node->GetNext();
1757             }
1758         }
1759 
1760         wxColour c = GetGlobalColor( _T ( "CHMGD" ) );
1761         dc.SetPen( wxPen( c,   2 * AIS_nominal_line_width_pix ) );
1762 
1763 #ifdef ocpnUSE_GL
1764 #ifndef USE_ANDROID_GLES2
1765         if (TrackLength > 1){
1766             glLineWidth(2);
1767             glColor3ub(c.Red(), c.Green(), c.Blue());
1768             glBegin(GL_LINE_STRIP);
1769 
1770             for (TrackPointCount = 0; TrackPointCount < TrackLength; TrackPointCount++)
1771                     glVertex2i(TrackPoints[TrackPointCount].x, TrackPoints[TrackPointCount].y);
1772 
1773             glEnd();
1774         }
1775 #else
1776         if ( TrackLength > 1 )
1777             dc.DrawLines(TrackPointCount, TrackPoints);
1778 #endif
1779 
1780 #else
1781         if ( dc.GetDC() && (TrackLength > 1) )
1782             dc.StrokeLines(TrackPointCount, TrackPoints);
1783 
1784 #endif
1785 
1786         if (TrackLength > 1)
1787             delete[] TrackPoints;
1788 
1789     }           // Draw tracks
1790 }
1791 
AISDraw(ocpnDC & dc,ViewPort & vp,ChartCanvas * cp)1792 void AISDraw( ocpnDC& dc, ViewPort& vp, ChartCanvas *cp )
1793 {
1794     if( !g_pAIS ) return;
1795 
1796     // Toggling AIS display on and off
1797     if (cp != NULL) {
1798          if (!cp->GetShowAIS())
1799               return;
1800     }
1801 
1802     AISSetMetrics();
1803 
1804     AIS_Target_Hash::iterator it;
1805     AIS_Target_Hash *current_targets = g_pAIS->GetTargetList();
1806     //      Iterate over the AIS Target Hashmap but only for the main chartcanvas. For secundairy canvasses we use the same value for the AIS importance
1807     bool go = false;
1808 
1809     if (cp == NULL) {
1810          go = true;
1811     }
1812     else if (cp->m_canvasIndex == 0) {
1813          go = true;
1814     }
1815 
1816     if (go) {
1817          for (it = (*current_targets).begin(); it != (*current_targets).end(); ++it) {
1818               //calculate the importancefactor for each target
1819               AIS_Target_Data *td = it->second;
1820               double  So, Cpa, Rang, Siz = 0.0;
1821               So = g_ScaledNumWeightSOG / 12 * td->SOG; //0 - 12 knts gives 0 - g_ScaledNumWeightSOG weight
1822               if (So > g_ScaledNumWeightSOG) So = g_ScaledNumWeightSOG;
1823 
1824               if (td->bCPA_Valid) {
1825                    Cpa = g_ScaledNumWeightCPA - g_ScaledNumWeightCPA / 4 * td->CPA;
1826                    //if TCPA is positief (target is coming closer), make weight of CPA bigger
1827                    if (td->TCPA > .0) Cpa = Cpa + Cpa * g_ScaledNumWeightTCPA / 100;
1828                    if (Cpa < .0) Cpa = .0; //if CPA is > 4
1829               }
1830               else Cpa = .0;
1831 
1832               Rang = g_ScaledNumWeightRange / 10 * td->Range_NM;
1833               if (Rang > g_ScaledNumWeightRange) Rang = g_ScaledNumWeightRange;
1834               Rang = g_ScaledNumWeightRange - Rang;
1835 
1836               Siz = g_ScaledNumWeightSizeOfT / 30 * (td->DimA + td->DimB);
1837               if (Siz > g_ScaledNumWeightSizeOfT) Siz = g_ScaledNumWeightSizeOfT;
1838               td->importance = (float)So + Cpa + Rang + Siz;
1839          }
1840     }
1841 
1842 
1843     // If needed iterate over all targets, check if they fit in the viewport and if yes add the importancefactor to a sorted list
1844     AISImportanceSwitchPoint = 0.0;
1845 
1846     float *Array = new float[g_ShowScaled_Num];
1847     for(int i=0 ; i < g_ShowScaled_Num ; i++)
1848         Array[i] = 0.0;
1849 
1850     int LowestInd = 0;
1851     if (cp != NULL) {
1852          if (cp->GetAttenAIS()) {
1853               for (it = (*current_targets).begin(); it != (*current_targets).end(); ++it) {
1854                    AIS_Target_Data *td = it->second;
1855                    if (vp.GetBBox().Contains(td->Lat, td->Lon))
1856                    {
1857                         if (td->importance > AISImportanceSwitchPoint) {
1858                              Array[LowestInd] = td->importance;
1859 
1860                              AISImportanceSwitchPoint = Array[0];
1861                              LowestInd = 0;
1862                              for (int i = 1; i < g_ShowScaled_Num; i++) {
1863                                   if (Array[i] < AISImportanceSwitchPoint) {
1864                                        AISImportanceSwitchPoint = Array[i];
1865                                        LowestInd = i;
1866                                   }
1867                              }
1868                         }
1869                    }
1870               }
1871          }
1872     }
1873     delete[] Array;
1874 
1875 
1876 
1877     //    Draw all targets in three pass loop, sorted on SOG, GPSGate & DSC on top
1878     //    This way, fast targets are not obscured by slow/stationary targets
1879     for( it = ( *current_targets ).begin(); it != ( *current_targets ).end(); ++it ) {
1880         AIS_Target_Data *td = it->second;
1881         if( ( td->SOG < g_ShowMoored_Kts )
1882                 && !( ( td->Class == AIS_GPSG_BUDDY ) || ( td->Class == AIS_DSC ) ) )
1883         {
1884             AISDrawTarget( td, dc, vp, cp );
1885         }
1886     }
1887 
1888     for( it = ( *current_targets ).begin(); it != ( *current_targets ).end(); ++it ) {
1889         AIS_Target_Data *td = it->second;
1890         if( ( td->SOG >= g_ShowMoored_Kts )
1891                 && !( ( td->Class == AIS_GPSG_BUDDY ) || ( td->Class == AIS_DSC ) ) )
1892         {
1893             AISDrawTarget( td, dc, vp, cp ); // yes this is a doubling of code;(
1894             if( td->importance > 0 )
1895             AISDrawTarget( td, dc, vp, cp );
1896         }
1897     }
1898 
1899     for( it = ( *current_targets ).begin(); it != ( *current_targets ).end(); ++it ) {
1900         AIS_Target_Data *td = it->second;
1901         if( ( td->Class == AIS_GPSG_BUDDY ) || ( td->Class == AIS_DSC ) )
1902             AISDrawTarget( td, dc, vp, cp );
1903     }
1904 }
1905 
AnyAISTargetsOnscreen(ChartCanvas * cc,ViewPort & vp)1906 bool AnyAISTargetsOnscreen( ChartCanvas *cc, ViewPort &vp )
1907 {
1908     if( !g_pAIS )
1909         return false;
1910 
1911     if( !cc->GetShowAIS() )
1912         return false;//
1913 
1914     //      Iterate over the AIS Target Hashmap
1915     AIS_Target_Hash::iterator it;
1916     AIS_Target_Hash *current_targets = g_pAIS->GetTargetList();
1917 
1918     for( it = ( *current_targets ).begin(); it != ( *current_targets ).end(); ++it ) {
1919         AIS_Target_Data *td = it->second;
1920         if( vp.GetBBox().Contains( td->Lat,  td->Lon ) )
1921             return true;                       // yep
1922     }
1923 
1924     return false;
1925 }
1926