33#include <wx/tokenzr.h>
34#include <wx/datetime.h>
35#include <wx/wfstream.h>
36#include <wx/imaglist.h>
49#include "ais_decoder.h"
50#include "ais_target_data.h"
51#include "AISTargetAlertDialog.h"
52#include "AISTargetQueryDialog.h"
54#include "wx28compat.h"
55#include "OCPNPlatform.h"
57#include "ocpn_frame.h"
59#include "ocpn_plugin.h"
64int g_ais_cog_predictor_width;
70extern bool g_bShowCOG;
71extern double g_ShowCOG_Mins;
72extern bool g_bHideMoored;
73extern double g_ShowMoored_Kts;
74extern bool g_bAISShowTracks;
75extern bool g_bShowAreaNotices;
76extern bool g_bDrawAISSize;
77extern bool g_bDrawAISRealtime;
78extern double g_AIS_RealtPred_Kts;
79extern bool g_bShowAISName;
80extern int g_Show_Target_Name_Scale;
81extern bool g_bInlandEcdis;
83extern int g_ais_alert_dialog_x, g_ais_alert_dialog_y;
84extern int g_ais_alert_dialog_sx, g_ais_alert_dialog_sy;
86extern bool g_bShowScaled;
89int ImportanceSwitchPoint = 100;
90int g_ScaledNumWeightSOG;
91int g_ScaledNumWeightCPA;
92int g_ScaledNumWeightTCPA;
93int g_ScaledNumWeightRange;
94int g_ScaledNumWeightSizeOfT;
95int g_ScaledSizeMinimal;
97extern ArrayOfMmsiProperties g_MMSI_Props_Array;
100extern float g_ShipScaleFactorExp;
102float AISImportanceSwitchPoint = 0.0;
105static const long long lNaN = 0xfff8000000000000;
106#define NAN (*(double *)&lNaN)
108wxString ais8_001_22_notice_names[] = {
110 _(
"Caution Area: Marine mammals habitat (implies whales NOT "
112 _(
"Caution Area: Marine mammals in area - reduce speed"),
113 _(
"Caution Area: Marine mammals in area - stay clear"),
114 _(
"Caution Area: Marine mammals in area - report sightings"),
115 _(
"Caution Area: Protected habitat - reduce speed"),
116 _(
"Caution Area: Protected habitat - stay clear"),
117 _(
"Caution Area: Protected habitat - no fishing or anchoring"),
118 _(
"Caution Area: Derelicts (drifting objects)"),
119 _(
"Caution Area: Traffic congestion"),
120 _(
"Caution Area: Marine event"),
121 _(
"Caution Area: Divers down"),
122 _(
"Caution Area: Swim area"),
123 _(
"Caution Area: Dredge operations"),
124 _(
"Caution Area: Survey operations"),
125 _(
"Caution Area: Underwater operation"),
126 _(
"Caution Area: Seaplane operations"),
127 _(
"Caution Area: Fishery - nets in water"),
128 _(
"Caution Area: Cluster of fishing vessels"),
129 _(
"Caution Area: Fairway closed"),
130 _(
"Caution Area: Harbour closed"),
131 _(
"Caution Area: Risk (define in Associated text field)"),
132 _(
"Caution Area: Underwater vehicle operation"),
133 _(
"(reserved for future use)"),
134 _(
"Environmental Caution Area: Storm front (line squall)"),
135 _(
"Environmental Caution Area: Hazardous sea ice"),
136 _(
"Environmental Caution Area: Storm warning (storm cell or line "
138 _(
"Environmental Caution Area: High wind"),
139 _(
"Environmental Caution Area: High waves"),
140 _(
"Environmental Caution Area: Restricted visibility (fog, rain, "
142 _(
"Environmental Caution Area: Strong currents"),
143 _(
"Environmental Caution Area: Heavy icing"),
144 _(
"(reserved for future use)"),
145 _(
"Restricted Area: Fishing prohibited"),
146 _(
"Restricted Area: No anchoring."),
147 _(
"Restricted Area: Entry approval required prior to transit"),
148 _(
"Restricted Area: Entry prohibited"),
149 _(
"Restricted Area: Active military OPAREA"),
150 _(
"Restricted Area: Firing - danger area."),
151 _(
"Restricted Area: Drifting Mines"),
152 _(
"(reserved for future use)"),
153 _(
"Anchorage Area: Anchorage open"),
154 _(
"Anchorage Area: Anchorage closed"),
155 _(
"Anchorage Area: Anchoring prohibited"),
156 _(
"Anchorage Area: Deep draft anchorage"),
157 _(
"Anchorage Area: Shallow draft anchorage"),
158 _(
"Anchorage Area: Vessel transfer operations"),
159 _(
"(reserved for future use)"),
160 _(
"(reserved for future use)"),
161 _(
"(reserved for future use)"),
162 _(
"(reserved for future use)"),
163 _(
"(reserved for future use)"),
164 _(
"(reserved for future use)"),
165 _(
"(reserved for future use)"),
166 _(
"(reserved for future use)"),
167 _(
"(reserved for future use)"),
168 _(
"(reserved for future use)"),
169 _(
"Security Alert - Level 1"),
170 _(
"Security Alert - Level 2"),
171 _(
"Security Alert - Level 3"),
172 _(
"(reserved for future use)"),
173 _(
"(reserved for future use)"),
174 _(
"(reserved for future use)"),
175 _(
"(reserved for future use)"),
176 _(
"(reserved for future use)"),
177 _(
"Distress Area: Vessel disabled and adrift"),
178 _(
"Distress Area: Vessel sinking"),
179 _(
"Distress Area: Vessel abandoning ship"),
180 _(
"Distress Area: Vessel requests medical assistance"),
181 _(
"Distress Area: Vessel flooding"),
182 _(
"Distress Area: Vessel fire/explosion"),
183 _(
"Distress Area: Vessel grounding"),
184 _(
"Distress Area: Vessel collision"),
185 _(
"Distress Area: Vessel listing/capsizing"),
186 _(
"Distress Area: Vessel under assault"),
187 _(
"Distress Area: Person overboard"),
188 _(
"Distress Area: SAR area"),
189 _(
"Distress Area: Pollution response area"),
190 _(
"(reserved for future use)"),
191 _(
"(reserved for future use)"),
192 _(
"(reserved for future use)"),
193 _(
"Instruction: Contact VTS at this point/juncture"),
194 _(
"Instruction: Contact Port Administration at this "
196 _(
"Instruction: Do not proceed beyond this point/juncture"),
197 _(
"Instruction: Await instructions prior to proceeding beyond this "
199 _(
"Proceed to this location - await instructions"),
200 _(
"Clearance granted - proceed to berth"),
201 _(
"(reserved for future use)"),
202 _(
"(reserved for future use)"),
203 _(
"Information: Pilot boarding position"),
204 _(
"Information: Icebreaker waiting area"),
205 _(
"Information: Places of refuge"),
206 _(
"Information: Position of icebreakers"),
207 _(
"Information: Location of response units"),
208 _(
"VTS active target"),
209 _(
"Rogue or suspicious vessel"),
210 _(
"Vessel requesting non-distress assistance"),
211 _(
"Chart Feature: Sunken vessel"),
212 _(
"Chart Feature: Submerged object"),
213 _(
"Chart Feature: Semi-submerged object"),
214 _(
"Chart Feature: Shoal area"),
215 _(
"Chart Feature: Shoal area due north"),
216 _(
"Chart Feature: Shoal area due east"),
217 _(
"Chart Feature: Shoal area due south"),
218 _(
"Chart Feature: Shoal area due west"),
219 _(
"Chart Feature: Channel obstruction"),
220 _(
"Chart Feature: Reduced vertical clearance"),
221 _(
"Chart Feature: Bridge closed"),
222 _(
"Chart Feature: Bridge partially open"),
223 _(
"Chart Feature: Bridge fully open"),
224 _(
"(reserved for future use)"),
225 _(
"(reserved for future use)"),
226 _(
"(reserved for future use)"),
227 _(
"Report from ship: Icing info"),
228 _(
"(reserved for future use)"),
229 _(
"Report from ship: Miscellaneous information - define in "
230 "Associated text field"),
231 _(
"(reserved for future use)"),
232 _(
"(reserved for future use)"),
233 _(
"(reserved for future use)"),
234 _(
"(reserved for future use)"),
235 _(
"(reserved for future use)"),
236 _(
"Route: Recommended route"),
237 _(
"Route: Alternative route"),
238 _(
"Route: Recommended route through ice"),
239 _(
"(reserved for future use)"),
240 _(
"(reserved for future use)"),
241 _(
"Other - Define in associated text field"),
242 _(
"Cancellation - cancel area as identified by Message Linkage "
244 _(
"Undefined (default)")
248 double rlon, wxPoint *r) {
250 return cp->GetCanvasPointPix(rlat, rlon, r);
252 *r = vp.GetPixFromLL(rlat, rlon);
256static wxPoint transrot(wxPoint pt,
float sin_theta,
float cos_theta,
257 wxPoint offset = wxPoint(0, 0)) {
259 float px = (float)(pt.x * sin_theta) + (float)(pt.y * cos_theta);
260 float py = (float)(pt.y * sin_theta) - (float)(pt.x * cos_theta);
261 ret.x = (int)wxRound(px);
262 ret.y = (int)wxRound(py);
269static void transrot_pts(
int n, wxPoint *pt,
float sin_theta,
float cos_theta,
270 wxPoint offset = wxPoint(0, 0)) {
271 for (
int i = 0; i < n; i++)
272 pt[i] = transrot(pt[i], sin_theta, cos_theta, offset);
276 if (cp == NULL)
return;
277 if (!g_pAIS || !cp->GetShowAIS() || !g_bShowAreaNotices)
return;
279 wxDateTime now = wxDateTime::Now();
282 bool b_pens_set =
false;
288 wxBrush *yellow_brush = wxTheBrushList->FindOrCreateBrush(
289 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
290 wxBrush *green_brush = wxTheBrushList->FindOrCreateBrush(
291 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
295 float vp_scale = vp.view_scale_ppm;
297 for (
const auto &target : g_pAIS->GetAreaNoticeSourcesList()) {
298 auto target_data = target.second;
299 if (!target_data->area_notices.empty()) {
301 pen_save = dc.GetPen();
302 brush_save = dc.GetBrush();
304 yellow = GetGlobalColor(_T (
"YELO1" ));
305 yellow.Set(yellow.Red(), yellow.Green(), yellow.Blue(), 64);
307 green = GetGlobalColor(_T (
"GREEN4" ));
308 green.Set(green.Red(), green.Green(), green.Blue(), 64);
310 pen.SetColour(yellow);
313 yellow_brush = wxTheBrushList->FindOrCreateBrush(
314 yellow, wxBRUSHSTYLE_CROSSDIAG_HATCH);
316 wxTheBrushList->FindOrCreateBrush(green, wxBRUSHSTYLE_TRANSPARENT);
317 brush = yellow_brush;
322 for (
auto &ani : target_data->area_notices) {
325 if (area_notice.expiry_time > now) {
326 std::vector<wxPoint> points;
327 bool draw_polygon =
false;
329 switch (area_notice.notice_type) {
331 pen.SetColour(green);
335 pen.SetColour(yellow);
336 brush = yellow_brush;
339 pen.SetColour(yellow);
340 brush = yellow_brush;
345 for (Ais8_001_22_SubAreaList::iterator sa =
346 area_notice.sub_areas.begin();
347 sa != area_notice.sub_areas.end(); ++sa) {
349 case AIS8_001_22_SHAPE_CIRCLE: {
350 wxPoint target_point;
351 GetCanvasPointPix(vp, cp, sa->latitude, sa->longitude,
353 points.push_back(target_point);
354 if (sa->radius_m > 0.0)
355 dc.DrawCircle(target_point, sa->radius_m * vp_scale);
358 case AIS8_001_22_SHAPE_POLYGON:
361 case AIS8_001_22_SHAPE_POLYLINE: {
362 double lat = sa->latitude;
363 double lon = sa->longitude;
364 for (
int i = 0; i < 4; ++i) {
365 ll_gc_ll(lat, lon, sa->angles[i], sa->dists_m[i] / 1852.0,
367 wxPoint target_point;
368 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
369 points.push_back(target_point);
374 if (draw_polygon) dc.DrawPolygon(points.size(), &points.front());
382 dc.SetBrush(brush_save);
386static void TargetFrame(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
388 int gap2 = 2 * radius / 6;
390 wxPen pen_save = dc.GetPen();
394 dc.DrawLine(x - radius, y + gap2, x - radius, y + radius);
395 dc.DrawLine(x - radius, y + radius, x - gap2, y + radius);
396 dc.DrawLine(x + gap2, y + radius, x + radius, y + radius);
397 dc.DrawLine(x + radius, y + radius, x + radius, y + gap2);
398 dc.DrawLine(x + radius, y - gap2, x + radius, y - radius);
399 dc.DrawLine(x + radius, y - radius, x + gap2, y - radius);
400 dc.DrawLine(x - gap2, y - radius, x - radius, y - radius);
401 dc.DrawLine(x - radius, y - radius, x - radius, y - gap2);
406static void AtoN_Diamond(
ocpnDC &dc,
int x,
int y,
int radius,
409 wxPen pen_save = dc.GetPen();
412 wxPen aton_WhiteBorderPen;
415 int rad1a = radius / 2;
416 int rad2a = radius / 4;
422 if ((td->NavStatus == ATON_VIRTUAL_OFFPOSITION) ||
423 (td->NavStatus == ATON_REAL_OFFPOSITION))
424 aton_DrawPen = wxPen(GetGlobalColor(_T (
"URED" )), 2);
426 aton_DrawPen = wxPen(GetGlobalColor(_T (
"UBLCK" )), 2);
428 bool b_virt = (td->NavStatus == ATON_VIRTUAL) |
429 (td->NavStatus == ATON_VIRTUAL_ONPOSITION) |
430 (td->NavStatus == ATON_VIRTUAL_OFFPOSITION);
432 if (b_virt) aton_DrawPen.SetStyle(wxPENSTYLE_SHORT_DASH);
434 aton_WhiteBorderPen =
435 wxPen(GetGlobalColor(_T (
"CHWHT" )), aton_DrawPen.GetWidth() + 2);
440 diamond[0] = wxPoint(radius, 0);
441 diamond[1] = wxPoint(0, -radius);
442 diamond[2] = wxPoint(-radius, 0);
443 diamond[3] = wxPoint(0, radius);
444 diamond[4] = wxPoint(radius, 0);
445 dc.SetPen(aton_WhiteBorderPen);
446 dc.DrawLines(5, diamond, x, y);
447 dc.SetPen(aton_DrawPen);
448 dc.DrawLines(5, diamond, x, y);
450 aton_DrawPen = wxPen(GetGlobalColor(_T (
"UBLCK" )),
452 aton_WhiteBorderPen =
453 wxPen(GetGlobalColor(_T (
"CHWHT" )), aton_DrawPen.GetWidth() + 2);
457 cross[0] = wxPoint(-rad2a, 0);
458 cross[1] = wxPoint(rad2a, 0);
459 cross[2] = wxPoint(0, 0);
460 cross[3] = wxPoint(0, rad2a);
461 cross[4] = wxPoint(0, -rad2a);
462 dc.SetPen(aton_WhiteBorderPen);
463 dc.DrawLines(5, cross, x, y);
464 dc.SetPen(aton_DrawPen);
465 dc.DrawLines(5, cross, x, y);
467 wxPoint TriPointUp[4];
468 TriPointUp[0] = wxPoint(-rad1a, 0);
469 TriPointUp[1] = wxPoint(rad1a, 0);
470 TriPointUp[2] = wxPoint(0, -rad1a);
471 TriPointUp[3] = wxPoint(-rad1a, 0);
473 wxPoint TriPointDown[4];
474 TriPointDown[0] = wxPoint(-rad1a, -rad1a);
475 TriPointDown[1] = wxPoint(rad1a, -rad1a);
476 TriPointDown[2] = wxPoint(0, 0);
477 TriPointDown[3] = wxPoint(-rad1a, -rad1a);
479 wxPoint CircleOpen[16];
480 CircleOpen[0] = wxPoint(-1, 5);
481 CircleOpen[1] = wxPoint(1, 5);
482 CircleOpen[2] = wxPoint(3, 4);
483 CircleOpen[3] = wxPoint(4, 3);
484 CircleOpen[4] = wxPoint(5, 1);
485 CircleOpen[5] = wxPoint(5, -1);
486 CircleOpen[6] = wxPoint(4, -3);
487 CircleOpen[7] = wxPoint(3, -4);
488 CircleOpen[8] = wxPoint(1, -5);
489 CircleOpen[9] = wxPoint(-1, -5);
490 CircleOpen[10] = wxPoint(-3, -4);
491 CircleOpen[11] = wxPoint(-4, -3);
492 CircleOpen[12] = wxPoint(-5, -1);
493 CircleOpen[13] = wxPoint(-4, 3);
494 CircleOpen[14] = wxPoint(-3, 4);
495 CircleOpen[15] = wxPoint(-1, 5);
497 switch (td->ShipType) {
500 dc.SetPen(aton_WhiteBorderPen);
501 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
502 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
503 dc.SetPen(aton_DrawPen);
504 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
505 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
509 dc.SetPen(aton_WhiteBorderPen);
510 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
511 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
512 dc.SetPen(aton_DrawPen);
513 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
514 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
518 dc.SetPen(aton_WhiteBorderPen);
519 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
520 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
521 dc.SetPen(aton_DrawPen);
522 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
523 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
527 dc.SetPen(aton_WhiteBorderPen);
528 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
529 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
530 dc.SetPen(aton_DrawPen);
531 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
532 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
537 aRect[0] = wxPoint(-rad3a, 0);
538 aRect[1] = wxPoint(-rad3a, -rad3a - rad3a);
539 aRect[2] = wxPoint(rad3a, -rad3a - rad3a);
540 aRect[3] = wxPoint(rad3a, 0);
541 aRect[4] = wxPoint(-rad3a, 0);
542 dc.SetPen(aton_WhiteBorderPen);
543 dc.DrawLines(5, aRect, x, y - radius - 1);
544 dc.SetPen(aton_DrawPen);
545 dc.DrawLines(5, aRect, x, y - radius - 1);
549 dc.SetPen(aton_WhiteBorderPen);
550 dc.DrawLines(4, TriPointUp, x, y - radius);
551 dc.SetPen(aton_DrawPen);
552 dc.DrawLines(4, TriPointUp, x, y - radius);
556 dc.SetPen(aton_WhiteBorderPen);
557 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
558 dc.SetPen(aton_DrawPen);
559 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
560 dc.SetPen(aton_WhiteBorderPen);
561 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
562 dc.SetPen(aton_DrawPen);
563 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
567 dc.SetPen(aton_WhiteBorderPen);
568 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
569 dc.SetPen(aton_DrawPen);
570 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
574 cross[0] = wxPoint(-rad2a, -rad2a);
575 cross[1] = wxPoint(rad2a, rad2a);
576 cross[2] = wxPoint(0, 0);
577 cross[3] = wxPoint(-rad2a, rad2a);
578 cross[4] = wxPoint(rad2a, -rad2a);
579 dc.SetPen(aton_WhiteBorderPen);
580 dc.DrawLines(5, cross, x, y - radius - rad3a);
581 dc.SetPen(aton_DrawPen);
582 dc.DrawLines(5, cross, x, y - radius - rad3a);
590static void Base_Square(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
592 int gap2 = 2 * radius / 6;
593 int pen_width = pen.GetWidth();
595 wxPen pen_save = dc.GetPen();
599 dc.DrawLine(x - radius, y - radius, x - radius, y + radius);
600 dc.DrawLine(x - radius, y + radius, x + radius, y + radius);
601 dc.DrawLine(x + radius, y + radius, x + radius, y - radius);
602 dc.DrawLine(x + radius, y - radius, x - radius, y - radius);
606 pen.SetWidth(pen_width);
609 dc.DrawLine(x - gap2, y, x + gap2, y);
610 dc.DrawLine(x, y - gap2, x, y + gap2);
615static void SART_Render(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
617 int gap = (radius * 12) / 10;
618 int pen_width = pen.GetWidth();
620 wxPen pen_save = dc.GetPen();
624 wxBrush brush_save = dc.GetBrush();
625 wxBrush *ppBrush = wxTheBrushList->FindOrCreateBrush(
626 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
627 dc.SetBrush(*ppBrush);
629 dc.DrawCircle(x, y, radius);
633 pen.SetWidth(pen_width);
636 dc.DrawLine(x - gap, y - gap, x + gap, y + gap);
637 dc.DrawLine(x - gap, y + gap, x + gap, y - gap);
639 dc.SetBrush(brush_save);
645static void spherical_ll_gc_ll(
float lat,
float lon,
float brg,
float dist,
646 float *dlat,
float *dlon) {
647 float angr = brg / 180 * M_PI;
648 float latr = lat * M_PI / 180;
649 float D = dist / 3443;
650 float sD = sinf(D), cD = cosf(D);
651 float sy = sinf(latr), cy = cosf(latr);
652 float sa = sinf(angr), ca = cosf(angr);
654 *dlon = lon + asinf(sa * sD / cy) * 180 / M_PI;
655 *dlat = asinf(sy * cD + cy * sD * ca) * 180 / M_PI;
659float AIS_scale_factor;
660float AIS_nominal_target_size_mm;
661float AIS_nominal_icon_size_pixels;
663float AIS_user_scale_factor;
664double AIS_nominal_line_width_pix;
666float AIS_width_interceptbar_base;
667float AIS_width_interceptbar_top;
668float AIS_intercept_bar_circle_diameter;
669float AIS_width_interceptline;
670float AIS_width_cogpredictor_base;
671float AIS_width_cogpredictor_line;
672float AIS_width_target_outline;
673float AIS_icon_diameter;
676static void AISSetMetrics() {
677 AIS_scale_factor = 1.0;
679 double DPIscale = 1.0;
680 DPIscale = g_Platform->GetDisplayDIPMult(gFrame);
690 AIS_nominal_target_size_mm = 30.0 / g_Platform->GetDisplayDPmm();
694 AIS_nominal_target_size_mm = wxMin(AIS_nominal_target_size_mm, 10.0);
695 AIS_nominal_target_size_mm = wxMax(AIS_nominal_target_size_mm, 5.0);
697 AIS_nominal_icon_size_pixels =
698 wxMax(4.0, g_Platform->GetDisplayDPmm() *
699 AIS_nominal_target_size_mm);
701 AIS_pix_factor = AIS_nominal_icon_size_pixels /
704 AIS_scale_factor *= AIS_pix_factor;
706 AIS_user_scale_factor = g_ShipScaleFactorExp;
707 if (g_ShipScaleFactorExp > 1.0)
708 AIS_user_scale_factor = (log(g_ShipScaleFactorExp) + 1.0) *
711 AIS_scale_factor *= AIS_user_scale_factor;
715 AIS_nominal_line_width_pix =
716 wxMax(1.5, g_Platform->GetDisplayDPmm() / (2.0 / DPIscale));
719 AIS_width_interceptbar_base = 3 * AIS_nominal_line_width_pix;
720 AIS_width_interceptbar_top = 1.5 * AIS_nominal_line_width_pix;
721 AIS_intercept_bar_circle_diameter = 3.5 * AIS_nominal_line_width_pix;
722 AIS_width_interceptline = 2 * AIS_nominal_line_width_pix;
723 AIS_width_cogpredictor_base = 3 * AIS_nominal_line_width_pix;
724 AIS_width_cogpredictor_line = 1.3 * AIS_nominal_line_width_pix;
725 AIS_width_target_outline = 1.4 * AIS_nominal_line_width_pix;
726 AIS_icon_diameter = AIS_intercept_bar_circle_diameter * AIS_user_scale_factor;
728 wxFont *font =FontMgr::Get().GetFont(_(
"AIS Target Name"), 12);
729 double scaler = DPIscale;
732 FindOrCreateFont_PlugIn(font->GetPointSize() / scaler,
733 font->GetFamily(), font->GetStyle(),
734 font->GetWeight(),
false,
735 font->GetFaceName());
743 if (NULL == td)
return;
746 if (td->b_lost)
return;
751 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts) &&
752 (td->NavStatus != NOT_UNDER_COMMAND) &&
753 ((td->Class == AIS_CLASS_A) || (td->Class == AIS_CLASS_B)))
757 if (!td->b_positionOnceValid)
return;
760 if (td->b_OwnShip)
return;
763 float target_sog = td->SOG;
764 if ((td->SOG > 102.2) && !td->b_SarAircraftPosnReport) target_sog = 0.;
767 wxPoint TargetPoint, PredPoint;
770 if (td->n_alert_state == AIS_ALERT_SET)
774 if (vp.GetBBox().Contains(td->Lat, td->Lon))
778 if (1 && td->b_show_track) {
779 if (td->m_ptrack.size() > 0) {
781 if (vp.GetBBox().Contains(ptrack_point.m_lat, ptrack_point.m_lon))
789 float pred_lat, pred_lon;
790 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
791 target_sog * g_ShowCOG_Mins / 60., &pred_lat, &pred_lon);
794 if (vp.GetBBox().Contains(pred_lat, pred_lon))
798 box.SetFromSegment(td->Lat, td->Lon, pred_lat, pred_lon);
801 if (!vp.GetBBox().IntersectOut(box)) drawit++;
807 GetCanvasPointPix(vp, cp, td->Lat, td->Lon, &TargetPoint);
808 GetCanvasPointPix(vp, cp, pred_lat, pred_lon, &PredPoint);
810 bool b_hdgValid =
true;
812 float theta = (float)-PI / 2.;
814 if ((
int)(td->HDG) != 511) {
815 theta = ((td->HDG - 90) * PI / 180.) + vp.rotation;
819 if (!g_bInlandEcdis) {
824 float angle_distance_nm = (100. / vp.view_scale_ppm) / 1852.;
825 float angle_lat, angle_lon;
826 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG, angle_distance_nm,
827 &angle_lat, &angle_lon);
830 GetCanvasPointPix(vp, cp, angle_lat, angle_lon, &AnglePoint);
832 if (abs(AnglePoint.x - TargetPoint.x) > 0) {
833 if (target_sog > g_ShowMoored_Kts) {
834 theta = atan2f((
double)(AnglePoint.y - TargetPoint.y),
835 (
double)(AnglePoint.x - TargetPoint.x));
838 theta = (float)-PI / 2.;
840 if (AnglePoint.y > TargetPoint.y)
841 theta = (float)PI / 2.;
845 if (td->SOG >= g_ShowMoored_Kts)
854 float sin_theta = sinf(theta), cos_theta = cosf(theta);
857 dash_long[0] = (int)(1.0 * gFrame->GetPrimaryCanvas()
860 (int)(0.5 * gFrame->GetPrimaryCanvas()->GetPixPerMM());
862 int targetscale = 100;
865 idxCC = cp->m_canvasIndex;
867 if (idxCC > AIS_TARGETDATA_MAX_CANVAS - 1)
870 if (cp->GetAttenAIS()) {
871 if (td->NavStatus <= 15) {
875 if (td->importance < AISImportanceSwitchPoint)
876 targetscale = td->last_scale[idxCC] - 2;
878 if (td->importance > AISImportanceSwitchPoint)
879 targetscale = td->last_scale[idxCC] + 5;
880 if (targetscale > 100) targetscale = 100;
881 if (targetscale < 50) targetscale = 50;
882 td->last_scale[idxCC] = targetscale;
888 wxPoint ais_real_size[6];
889 bool bcan_draw_size =
true;
890 if (g_bDrawAISSize) {
891 if (td->DimA + td->DimB == 0 || td->DimC + td->DimD == 0) {
892 bcan_draw_size =
false;
894 double ref_lat, ref_lon;
895 ll_gc_ll(td->Lat, td->Lon, 0, 100. / 1852., &ref_lat, &ref_lon);
896 wxPoint2DDouble b_point = vp.GetDoublePixFromLL(td->Lat, td->Lon);
897 wxPoint2DDouble r_point = vp.GetDoublePixFromLL(ref_lat, ref_lon);
898 double ppm = r_point.GetDistance(b_point) / 100.;
899 double offwid = (td->DimC + td->DimD) * ppm * 0.25;
900 double offlen = (td->DimA + td->DimB) * ppm * 0.15;
901 ais_real_size[0].x = -td->DimD * ppm;
902 ais_real_size[0].y = -td->DimB * ppm;
903 ais_real_size[1].x = -td->DimD * ppm;
904 ais_real_size[1].y = td->DimA * ppm - offlen;
905 ais_real_size[2].x = -td->DimD * ppm + offwid;
906 ais_real_size[2].y = td->DimA * ppm;
907 ais_real_size[3].x = td->DimC * ppm - offwid;
908 ais_real_size[3].y = td->DimA * ppm;
909 ais_real_size[4].x = td->DimC * ppm;
910 ais_real_size[4].y = td->DimA * ppm - offlen;
911 ais_real_size[5].x = td->DimC * ppm;
912 ais_real_size[5].y = -td->DimB * ppm;
914 if (ais_real_size[4].x - ais_real_size[0].x < 16 ||
915 ais_real_size[2].y - ais_real_size[0].y < 30)
916 bcan_draw_size =
false;
918 bcan_draw_size =
true;
919 transrot_pts(6, ais_real_size, sin_theta, cos_theta);
926 wxPoint ais_quad_icon[4] = {wxPoint(-8, -6), wxPoint(0, 24), wxPoint(8, -6),
928 wxPoint ais_octo_icon[8] = {wxPoint(4, 8), wxPoint(8, 4), wxPoint(8, -4),
929 wxPoint(4, -8), wxPoint(-4, -8), wxPoint(-8, -4),
930 wxPoint(-8, 4), wxPoint(-4, 8)};
932 if (!g_bInlandEcdis) {
934 if (targetscale == 50) {
935 ais_quad_icon[0] = wxPoint(-4, -3);
936 ais_quad_icon[1] = wxPoint(0, 12);
937 ais_quad_icon[2] = wxPoint(4, -3);
938 ais_quad_icon[3] = wxPoint(0, -3);
939 }
else if (targetscale != 100) {
941 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
942 ais_quad_icon[1] = wxPoint(0, (
int)24 * targetscale / 100);
944 wxPoint((
int)8 * targetscale / 100, (
int)-6 * targetscale / 100);
945 ais_quad_icon[3] = wxPoint(0, (
int)-6 * targetscale / 100);
949 if (td->Class == AIS_CLASS_B) ais_quad_icon[3].y = 0;
951 if ((td->Class == AIS_GPSG_BUDDY) || (td->b_isFollower)) {
952 ais_quad_icon[0] = wxPoint(-5, -12);
953 ais_quad_icon[1] = wxPoint(-3, 12);
954 ais_quad_icon[2] = wxPoint(3, 12);
955 ais_quad_icon[3] = wxPoint(5, -12);
956 }
else if (td->Class == AIS_DSC) {
957 ais_quad_icon[0].y = 0;
958 ais_quad_icon[1].y = 8;
959 ais_quad_icon[2].y = 0;
960 ais_quad_icon[3].y = -8;
961 }
else if (td->Class == AIS_APRS) {
962 ais_quad_icon[0] = wxPoint(-8, -8);
963 ais_quad_icon[1] = wxPoint(-8, 8);
964 ais_quad_icon[2] = wxPoint(8, 8);
965 ais_quad_icon[3] = wxPoint(8, -8);
968 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
971 iconPoints = ais_quad_icon;
975 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
977 iconPoints = ais_quad_icon;
980 iconPoints = ais_octo_icon;
984 wxColour UBLCK = GetGlobalColor(_T (
"UBLCK" ));
985 dc.SetPen(wxPen(UBLCK));
988 wxColour UINFG = GetGlobalColor(_T (
"UINFG" ));
989 wxBrush target_brush = wxBrush(UINFG);
992 if (td->b_isEuroInland && !g_bInlandEcdis)
993 target_brush = wxBrush(GetGlobalColor(_T (
"TEAL1" )));
996 if (td->b_nameFromCache)
997 target_brush = wxBrush(GetGlobalColor(_T (
"GREEN5" )));
1000 wxColour URED = GetGlobalColor(_T (
"URED" ));
1001 if (!td->b_nameValid) target_brush = wxBrush(GetGlobalColor(_T (
"CHYLW" )));
1003 if ((td->Class == AIS_DSC) && ((td->ShipType == 12) ||
1004 (td->ShipType == 16)) )
1005 target_brush = wxBrush(URED);
1007 if (td->b_SarAircraftPosnReport) target_brush = wxBrush(UINFG);
1009 if ((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid))
1010 target_brush = wxBrush(URED);
1012 if ((td->n_alert_state == AIS_ALERT_NO_DIALOG_SET) &&
1014 (!td->b_isFollower))
1015 target_brush = wxBrush(URED);
1017 if (td->b_positionDoubtful)
1018 target_brush = wxBrush(GetGlobalColor(_T (
"UINFF" )));
1020 wxPen target_outline_pen(UBLCK, AIS_width_target_outline);
1023 if (((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid)) ||
1024 (td->b_show_AIS_CPA && (td->bCPA_Valid))) {
1026 double tcpa_lat, tcpa_lon;
1027 ll_gc_ll(td->Lat, td->Lon, td->COG, target_sog * td->TCPA / 60., &tcpa_lat,
1030 wxPoint TPoint = TargetPoint;
1031 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1034 ClipResult res = cohen_sutherland_line_clip_i(
1035 &TPoint.x, &TPoint.y, &tCPAPoint.x, &tCPAPoint.y, 0, vp.pix_width, 0,
1038 if (res != Invisible) {
1039 wxPen ppPen2(URED, AIS_width_cogpredictor_line, wxPENSTYLE_USER_DASH);
1040 ppPen2.SetDashes(2, dash_long);
1043 dc.StrokeLine(TPoint.x, TPoint.y, tCPAPoint.x, tCPAPoint.y);
1047 double ocpa_lat, ocpa_lon;
1050 if (std::isnan(gCog) || std::isnan(gSog)) {
1054 ll_gc_ll(gLat, gLon, gCog, gSog * td->TCPA / 60., &ocpa_lat, &ocpa_lon);
1059 GetCanvasPointPix(vp, cp, ocpa_lat, ocpa_lon, &oCPAPoint);
1060 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1063 wxPoint oCPAPoint_unclipped = oCPAPoint;
1064 wxPoint tCPAPoint_unclipped = tCPAPoint;
1067 ClipResult ores = cohen_sutherland_line_clip_i(
1068 &tCPAPoint.x, &tCPAPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0, vp.pix_width,
1071 if (ores != Invisible) {
1072 wxColour yellow = GetGlobalColor(_T (
"YELO1" ));
1073 dc.SetPen(wxPen(yellow, AIS_width_interceptbar_base));
1074 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1076 wxPen ppPen2(URED, AIS_width_interceptbar_top, wxPENSTYLE_USER_DASH);
1077 ppPen2.SetDashes(2, dash_long);
1079 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1082 wxBrush br(GetGlobalColor(_T (
"BLUE3" )));
1084 dc.SetPen(wxPen(UBLCK, AIS_width_target_outline));
1087 dc.StrokeCircle(tCPAPoint_unclipped.x, tCPAPoint_unclipped.y,
1088 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1093 GetCanvasPointPix(vp, cp, gLat, gLon, &oShipPoint);
1094 oCPAPoint = oCPAPoint_unclipped;
1096 ClipResult ownres = cohen_sutherland_line_clip_i(
1097 &oShipPoint.x, &oShipPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0,
1098 vp.pix_width, 0, vp.pix_height);
1100 if (ownres != Invisible) {
1101 wxPen ppPen2(URED, AIS_width_interceptline, wxPENSTYLE_USER_DASH);
1102 ppPen2.SetDashes(2, dash_long);
1105 dc.StrokeLine(oShipPoint.x, oShipPoint.y, oCPAPoint.x, oCPAPoint.y);
1108 dc.SetPen(wxPen(UBLCK));
1109 dc.SetBrush(wxBrush(URED));
1115 if (g_pais_alert_dialog_active && g_pais_alert_dialog_active->IsShown() &&
1117 if (g_pais_alert_dialog_active->Get_Dialog_MMSI() == td->MMSI)
1118 cp->JaggyCircle(dc, wxPen(URED, 2), TargetPoint.x, TargetPoint.y, 100);
1123 if (g_pais_query_dialog_active && g_pais_query_dialog_active->IsShown()) {
1124 if (g_pais_query_dialog_active->GetMMSI() == td->MMSI)
1125 TargetFrame(dc, wxPen(UBLCK, 2), TargetPoint.x, TargetPoint.y, 25);
1130 if ((g_bShowCOG) && (target_sog > g_ShowMoored_Kts) && td->b_active) {
1131 int pixx = TargetPoint.x;
1132 int pixy = TargetPoint.y;
1133 int pixx1 = PredPoint.x;
1134 int pixy1 = PredPoint.y;
1138 float l = sqrtf(powf((
float)(PredPoint.x - TargetPoint.x), 2) +
1139 powf((
float)(PredPoint.y - TargetPoint.y), 2));
1142 ClipResult res = cohen_sutherland_line_clip_i(
1143 &pixx, &pixy, &pixx1, &pixy1, 0, vp.pix_width, 0, vp.pix_height);
1145 if (res != Invisible) {
1147 if (targetscale >= 75) {
1148 wxPen wide_pen(target_brush.GetColour(), AIS_width_cogpredictor_base);
1149 dc.SetPen(wide_pen);
1150 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1153 if (AIS_width_cogpredictor_base > 1) {
1155 wxPen narrow_pen(UBLCK, AIS_width_cogpredictor_line);
1156 if (targetscale < 75) {
1157 narrow_pen.SetWidth(1);
1158 narrow_pen.SetStyle(wxPENSTYLE_USER_DASH);
1162 narrow_pen.SetDashes(2, dash_dot);
1164 dc.SetPen(narrow_pen);
1165 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1169 dc.SetBrush(target_brush);
1170 dc.StrokeCircle(PredPoint.x, PredPoint.y, 5* targetscale / 100);
1175#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1178 glTranslated(PredPoint.x, PredPoint.y, 0);
1179 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1181 float points[] = {0.0f, 5.0f, 2.5f, 4.330127f, 4.330127f,
1182 2.5f, 5.0f, 0, 4.330127f, -2.5f,
1183 2.5f, -4.330127f, 0, -5.1f, -2.5f,
1184 -4.330127f, -4.330127f, -2.5f, -5.0f, 0,
1185 -4.330127f, 2.5f, -2.5f, 4.330127f, 0,
1187 if (targetscale <= 75) {
1188 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1190 points[i] = points[i] / 2;
1193 wxColour c = target_brush.GetColour();
1194 glColor3ub(c.Red(), c.Green(), c.Blue());
1196 glBegin(GL_TRIANGLE_FAN);
1197 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1199 glVertex2i(points[i], points[i + 1]);
1202 glColor3ub(0, 0, 0);
1203 glLineWidth(AIS_width_target_outline);
1204 glBegin(GL_LINE_LOOP);
1205 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1207 glVertex2i(points[i], points[i + 1]);
1212 dc.SetBrush(target_brush);
1213 dc.StrokeCircle(PredPoint.x, PredPoint.y,
1214 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor* targetscale / 100);
1221 if ((td->ROTAIS != 0) && (td->ROTAIS != -128) && (!g_bShowScaled)) {
1222 float cog_angle = td->COG * PI / 180.;
1224 float theta2 = theta;
1225 if (td->SOG >= g_ShowMoored_Kts)
1226 theta2 = cog_angle - (PI / 2);
1230 theta2 += (float)PI / 2;
1232 theta2 -= (float)PI / 2;
1234 int xrot = (int)round(pixx1 + (nv * cosf(theta2)));
1235 int yrot = (int)round(pixy1 + (nv * sinf(theta2)));
1236 dc.StrokeLine(pixx1, pixy1, xrot, yrot);
1242 if (td->Class == AIS_ARPA) {
1243 wxPen target_pen(UBLCK, 2);
1245 dc.SetPen(target_pen);
1246 dc.SetBrush(target_brush);
1247 dc.StrokeCircle(TargetPoint.x, TargetPoint.y,
1248 1.4 * AIS_icon_diameter);
1249 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1);
1251 if (!td->b_active) {
1252 dc.SetPen(wxPen(UBLCK, 2));
1253 dc.StrokeLine(TargetPoint.x - 14, TargetPoint.y, TargetPoint.x + 14,
1255 dc.SetPen(wxPen(UBLCK, 1));
1257 }
else if (td->Class == AIS_ATON) {
1258 AtoN_Diamond(dc, TargetPoint.x, TargetPoint.y,
1259 2 * AIS_icon_diameter, td);
1260 }
else if (td->Class == AIS_BASE) {
1261 Base_Square(dc, wxPen(UBLCK, 2), TargetPoint.x, TargetPoint.y, 8);
1262 }
else if (td->Class == AIS_SART) {
1263 if (td->NavStatus == 14)
1264 SART_Render(dc, wxPen(URED, 2), TargetPoint.x, TargetPoint.y, 8);
1266 SART_Render(dc, wxPen(GetGlobalColor(_T (
"UGREN" )), 2), TargetPoint.x,
1269 }
else if (td->b_SarAircraftPosnReport) {
1270 int airtype = (td->MMSI % 1000) / 100;
1271 int ar = airtype == 5 ? 15 : 9;
1272 wxPoint SarIcon[15];
1276 SarIcon[0] = wxPoint(0, 9) * AIS_scale_factor * 1.4;
1277 SarIcon[1] = wxPoint(1, 1) * AIS_scale_factor * 1.4;
1278 SarIcon[2] = wxPoint(2, 1) * AIS_scale_factor * 1.4;
1279 SarIcon[3] = wxPoint(9, 8) * AIS_scale_factor * 1.4;
1280 SarIcon[4] = wxPoint(9, 7) * AIS_scale_factor * 1.4;
1281 SarIcon[5] = wxPoint(3, 0) * AIS_scale_factor * 1.4;
1282 SarIcon[6] = wxPoint(3, -5) * AIS_scale_factor * 1.4;
1283 SarIcon[7] = wxPoint(9, -12) * AIS_scale_factor * 1.4;
1284 SarIcon[8] = wxPoint(9, -13) * AIS_scale_factor * 1.4;
1285 SarIcon[9] = wxPoint(2, -5) * AIS_scale_factor * 1.4;
1286 SarIcon[10] = wxPoint(1, -15) * AIS_scale_factor * 1.4;
1287 SarIcon[11] = wxPoint(3, -16) * AIS_scale_factor * 1.4;
1288 SarIcon[12] = wxPoint(4, -18) * AIS_scale_factor * 1.4;
1289 SarIcon[13] = wxPoint(1, -18) * AIS_scale_factor * 1.4;
1290 SarIcon[14] = wxPoint(0, -19) * AIS_scale_factor * 1.4;
1292 SarIcon[0] = wxPoint(0, 12) * AIS_scale_factor;
1293 SarIcon[1] = wxPoint(4, 2) * AIS_scale_factor;
1294 SarIcon[2] = wxPoint(16, -2) * AIS_scale_factor;
1295 SarIcon[3] = wxPoint(16, -8) * AIS_scale_factor;
1296 SarIcon[4] = wxPoint(4, -8) * AIS_scale_factor;
1297 SarIcon[5] = wxPoint(3, -16) * AIS_scale_factor;
1298 SarIcon[6] = wxPoint(10, -18) * AIS_scale_factor;
1299 SarIcon[7] = wxPoint(10, -22) * AIS_scale_factor;
1300 SarIcon[8] = wxPoint(0, -22) * AIS_scale_factor;
1307 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1308 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1310 wxPen tri_pen(target_brush.GetColour(), 1);
1312 dc.SetBrush(target_brush);
1314 int mappings[7][3] = {{0, 1, 4}, {1, 2, 3}, {1, 3, 4}, {0, 4, 5},
1315 {0, 5, 8}, {5, 6, 7}, {5, 7, 8}};
1316 for (
int i = 0; i < 7; i++) {
1317 wxPoint ais_tri_icon[3];
1318 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1319 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1322 dc.SetPen(target_outline_pen);
1323 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1324 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1328 for (
int i = 0; i < ar; i++)
1330 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1332 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1335 dc.SetBrush(target_brush);
1337 for (
int i = 0; i < 7; i++) {
1338 wxPoint ais_tri_icon[3];
1339 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1340 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1343 dc.SetPen(target_outline_pen);
1344 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1345 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1348 if (!td->b_active) {
1349 dc.SetPen(wxPen(UBLCK, 3));
1350 dc.StrokeLine(TargetPoint.x - 16, TargetPoint.y, TargetPoint.x + 16,
1355 wxPen target_pen(UBLCK, 1);
1356 dc.SetPen(target_pen);
1358 wxPoint Point = TargetPoint;
1359 if (g_bDrawAISRealtime &&
1360 (td->Class == AIS_CLASS_A || td->Class == AIS_CLASS_B) &&
1361 td->SOG > g_AIS_RealtPred_Kts && td->SOG < 102.2) {
1362 wxDateTime now = wxDateTime::Now();
1364 int target_age = now.GetTicks() - td->PositionReportTicks;
1367 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
1368 td->SOG * target_age / 3600.0, &lat, &lon);
1370 GetCanvasPointPix(vp, cp, lat, lon, &Point);
1372 wxBrush realtime_brush = wxBrush(GetGlobalColor(
"GREY1"));
1373 dc.SetBrush(realtime_brush);
1374 dc.StrokePolygon(nPoints, iconPoints, Point.x, Point.y, AIS_scale_factor);
1376 dc.SetBrush(target_brush);
1379 dc.StrokePolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1384#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1386 wxColour c = target_brush.GetColour();
1387 glColor3ub(c.Red(), c.Green(), c.Blue());
1390 glTranslated(TargetPoint.x, TargetPoint.y, 0);
1391 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1393 glBegin(GL_TRIANGLE_FAN);
1396 glVertex2i(ais_quad_icon[3].x, ais_quad_icon[3].y);
1397 glVertex2i(ais_quad_icon[0].x, ais_quad_icon[0].y);
1398 glVertex2i(ais_quad_icon[1].x, ais_quad_icon[1].y);
1399 glVertex2i(ais_quad_icon[2].x, ais_quad_icon[2].y);
1401 for (
int i = 0; i < 8; i++) {
1402 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1407 glLineWidth(AIS_width_target_outline);
1409 glColor3ub(UBLCK.Red(), UBLCK.Green(), UBLCK.Blue());
1411 glBegin(GL_LINE_LOOP);
1412 for (
int i = 0; i < nPoints; i++)
1413 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1418 dc.SetPen(target_outline_pen);
1419 dc.DrawPolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1425 if (td->b_isFollower) {
1426 wxPoint ais_follow_stroke[3];
1427 ais_follow_stroke[0] = wxPoint(-3, -20) * AIS_scale_factor;
1428 ais_follow_stroke[1] = wxPoint(0, 0) * AIS_scale_factor;
1429 ais_follow_stroke[2] = wxPoint(3, -20) * AIS_scale_factor;
1431 transrot_pts(3, ais_follow_stroke, sin_theta, cos_theta);
1433 int penWidth = wxMax(target_outline_pen.GetWidth(), 2);
1434 dc.SetPen(wxPen(UBLCK, penWidth));
1435 dc.StrokeLine(ais_follow_stroke[0].x + TargetPoint.x,
1436 ais_follow_stroke[0].y + TargetPoint.y,
1437 ais_follow_stroke[1].x + TargetPoint.x,
1438 ais_follow_stroke[1].y + TargetPoint.y);
1439 dc.StrokeLine(ais_follow_stroke[1].x + TargetPoint.x,
1440 ais_follow_stroke[1].y + TargetPoint.y,
1441 ais_follow_stroke[2].x + TargetPoint.x,
1442 ais_follow_stroke[2].y + TargetPoint.y);
1445 if (g_bDrawAISSize && bcan_draw_size) {
1446 dc.SetPen(target_outline_pen);
1447 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1448 if (!g_bInlandEcdis) {
1449 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1452 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1457 dc.SetBrush(wxBrush(GetGlobalColor(_T (
"SHIPS" ))));
1458 int navstatus = td->NavStatus;
1462 if (((td->ShipType >= 40) && (td->ShipType < 50)) &&
1463 (navstatus == UNDERWAY_USING_ENGINE || td->Class == AIS_CLASS_B ))
1466 if (targetscale > 90) {
1467 switch (navstatus) {
1470 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1473 case RESTRICTED_MANOEUVRABILITY: {
1475 diamond[0] = wxPoint(4, 0) * AIS_scale_factor;
1476 diamond[1] = wxPoint(0, -6) * AIS_scale_factor;
1477 diamond[2] = wxPoint(-4, 0) * AIS_scale_factor;
1478 diamond[3] = wxPoint(0, 6) * AIS_scale_factor;
1479 dc.StrokePolygon(4, diamond, TargetPoint.x, TargetPoint.y - (11 * AIS_scale_factor));
1480 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1481 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - ( 22 * AIS_scale_factor ), 4 * AIS_scale_factor);
1485 case CONSTRAINED_BY_DRAFT: {
1486 wxPoint can[4] = {wxPoint(-3, 0) * AIS_scale_factor,
1487 wxPoint(3, 0) * AIS_scale_factor,
1488 wxPoint(3, -16) * AIS_scale_factor,
1489 wxPoint(-3, -16) * AIS_scale_factor };
1490 dc.StrokePolygon(4, can, TargetPoint.x, TargetPoint.y);
1493 case NOT_UNDER_COMMAND: {
1494 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1495 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9, 4 * AIS_scale_factor);
1500 tri[0] = wxPoint(-4, 0) * AIS_scale_factor;
1501 tri[1] = wxPoint(4, 0) * AIS_scale_factor;
1502 tri[2] = wxPoint(0, -9) * AIS_scale_factor;
1503 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1504 tri[0] = wxPoint(0, -9) * AIS_scale_factor;
1505 tri[1] = wxPoint(4, -18) * AIS_scale_factor;
1506 tri[2] = wxPoint(-4, -18) * AIS_scale_factor;
1507 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1511 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1512 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9, 4 * AIS_scale_factor);
1513 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 18, 4 * AIS_scale_factor);
1518 dc.SetBrush(target_brush);
1520 wxPoint arrow1[3] = {
1521 wxPoint(-4, 20) * AIS_scale_factor,
1522 wxPoint(0, 27) * AIS_scale_factor,
1523 wxPoint(4, 20) * AIS_scale_factor };
1524 transrot_pts(3, arrow1, sin_theta, cos_theta, TargetPoint);
1525 dc.StrokePolygon(3, arrow1);
1527 wxPoint arrow2[3] = {
1528 wxPoint(-4, 27) * AIS_scale_factor,
1529 wxPoint(0, 34) * AIS_scale_factor,
1530 wxPoint(4, 27) * AIS_scale_factor };
1531 transrot_pts(3, arrow2, sin_theta, cos_theta, TargetPoint);
1532 dc.StrokePolygon(3, arrow2);
1539 if (!td->b_active) {
1540 wxPoint p1 = transrot(wxPoint((
int)-14 * targetscale / 100, 0), sin_theta,
1541 cos_theta, TargetPoint);
1542 wxPoint p2 = transrot(wxPoint((
int)14 * targetscale / 100, 0), sin_theta,
1543 cos_theta, TargetPoint);
1545 dc.SetPen(wxPen(UBLCK, 2));
1546 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1553 if (td->blue_paddle && td->blue_paddle < 3) {
1554 wxPoint ais_flag_icon[4];
1557 if (g_bInlandEcdis) {
1559 ais_flag_icon[0] = wxPoint(-4, 4);
1560 ais_flag_icon[1] = wxPoint(-4, 11);
1561 ais_flag_icon[2] = wxPoint(-11, 11);
1562 ais_flag_icon[3] = wxPoint(-11, 4);
1563 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1565 ais_flag_icon[0] = wxPoint(TargetPoint.x - 4, TargetPoint.y + 4);
1566 ais_flag_icon[1] = wxPoint(TargetPoint.x - 4, TargetPoint.y - 3);
1567 ais_flag_icon[2] = wxPoint(TargetPoint.x + 3, TargetPoint.y - 3);
1568 ais_flag_icon[3] = wxPoint(TargetPoint.x + 3, TargetPoint.y + 4);
1571 dc.SetPen(wxPen(GetGlobalColor(_T (
"CHWHT" )), penWidth));
1575 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
1577 wxPoint((
int)-2 * targetscale / 100, (
int)18 * targetscale / 100);
1578 ais_flag_icon[2] = wxPoint((
int)-2 * targetscale / 100, 0);
1580 wxPoint((
int)-2 * targetscale / 100, (
int)-6 * targetscale / 100);
1581 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1583 if (targetscale < 100) penWidth = 1;
1584 dc.SetPen(wxPen(GetGlobalColor(_T (
"CHWHT" )), penWidth));
1586 if (td->blue_paddle == 1) {
1587 ais_flag_icon[1] = ais_flag_icon[0];
1588 ais_flag_icon[2] = ais_flag_icon[3];
1591 dc.SetBrush(wxBrush(GetGlobalColor(_T (
"UINFB" ))));
1592 dc.StrokePolygon(4, ais_flag_icon);
1596 if ((g_bShowAISName) && (targetscale > 75)) {
1597 int true_scale_display = (int)(floor(vp.chart_scale / 100.) * 100);
1598 if (true_scale_display <
1599 g_Show_Target_Name_Scale) {
1601 wxString tgt_name = td->GetFullName();
1602 tgt_name = tgt_name.substr(0, tgt_name.find(_T (
"Unknown" ), 0));
1604 if (tgt_name != wxEmptyString) {
1605 dc.SetFont(*AIS_NameFont);
1606 dc.SetTextForeground(FontMgr::Get().GetFontColor(_(
"AIS Target Name")));
1609 dc.GetTextExtent(_T(
"W"), &w, &h);
1610 h *= g_Platform->GetDisplayDIPMult(gFrame);
1611 w *= g_Platform->GetDisplayDIPMult(gFrame);
1613 if ((td->COG > 90) && (td->COG < 180))
1614 dc.DrawText(tgt_name, TargetPoint.x + w, TargetPoint.y - h);
1616 dc.DrawText(tgt_name, TargetPoint.x + w, TargetPoint.y );
1624 bool b_noshow =
false;
1625 bool b_forceshow =
false;
1626 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
1627 if (td->MMSI == g_MMSI_Props_Array[i]->MMSI) {
1629 if (TRACKTYPE_NEVER == props->TrackType) {
1632 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
1640 int TrackLength = td->m_ptrack.size();
1641 if (((!b_noshow && td->b_show_track) || b_forceshow) && (TrackLength > 1)) {
1643 int TrackPointCount;
1644 wxPoint *TrackPoints = 0;
1645 TrackPoints =
new wxPoint[TrackLength];
1646 auto it = td->m_ptrack.begin();
1647 for (TrackPointCount = 0;
1648 it != td->m_ptrack.end() && (TrackPointCount < TrackLength);
1649 TrackPointCount++, ++it) {
1651 GetCanvasPointPix(vp, cp, ptrack_point.m_lat, ptrack_point.m_lon,
1652 &TrackPoints[TrackPointCount]);
1655 wxColour c = GetGlobalColor(_T (
"CHMGD" ));
1656 dc.SetPen(wxPen(c, 1.5 * AIS_nominal_line_width_pix));
1660#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1664 glColor3ub(c.Red(), c.Green(), c.Blue());
1665 glBegin(GL_LINE_STRIP);
1667 for (TrackPointCount = 0; TrackPointCount < TrackLength;
1669 glVertex2i(TrackPoints[TrackPointCount].x,
1670 TrackPoints[TrackPointCount].y);
1675 dc.DrawLines(TrackPointCount, TrackPoints);
1678 dc.DrawLines(TrackPointCount, TrackPoints);
1683 dc.StrokeLines(TrackPointCount, TrackPoints);
1687 delete[] TrackPoints;
1693 if (!g_pAIS)
return;
1697 if (!cp->GetShowAIS())
return;
1702 const auto ¤t_targets = g_pAIS->GetTargetList();
1710 }
else if (cp->m_canvasIndex == 0) {
1715 for (
const auto &it : current_targets) {
1717 auto td = it.second;
1718 double So, Cpa, Rang, Siz = 0.0;
1719 So = g_ScaledNumWeightSOG / 12 *
1721 if (So > g_ScaledNumWeightSOG) So = g_ScaledNumWeightSOG;
1723 if (td->bCPA_Valid) {
1724 Cpa = g_ScaledNumWeightCPA - g_ScaledNumWeightCPA / 4 * td->CPA;
1727 if (td->TCPA > .0) Cpa = Cpa + Cpa * g_ScaledNumWeightTCPA / 100;
1728 if (Cpa < .0) Cpa = .0;
1732 Rang = g_ScaledNumWeightRange / 10 * td->Range_NM;
1733 if (Rang > g_ScaledNumWeightRange) Rang = g_ScaledNumWeightRange;
1734 Rang = g_ScaledNumWeightRange - Rang;
1736 Siz = g_ScaledNumWeightSizeOfT / 30 * (td->DimA + td->DimB);
1737 if (Siz > g_ScaledNumWeightSizeOfT) Siz = g_ScaledNumWeightSizeOfT;
1738 td->importance = (float)So + Cpa + Rang + Siz;
1744 AISImportanceSwitchPoint = 0.0;
1746 float *Array =
new float[g_ShowScaled_Num];
1747 for (
int i = 0; i < g_ShowScaled_Num; i++) Array[i] = 0.0;
1751 if (cp->GetAttenAIS()) {
1752 for (
const auto &it : current_targets) {
1753 auto td = it.second;
1754 if (vp.GetBBox().Contains(td->Lat, td->Lon)) {
1755 if (td->importance > AISImportanceSwitchPoint) {
1756 Array[LowestInd] = td->importance;
1758 AISImportanceSwitchPoint = Array[0];
1760 for (
int i = 1; i < g_ShowScaled_Num; i++) {
1761 if (Array[i] < AISImportanceSwitchPoint) {
1762 AISImportanceSwitchPoint = Array[i];
1775 for (
const auto &it : current_targets) {
1776 auto td = it.second;
1777 if ((td->SOG < g_ShowMoored_Kts) &&
1778 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1779 AISDrawTarget(td.get(), dc, vp, cp);
1783 for (
const auto &it : current_targets) {
1784 auto td = it.second;
1785 if ((td->SOG >= g_ShowMoored_Kts) &&
1786 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1787 AISDrawTarget(td.get(), dc, vp, cp);
1788 if (td->importance > 0) AISDrawTarget(td.get(), dc, vp, cp);
1792 for (
const auto &it : current_targets) {
1793 auto td = it.second;
1794 if ((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))
1795 AISDrawTarget(td.get(), dc, vp, cp);
1800 if (!g_pAIS)
return false;
1802 if (!cc->GetShowAIS())
return false;
1805 for (
const auto &it : g_pAIS->GetTargetList()) {
1806 auto td = it.second;
1807 if (vp.GetBBox().Contains(td->Lat, td->Lon))
return true;