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