37#include <wx/tokenzr.h>
38#include <wx/datetime.h>
39#include <wx/wfstream.h>
40#include <wx/imaglist.h>
71int ImportanceSwitchPoint = 100;
76extern float g_ShipScaleFactorExp;
77extern float g_MarkScaleFactorExp;
79float AISImportanceSwitchPoint = 0.0;
82static const long long lNaN = 0xfff8000000000000;
83#define NAN (*(double *)&lNaN)
86wxString ais8_001_22_notice_names[] = {
88 _(
"Caution Area: Marine mammals habitat (implies whales NOT "
90 _(
"Caution Area: Marine mammals in area - reduce speed"),
91 _(
"Caution Area: Marine mammals in area - stay clear"),
92 _(
"Caution Area: Marine mammals in area - report sightings"),
93 _(
"Caution Area: Protected habitat - reduce speed"),
94 _(
"Caution Area: Protected habitat - stay clear"),
95 _(
"Caution Area: Protected habitat - no fishing or anchoring"),
96 _(
"Caution Area: Derelicts (drifting objects)"),
97 _(
"Caution Area: Traffic congestion"),
98 _(
"Caution Area: Marine event"),
99 _(
"Caution Area: Divers down"),
100 _(
"Caution Area: Swim area"),
101 _(
"Caution Area: Dredge operations"),
102 _(
"Caution Area: Survey operations"),
103 _(
"Caution Area: Underwater operation"),
104 _(
"Caution Area: Seaplane operations"),
105 _(
"Caution Area: Fishery - nets in water"),
106 _(
"Caution Area: Cluster of fishing vessels"),
107 _(
"Caution Area: Fairway closed"),
108 _(
"Caution Area: Harbour closed"),
109 _(
"Caution Area: Risk (define in Associated text field)"),
110 _(
"Caution Area: Underwater vehicle operation"),
111 _(
"(reserved for future use)"),
112 _(
"Environmental Caution Area: Storm front (line squall)"),
113 _(
"Environmental Caution Area: Hazardous sea ice"),
114 _(
"Environmental Caution Area: Storm warning (storm cell or line "
116 _(
"Environmental Caution Area: High wind"),
117 _(
"Environmental Caution Area: High waves"),
118 _(
"Environmental Caution Area: Restricted visibility (fog, rain, "
120 _(
"Environmental Caution Area: Strong currents"),
121 _(
"Environmental Caution Area: Heavy icing"),
122 _(
"(reserved for future use)"),
123 _(
"Restricted Area: Fishing prohibited"),
124 _(
"Restricted Area: No anchoring."),
125 _(
"Restricted Area: Entry approval required prior to transit"),
126 _(
"Restricted Area: Entry prohibited"),
127 _(
"Restricted Area: Active military OPAREA"),
128 _(
"Restricted Area: Firing - danger area."),
129 _(
"Restricted Area: Drifting Mines"),
130 _(
"(reserved for future use)"),
131 _(
"Anchorage Area: Anchorage open"),
132 _(
"Anchorage Area: Anchorage closed"),
133 _(
"Anchorage Area: Anchoring prohibited"),
134 _(
"Anchorage Area: Deep draft anchorage"),
135 _(
"Anchorage Area: Shallow draft anchorage"),
136 _(
"Anchorage Area: Vessel transfer operations"),
137 _(
"(reserved for future use)"),
138 _(
"(reserved for future use)"),
139 _(
"(reserved for future use)"),
140 _(
"(reserved for future use)"),
141 _(
"(reserved for future use)"),
142 _(
"(reserved for future use)"),
143 _(
"(reserved for future use)"),
144 _(
"(reserved for future use)"),
145 _(
"(reserved for future use)"),
146 _(
"(reserved for future use)"),
147 _(
"Security Alert - Level 1"),
148 _(
"Security Alert - Level 2"),
149 _(
"Security Alert - Level 3"),
150 _(
"(reserved for future use)"),
151 _(
"(reserved for future use)"),
152 _(
"(reserved for future use)"),
153 _(
"(reserved for future use)"),
154 _(
"(reserved for future use)"),
155 _(
"Distress Area: Vessel disabled and adrift"),
156 _(
"Distress Area: Vessel sinking"),
157 _(
"Distress Area: Vessel abandoning ship"),
158 _(
"Distress Area: Vessel requests medical assistance"),
159 _(
"Distress Area: Vessel flooding"),
160 _(
"Distress Area: Vessel fire/explosion"),
161 _(
"Distress Area: Vessel grounding"),
162 _(
"Distress Area: Vessel collision"),
163 _(
"Distress Area: Vessel listing/capsizing"),
164 _(
"Distress Area: Vessel under assault"),
165 _(
"Distress Area: Person overboard"),
166 _(
"Distress Area: SAR area"),
167 _(
"Distress Area: Pollution response area"),
168 _(
"(reserved for future use)"),
169 _(
"(reserved for future use)"),
170 _(
"(reserved for future use)"),
171 _(
"Instruction: Contact VTS at this point/juncture"),
172 _(
"Instruction: Contact Port Administration at this "
174 _(
"Instruction: Do not proceed beyond this point/juncture"),
175 _(
"Instruction: Await instructions prior to proceeding beyond this "
177 _(
"Proceed to this location - await instructions"),
178 _(
"Clearance granted - proceed to berth"),
179 _(
"(reserved for future use)"),
180 _(
"(reserved for future use)"),
181 _(
"Information: Pilot boarding position"),
182 _(
"Information: Icebreaker waiting area"),
183 _(
"Information: Places of refuge"),
184 _(
"Information: Position of icebreakers"),
185 _(
"Information: Location of response units"),
186 _(
"VTS active target"),
187 _(
"Rogue or suspicious vessel"),
188 _(
"Vessel requesting non-distress assistance"),
189 _(
"Chart Feature: Sunken vessel"),
190 _(
"Chart Feature: Submerged object"),
191 _(
"Chart Feature: Semi-submerged object"),
192 _(
"Chart Feature: Shoal area"),
193 _(
"Chart Feature: Shoal area due north"),
194 _(
"Chart Feature: Shoal area due east"),
195 _(
"Chart Feature: Shoal area due south"),
196 _(
"Chart Feature: Shoal area due west"),
197 _(
"Chart Feature: Channel obstruction"),
198 _(
"Chart Feature: Reduced vertical clearance"),
199 _(
"Chart Feature: Bridge closed"),
200 _(
"Chart Feature: Bridge partially open"),
201 _(
"Chart Feature: Bridge fully open"),
202 _(
"(reserved for future use)"),
203 _(
"(reserved for future use)"),
204 _(
"(reserved for future use)"),
205 _(
"Report from ship: Icing info"),
206 _(
"(reserved for future use)"),
207 _(
"Report from ship: Miscellaneous information - define in "
208 "Associated text field"),
209 _(
"(reserved for future use)"),
210 _(
"(reserved for future use)"),
211 _(
"(reserved for future use)"),
212 _(
"(reserved for future use)"),
213 _(
"(reserved for future use)"),
214 _(
"Route: Recommended route"),
215 _(
"Route: Alternative route"),
216 _(
"Route: Recommended route through ice"),
217 _(
"(reserved for future use)"),
218 _(
"(reserved for future use)"),
219 _(
"Other - Define in associated text field"),
220 _(
"Cancellation - cancel area as identified by Message Linkage "
222 _(
"Undefined (default)")
226 double rlon, wxPoint *r) {
234static wxPoint transrot(wxPoint pt,
float sin_theta,
float cos_theta,
235 wxPoint offset = wxPoint(0, 0)) {
237 float px = (float)(pt.x * sin_theta) + (float)(pt.y * cos_theta);
238 float py = (float)(pt.y * sin_theta) - (float)(pt.x * cos_theta);
239 ret.x = (int)wxRound(px);
240 ret.y = (int)wxRound(py);
247static void transrot_pts(
int n, wxPoint *pt,
float sin_theta,
float cos_theta,
248 wxPoint offset = wxPoint(0, 0)) {
249 for (
int i = 0; i < n; i++)
250 pt[i] = transrot(pt[i], sin_theta, cos_theta, offset);
254 if (cp == NULL)
return;
255 if (!
g_pAIS || !cp->GetShowAIS() || !g_bShowAreaNotices)
return;
257 wxDateTime now = wxDateTime::Now();
260 bool b_pens_set =
false;
266 wxBrush *yellow_brush = wxTheBrushList->FindOrCreateBrush(
267 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
268 wxBrush *green_brush = wxTheBrushList->FindOrCreateBrush(
269 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
275 for (
const auto &target :
g_pAIS->GetAreaNoticeSourcesList()) {
276 auto target_data = target.second;
277 if (!target_data->area_notices.empty()) {
279 pen_save = dc.GetPen();
280 brush_save = dc.GetBrush();
282 yellow = GetGlobalColor(
"YELO1");
283 yellow.Set(yellow.Red(), yellow.Green(), yellow.Blue(), 64);
285 green = GetGlobalColor(
"GREEN4");
286 green.Set(green.Red(), green.Green(), green.Blue(), 64);
288 pen.SetColour(yellow);
291 yellow_brush = wxTheBrushList->FindOrCreateBrush(
292 yellow, wxBRUSHSTYLE_CROSSDIAG_HATCH);
294 wxTheBrushList->FindOrCreateBrush(green, wxBRUSHSTYLE_TRANSPARENT);
295 brush = yellow_brush;
300 for (
auto &ani : target_data->area_notices) {
303 if (area_notice.expiry_time > now) {
304 std::vector<wxPoint> points;
305 bool draw_polygon =
false;
307 switch (area_notice.notice_type) {
309 pen.SetColour(green);
313 pen.SetColour(yellow);
314 brush = yellow_brush;
317 pen.SetColour(yellow);
318 brush = yellow_brush;
323 for (Ais8_001_22_SubAreaList::iterator sa =
324 area_notice.sub_areas.begin();
325 sa != area_notice.sub_areas.end(); ++sa) {
327 case AIS8_001_22_SHAPE_CIRCLE: {
328 wxPoint target_point;
329 GetCanvasPointPix(vp, cp, sa->latitude, sa->longitude,
331 points.push_back(target_point);
332 if (sa->radius_m > 0.0)
333 dc.DrawCircle(target_point, sa->radius_m * vp_scale);
336 case AIS8_001_22_SHAPE_RECT: {
337 wxPoint target_point;
338 double lat = sa->latitude;
339 double lon = sa->longitude;
340 int orient_east = 90 + sa->orient_deg;
341 if (orient_east > 360) orient_east -= 360;
342 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
343 points.push_back(target_point);
344 ll_gc_ll(lat, lon, orient_east, sa->e_dim_m / 1852.0, &lat,
346 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
347 points.push_back(target_point);
348 ll_gc_ll(lat, lon, sa->orient_deg, sa->n_dim_m / 1852.0, &lat,
350 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
351 points.push_back(target_point);
352 ll_gc_ll(sa->latitude, sa->longitude, sa->orient_deg,
353 sa->n_dim_m / 1852.0, &lat, &lon);
354 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
355 points.push_back(target_point);
359 case AIS8_001_22_SHAPE_SECTOR: {
360 wxPoint target_point;
362 double lat1 = sa->latitude;
363 double lon1 = sa->longitude;
364 GetCanvasPointPix(vp, cp, lat1, lon1, &target_point);
365 points.push_back(target_point);
367 for (
int i = 0; i < 18; ++i) {
371 i * (sa->right_bound_deg - sa->left_bound_deg) / 18,
372 sa->radius_m / 1852.0, &lat, &lon);
373 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
374 points.push_back(target_point);
377 ll_gc_ll(lat1, lon1, sa->right_bound_deg, sa->radius_m / 1852.0,
379 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
380 points.push_back(target_point);
385 case AIS8_001_22_SHAPE_POLYGON:
388 case AIS8_001_22_SHAPE_POLYLINE: {
389 double lat = sa->latitude;
390 double lon = sa->longitude;
391 for (
int i = 0; i < 4; ++i) {
392 ll_gc_ll(lat, lon, sa->angles[i], sa->dists_m[i] / 1852.0,
394 wxPoint target_point;
395 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
396 points.push_back(target_point);
398 dc.DrawLines(points.size(), &points.front());
402 if (draw_polygon) dc.DrawPolygon(points.size(), &points.front());
410 dc.SetBrush(brush_save);
414static void TargetFrame(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
416 int gap2 = 2 * radius / 6;
418 wxPen pen_save = dc.GetPen();
422 dc.
DrawLine(x - radius, y + gap2, x - radius, y + radius);
423 dc.
DrawLine(x - radius, y + radius, x - gap2, y + radius);
424 dc.
DrawLine(x + gap2, y + radius, x + radius, y + radius);
425 dc.
DrawLine(x + radius, y + radius, x + radius, y + gap2);
426 dc.
DrawLine(x + radius, y - gap2, x + radius, y - radius);
427 dc.
DrawLine(x + radius, y - radius, x + gap2, y - radius);
428 dc.
DrawLine(x - gap2, y - radius, x - radius, y - radius);
429 dc.
DrawLine(x - radius, y - radius, x - radius, y - gap2);
434static void AtoN_Diamond(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius,
437 radius *= gFrame->GetPrimaryCanvas()->GetContentScaleFactor();
440 wxPen pen_save = dc.GetPen();
443 wxPen aton_WhiteBorderPen;
446 int rad1a = radius / 2;
447 int rad2a = radius / 4;
453 if ((td->NavStatus == ATON_VIRTUAL_OFFPOSITION) ||
454 (td->NavStatus == ATON_REAL_OFFPOSITION))
455 aton_DrawPen = wxPen(GetGlobalColor(
"URED"), pen.GetWidth());
457 aton_DrawPen = wxPen(GetGlobalColor(
"UBLCK"), pen.GetWidth());
459 bool b_virt = (td->NavStatus == ATON_VIRTUAL) |
460 (td->NavStatus == ATON_VIRTUAL_ONPOSITION) |
461 (td->NavStatus == ATON_VIRTUAL_OFFPOSITION);
463 if (b_virt) aton_DrawPen.SetStyle(wxPENSTYLE_SHORT_DASH);
465 aton_WhiteBorderPen =
466 wxPen(GetGlobalColor(
"CHWHT"), aton_DrawPen.GetWidth() + 2);
471 diamond[0] = wxPoint(radius, 0);
472 diamond[1] = wxPoint(0, -radius);
473 diamond[2] = wxPoint(-radius, 0);
474 diamond[3] = wxPoint(0, radius);
475 diamond[4] = wxPoint(radius, 0);
476 dc.SetPen(aton_WhiteBorderPen);
477 dc.DrawLines(5, diamond, x, y);
478 dc.SetPen(aton_DrawPen);
479 dc.DrawLines(5, diamond, x, y);
482 wxPen(GetGlobalColor(
"UBLCK"),
484 aton_WhiteBorderPen =
485 wxPen(GetGlobalColor(
"CHWHT"), aton_DrawPen.GetWidth() + 2);
489 cross[0] = wxPoint(-rad2a, 0);
490 cross[1] = wxPoint(rad2a, 0);
491 cross[2] = wxPoint(0, 0);
492 cross[3] = wxPoint(0, rad2a);
493 cross[4] = wxPoint(0, -rad2a);
494 dc.SetPen(aton_WhiteBorderPen);
495 dc.DrawLines(5, cross, x, y);
496 dc.SetPen(aton_DrawPen);
497 dc.DrawLines(5, cross, x, y);
499 wxPoint TriPointUp[4];
500 TriPointUp[0] = wxPoint(-rad1a, 0);
501 TriPointUp[1] = wxPoint(rad1a, 0);
502 TriPointUp[2] = wxPoint(0, -rad1a);
503 TriPointUp[3] = wxPoint(-rad1a, 0);
505 wxPoint TriPointDown[4];
506 TriPointDown[0] = wxPoint(-rad1a, -rad1a);
507 TriPointDown[1] = wxPoint(rad1a, -rad1a);
508 TriPointDown[2] = wxPoint(0, 0);
509 TriPointDown[3] = wxPoint(-rad1a, -rad1a);
511 wxPoint CircleOpen[16];
512 CircleOpen[0] = wxPoint(-1, 5);
513 CircleOpen[1] = wxPoint(1, 5);
514 CircleOpen[2] = wxPoint(3, 4);
515 CircleOpen[3] = wxPoint(4, 3);
516 CircleOpen[4] = wxPoint(5, 1);
517 CircleOpen[5] = wxPoint(5, -1);
518 CircleOpen[6] = wxPoint(4, -3);
519 CircleOpen[7] = wxPoint(3, -4);
520 CircleOpen[8] = wxPoint(1, -5);
521 CircleOpen[9] = wxPoint(-1, -5);
522 CircleOpen[10] = wxPoint(-3, -4);
523 CircleOpen[11] = wxPoint(-4, -3);
524 CircleOpen[12] = wxPoint(-5, -1);
525 CircleOpen[13] = wxPoint(-4, 3);
526 CircleOpen[14] = wxPoint(-3, 4);
527 CircleOpen[15] = wxPoint(-1, 5);
529 switch (td->ShipType) {
532 dc.SetPen(aton_WhiteBorderPen);
533 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
534 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
535 dc.SetPen(aton_DrawPen);
536 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
537 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
541 dc.SetPen(aton_WhiteBorderPen);
542 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
543 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
544 dc.SetPen(aton_DrawPen);
545 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
546 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
550 dc.SetPen(aton_WhiteBorderPen);
551 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
552 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
553 dc.SetPen(aton_DrawPen);
554 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
555 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
559 dc.SetPen(aton_WhiteBorderPen);
560 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
561 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
562 dc.SetPen(aton_DrawPen);
563 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
564 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
569 aRect[0] = wxPoint(-rad3a, 0);
570 aRect[1] = wxPoint(-rad3a, -rad3a - rad3a);
571 aRect[2] = wxPoint(rad3a, -rad3a - rad3a);
572 aRect[3] = wxPoint(rad3a, 0);
573 aRect[4] = wxPoint(-rad3a, 0);
574 dc.SetPen(aton_WhiteBorderPen);
575 dc.DrawLines(5, aRect, x, y - radius - 1);
576 dc.SetPen(aton_DrawPen);
577 dc.DrawLines(5, aRect, x, y - radius - 1);
581 dc.SetPen(aton_WhiteBorderPen);
582 dc.DrawLines(4, TriPointUp, x, y - radius);
583 dc.SetPen(aton_DrawPen);
584 dc.DrawLines(4, TriPointUp, x, y - radius);
588 dc.SetPen(aton_WhiteBorderPen);
589 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
590 dc.SetPen(aton_DrawPen);
591 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
592 dc.SetPen(aton_WhiteBorderPen);
593 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
594 dc.SetPen(aton_DrawPen);
595 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
599 dc.SetPen(aton_WhiteBorderPen);
600 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
601 dc.SetPen(aton_DrawPen);
602 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
606 cross[0] = wxPoint(-rad2a, -rad2a);
607 cross[1] = wxPoint(rad2a, rad2a);
608 cross[2] = wxPoint(0, 0);
609 cross[3] = wxPoint(-rad2a, rad2a);
610 cross[4] = wxPoint(rad2a, -rad2a);
611 dc.SetPen(aton_WhiteBorderPen);
612 dc.DrawLines(5, cross, x, y - radius - rad3a);
613 dc.SetPen(aton_DrawPen);
614 dc.DrawLines(5, cross, x, y - radius - rad3a);
622static void Base_Square(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
624 int gap2 = 2 * radius / 6;
625 int pen_width = pen.GetWidth();
627 wxPen pen_save = dc.GetPen();
631 dc.
DrawLine(x - radius, y - radius, x - radius, y + radius);
632 dc.
DrawLine(x - radius, y + radius, x + radius, y + radius);
633 dc.
DrawLine(x + radius, y + radius, x + radius, y - radius);
634 dc.
DrawLine(x + radius, y - radius, x - radius, y - radius);
638 pen.SetWidth(pen_width);
641 dc.
DrawLine(x - gap2, y, x + gap2, y);
642 dc.
DrawLine(x, y - gap2, x, y + gap2);
647static void SART_Render(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
649 int gap = (radius * 12) / 10;
650 int pen_width = pen.GetWidth();
652 wxPen pen_save = dc.GetPen();
656 wxBrush brush_save = dc.GetBrush();
657 wxBrush *ppBrush = wxTheBrushList->FindOrCreateBrush(
658 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
659 dc.SetBrush(*ppBrush);
661 dc.DrawCircle(x, y, radius);
665 pen.SetWidth(pen_width);
671 dc.SetBrush(brush_save);
677static void spherical_ll_gc_ll(
float lat,
float lon,
float brg,
float dist,
678 float *dlat,
float *dlon) {
679 float angr = brg / 180 * M_PI;
680 float latr = lat * M_PI / 180;
681 float D = dist / 3443;
682 float sD = sinf(D), cD = cosf(D);
683 float sy = sinf(latr), cy = cosf(latr);
684 float sa = sinf(angr), ca = cosf(angr);
686 *dlon = lon + asinf(sa * sD / cy) * 180 / M_PI;
687 *dlat = asinf(sy * cD + cy * sD * ca) * 180 / M_PI;
691float AIS_scale_factor;
692float AIS_ATON_scale_factor;
693float AIS_nominal_target_size_mm;
694float AIS_nominal_icon_size_pixels;
696float AIS_user_scale_factor;
697double AIS_nominal_line_width_pix;
699float AIS_width_interceptbar_base;
700float AIS_width_interceptbar_top;
701float AIS_intercept_bar_circle_diameter;
702float AIS_width_interceptline;
703float AIS_width_cogpredictor_base;
704float AIS_width_cogpredictor_line;
705float AIS_width_target_outline;
706float AIS_icon_diameter;
707float AIS_ATON_reference;
711static void AISSetMetrics() {
714 double DPIscale = 1.0;
725 AIS_nominal_target_size_mm = 30.0 / g_Platform->GetDisplayDPmm();
729 AIS_nominal_target_size_mm = wxMin(AIS_nominal_target_size_mm, 10.0);
730 AIS_nominal_target_size_mm = wxMax(AIS_nominal_target_size_mm, 5.0);
732 AIS_nominal_icon_size_pixels =
733 wxMax(4.0, g_Platform->GetDisplayDPmm() *
734 AIS_nominal_target_size_mm);
736 AIS_pix_factor = AIS_nominal_icon_size_pixels /
739 AIS_scale_factor *= AIS_pix_factor;
741 AIS_user_scale_factor = g_ShipScaleFactorExp;
742 if (g_ShipScaleFactorExp > 1.0)
743 AIS_user_scale_factor = (log(g_ShipScaleFactorExp) + 1.0) *
745 AIS_scale_factor *= AIS_user_scale_factor;
748 AIS_ATON_scale_factor = g_MarkScaleFactorExp;
749 if (g_MarkScaleFactorExp > 1.0)
750 AIS_ATON_scale_factor = (log(g_MarkScaleFactorExp) + 1.0) *
755 AIS_nominal_line_width_pix =
756 wxMax(2, g_Platform->GetDisplayDPmm() / (4.0 / DPIscale));
759 AIS_width_interceptbar_base = 3 * AIS_nominal_line_width_pix;
760 AIS_width_interceptbar_top = 1.5 * AIS_nominal_line_width_pix;
761 AIS_intercept_bar_circle_diameter = 3.5 * AIS_nominal_line_width_pix;
762 AIS_width_interceptline = 2 * AIS_nominal_line_width_pix;
763 AIS_width_cogpredictor_base = 3 * AIS_nominal_line_width_pix;
764 AIS_width_cogpredictor_line = 1.3 * AIS_nominal_line_width_pix;
765 AIS_width_target_outline = 1.4 * AIS_nominal_line_width_pix;
766 AIS_icon_diameter = AIS_intercept_bar_circle_diameter * AIS_user_scale_factor;
770 AIS_intercept_bar_circle_diameter * AIS_ATON_scale_factor;
772 wxFont *font = FontMgr::Get().
GetFont(_(
"AIS Target Name"), 0);
773 double scaler = DPIscale;
776 font->GetPointSize() / scaler, font->GetFamily(), font->GetStyle(),
777 font->GetWeight(),
false, font->GetFaceName());
783 if (NULL == td)
return;
785 static bool firstTimeUse =
true;
788 g_AisFirstTimeUse =
true;
791 g_AisFirstTimeUse =
false;
792 firstTimeUse =
false;
796 if (td->b_lost)
return;
801 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts) &&
802 (td->NavStatus != NOT_UNDER_COMMAND) &&
803 ((td->Class == AIS_CLASS_A) || (td->Class == AIS_CLASS_B)))
807 if (!td->b_positionOnceValid)
return;
810 if (td->b_OwnShip)
return;
813 float target_sog = td->SOG;
814 if ((td->SOG > 102.2) && !td->b_SarAircraftPosnReport) target_sog = 0.;
817 wxPoint TargetPoint, PredPoint;
820 if (td->n_alert_state == AIS_ALERT_SET) {
824 if (vp.GetBBox().Contains(td->Lat, td->Lon))
828 if (1 && td->b_show_track) {
829 if (td->m_ptrack.size() > 0) {
831 if (vp.GetBBox().Contains(ptrack_point.m_lat, ptrack_point.m_lon))
841 float pred_lat, pred_lon;
842 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
843 target_sog * g_ShowCOG_Mins / 60., &pred_lat, &pred_lon);
846 if (vp.GetBBox().Contains(pred_lat, pred_lon))
850 box.SetFromSegment(td->Lat, td->Lon, pred_lat, pred_lon);
853 if (!vp.GetBBox().IntersectOut(box)) drawit++;
859 GetCanvasPointPix(vp, cp, td->Lat, td->Lon, &TargetPoint);
860 GetCanvasPointPix(vp, cp, pred_lat, pred_lon, &PredPoint);
862 bool b_hdgValid =
true;
864 float theta = (float)-PI / 2.;
866 if ((
int)(td->HDG) != 511) {
867 theta = ((td->HDG - 90) * PI / 180.) + vp.
rotation;
871 if (!g_bInlandEcdis) {
877 float angle_lat, angle_lon;
878 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG, angle_distance_nm,
879 &angle_lat, &angle_lon);
882 GetCanvasPointPix(vp, cp, angle_lat, angle_lon, &AnglePoint);
884 if (abs(AnglePoint.x - TargetPoint.x) > 0) {
885 if (target_sog > g_SOGminCOG_kts) {
886 theta = atan2f((
double)(AnglePoint.y - TargetPoint.y),
887 (
double)(AnglePoint.x - TargetPoint.x));
890 theta = (float)-PI / 2.;
892 if (AnglePoint.y > TargetPoint.y)
893 theta = (float)PI / 2.;
897 if (td->SOG >= g_SOGminCOG_kts)
906 float sin_theta = sinf(theta), cos_theta = cosf(theta);
909 dash_long[0] = (int)(1.0 * gFrame->GetPrimaryCanvas()
912 (int)(0.5 * gFrame->GetPrimaryCanvas()->
GetPixPerMM());
914 int targetscale = 100;
917 idxCC = cp->m_canvasIndex;
919 if (idxCC > AIS_TARGETDATA_MAX_CANVAS - 1)
922 if (cp->GetAttenAIS()) {
923 if (td->NavStatus <= 15) {
927 if (td->importance < AISImportanceSwitchPoint)
928 targetscale = td->last_scale[idxCC] - 2;
930 if (td->importance > AISImportanceSwitchPoint)
931 targetscale = td->last_scale[idxCC] + 5;
932 if (targetscale > 100) targetscale = 100;
933 if (targetscale < 50) targetscale = 50;
934 td->last_scale[idxCC] = targetscale;
940 wxPoint ais_real_size[6];
941 bool bcan_draw_size =
true;
942 if (g_bDrawAISSize) {
943 if (td->DimA + td->DimB == 0 || td->DimC + td->DimD == 0) {
944 bcan_draw_size =
false;
946 double ref_lat, ref_lon;
947 ll_gc_ll(td->Lat, td->Lon, 0, 100. / 1852., &ref_lat, &ref_lon);
950 double ppm = r_point.GetDistance(b_point) / 100.;
951 double offwid = (td->DimC + td->DimD) * ppm * 0.25;
952 double offlen = (td->DimA + td->DimB) * ppm * 0.15;
953 ais_real_size[0].x = -td->DimD * ppm;
954 ais_real_size[0].y = -td->DimB * ppm;
955 ais_real_size[1].x = -td->DimD * ppm;
956 ais_real_size[1].y = td->DimA * ppm - offlen;
957 ais_real_size[2].x = -td->DimD * ppm + offwid;
958 ais_real_size[2].y = td->DimA * ppm;
959 ais_real_size[3].x = td->DimC * ppm - offwid;
960 ais_real_size[3].y = td->DimA * ppm;
961 ais_real_size[4].x = td->DimC * ppm;
962 ais_real_size[4].y = td->DimA * ppm - offlen;
963 ais_real_size[5].x = td->DimC * ppm;
964 ais_real_size[5].y = -td->DimB * ppm;
966 if (ais_real_size[4].x - ais_real_size[0].x < 16 ||
967 ais_real_size[2].y - ais_real_size[0].y < 30)
968 bcan_draw_size =
false;
970 bcan_draw_size =
true;
971 transrot_pts(6, ais_real_size, sin_theta, cos_theta);
978 wxPoint ais_quad_icon[4] = {wxPoint(-8, -6), wxPoint(0, 24), wxPoint(8, -6),
980 wxPoint ais_octo_icon[8] = {wxPoint(4, 8), wxPoint(8, 4), wxPoint(8, -4),
981 wxPoint(4, -8), wxPoint(-4, -8), wxPoint(-8, -4),
982 wxPoint(-8, 4), wxPoint(-4, 8)};
984 if (!g_bInlandEcdis) {
986 if (targetscale == 50) {
987 ais_quad_icon[0] = wxPoint(-4, -3);
988 ais_quad_icon[1] = wxPoint(0, 12);
989 ais_quad_icon[2] = wxPoint(4, -3);
990 ais_quad_icon[3] = wxPoint(0, -3);
991 }
else if (targetscale != 100) {
993 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
994 ais_quad_icon[1] = wxPoint(0, (
int)24 * targetscale / 100);
996 wxPoint((
int)8 * targetscale / 100, (
int)-6 * targetscale / 100);
997 ais_quad_icon[3] = wxPoint(0, (
int)-6 * targetscale / 100);
1001 if (td->Class == AIS_CLASS_B) ais_quad_icon[3].y = 0;
1003 if ((td->Class == AIS_GPSG_BUDDY) || (td->b_isFollower)) {
1004 ais_quad_icon[0] = wxPoint(-5, -12);
1005 ais_quad_icon[1] = wxPoint(-3, 12);
1006 ais_quad_icon[2] = wxPoint(3, 12);
1007 ais_quad_icon[3] = wxPoint(5, -12);
1008 }
else if (td->Class == AIS_DSC) {
1009 ais_quad_icon[0].y = 0;
1010 ais_quad_icon[1].y = 8;
1011 ais_quad_icon[2].y = 0;
1012 ais_quad_icon[3].y = -8;
1013 }
else if (td->Class == AIS_APRS) {
1014 ais_quad_icon[0] = wxPoint(-8, -8);
1015 ais_quad_icon[1] = wxPoint(-8, 8);
1016 ais_quad_icon[2] = wxPoint(8, 8);
1017 ais_quad_icon[3] = wxPoint(8, -8);
1020 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1023 iconPoints = ais_quad_icon;
1027 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1029 iconPoints = ais_quad_icon;
1032 iconPoints = ais_octo_icon;
1036 wxColour UBLCK = GetGlobalColor(
"UBLCK");
1037 dc.SetPen(wxPen(UBLCK));
1040 wxColour UINFG = GetGlobalColor(
"UINFG");
1041 wxBrush target_brush = wxBrush(UINFG);
1044 if (td->b_isEuroInland && !g_bInlandEcdis)
1045 target_brush = wxBrush(GetGlobalColor(
"TEAL1"));
1048 if (td->b_nameFromCache) target_brush = wxBrush(GetGlobalColor(
"GREEN5"));
1051 wxColour URED = GetGlobalColor(
"URED");
1052 if (!td->b_nameValid) target_brush = wxBrush(GetGlobalColor(
"CHYLW"));
1054 if ((td->Class == AIS_DSC) &&
1055 ((td->ShipType == 12) || (td->ShipType == 16)))
1056 target_brush = wxBrush(URED);
1058 if (td->b_SarAircraftPosnReport) target_brush = wxBrush(UINFG);
1060 if ((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid))
1061 target_brush = wxBrush(URED);
1063 if ((td->n_alert_state == AIS_ALERT_NO_DIALOG_SET) && (td->bCPA_Valid) &&
1064 (!td->b_isFollower))
1065 target_brush = wxBrush(URED);
1067 if (td->b_positionDoubtful) target_brush = wxBrush(GetGlobalColor(
"UINFF"));
1069 wxPen target_outline_pen(UBLCK, AIS_width_target_outline);
1072 if (((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid)) ||
1073 (td->b_show_AIS_CPA && (td->bCPA_Valid))) {
1075 double tcpa_lat, tcpa_lon;
1076 ll_gc_ll(td->Lat, td->Lon, td->COG, target_sog * td->TCPA / 60., &tcpa_lat,
1079 wxPoint TPoint = TargetPoint;
1080 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1083 ClipResult res = cohen_sutherland_line_clip_i(
1084 &TPoint.x, &TPoint.y, &tCPAPoint.x, &tCPAPoint.y, 0, vp.
pix_width, 0,
1087 if (res != Invisible) {
1088 wxPen ppPen2(URED, AIS_width_cogpredictor_line, wxPENSTYLE_USER_DASH);
1089 ppPen2.SetDashes(2, dash_long);
1092 dc.StrokeLine(TPoint.x, TPoint.y, tCPAPoint.x, tCPAPoint.y);
1096 double ocpa_lat, ocpa_lon;
1099 if (std::isnan(
gCog) || std::isnan(
gSog)) {
1108 GetCanvasPointPix(vp, cp, ocpa_lat, ocpa_lon, &oCPAPoint);
1109 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1112 wxPoint oCPAPoint_unclipped = oCPAPoint;
1113 wxPoint tCPAPoint_unclipped = tCPAPoint;
1116 ClipResult ores = cohen_sutherland_line_clip_i(
1117 &tCPAPoint.x, &tCPAPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0, vp.
pix_width,
1120 if (ores != Invisible) {
1121 wxColour yellow = GetGlobalColor(
"YELO1");
1122 dc.SetPen(wxPen(yellow, AIS_width_interceptbar_base));
1123 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1125 wxPen ppPen2(URED, AIS_width_interceptbar_top, wxPENSTYLE_USER_DASH);
1126 ppPen2.SetDashes(2, dash_long);
1128 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1131 wxBrush br(GetGlobalColor(
"BLUE3"));
1133 dc.SetPen(wxPen(UBLCK, AIS_width_target_outline));
1137 tCPAPoint_unclipped.x, tCPAPoint_unclipped.y,
1138 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1140 oCPAPoint_unclipped.x, oCPAPoint_unclipped.y,
1141 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1146 GetCanvasPointPix(vp, cp,
gLat,
gLon, &oShipPoint);
1147 oCPAPoint = oCPAPoint_unclipped;
1149 ClipResult ownres = cohen_sutherland_line_clip_i(
1150 &oShipPoint.x, &oShipPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0,
1153 if (ownres != Invisible) {
1154 wxPen ppPen2(URED, AIS_width_interceptline, wxPENSTYLE_USER_DASH);
1155 ppPen2.SetDashes(2, dash_long);
1158 dc.StrokeLine(oShipPoint.x, oShipPoint.y, oCPAPoint.x, oCPAPoint.y);
1161 dc.SetPen(wxPen(UBLCK));
1162 dc.SetBrush(wxBrush(URED));
1168 auto alert_dlg_active =
1170 if (alert_dlg_active && alert_dlg_active->IsShown() && cp) {
1171 if (alert_dlg_active->Get_Dialog_MMSI() == td->MMSI)
1172 cp->JaggyCircle(dc, wxPen(URED, 2), TargetPoint.x, TargetPoint.y, 100);
1177 if (g_pais_query_dialog_active && g_pais_query_dialog_active->IsShown()) {
1178 if (g_pais_query_dialog_active->GetMMSI() == td->MMSI)
1179 TargetFrame(dc, wxPen(UBLCK, 2), TargetPoint.x, TargetPoint.y, 25);
1184 if ((g_bShowCOG) && (target_sog > g_SOGminCOG_kts) && td->b_active) {
1185 int pixx = TargetPoint.x;
1186 int pixy = TargetPoint.y;
1187 int pixx1 = PredPoint.x;
1188 int pixy1 = PredPoint.y;
1192 float l = sqrtf(powf((
float)(PredPoint.x - TargetPoint.x), 2) +
1193 powf((
float)(PredPoint.y - TargetPoint.y), 2));
1196 ClipResult res = cohen_sutherland_line_clip_i(
1199 if (res != Invisible) {
1201 if (targetscale >= 75) {
1202 wxPen wide_pen(target_brush.GetColour(), AIS_width_cogpredictor_base);
1203 dc.SetPen(wide_pen);
1204 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1207 if (AIS_width_cogpredictor_base > 1) {
1209 wxPen narrow_pen(UBLCK, AIS_width_cogpredictor_line);
1210 if (targetscale < 75) {
1211 narrow_pen.SetWidth(1);
1212 narrow_pen.SetStyle(wxPENSTYLE_USER_DASH);
1216 narrow_pen.SetDashes(2, dash_dot);
1218 dc.SetPen(narrow_pen);
1219 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1223 dc.SetBrush(target_brush);
1224 dc.StrokeCircle(PredPoint.x, PredPoint.y, 5 * targetscale / 100);
1229#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1232 glTranslated(PredPoint.x, PredPoint.y, 0);
1233 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1235 float points[] = {0.0f, 5.0f, 2.5f, 4.330127f, 4.330127f,
1236 2.5f, 5.0f, 0, 4.330127f, -2.5f,
1237 2.5f, -4.330127f, 0, -5.1f, -2.5f,
1238 -4.330127f, -4.330127f, -2.5f, -5.0f, 0,
1239 -4.330127f, 2.5f, -2.5f, 4.330127f, 0,
1241 if (targetscale <= 75) {
1242 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1244 points[i] = points[i] / 2;
1247 wxColour c = target_brush.GetColour();
1248 glColor3ub(c.Red(), c.Green(), c.Blue());
1250 glBegin(GL_TRIANGLE_FAN);
1251 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1253 glVertex2i(points[i], points[i + 1]);
1256 glColor3ub(0, 0, 0);
1257 glLineWidth(AIS_width_target_outline);
1258 glBegin(GL_LINE_LOOP);
1259 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1261 glVertex2i(points[i], points[i + 1]);
1266 dc.SetBrush(target_brush);
1267 dc.StrokeCircle(PredPoint.x, PredPoint.y,
1268 AIS_intercept_bar_circle_diameter *
1269 AIS_user_scale_factor * targetscale / 100);
1276 if ((td->ROTAIS != 0) && (td->ROTAIS != -128) && (!g_bShowScaled)) {
1277 float cog_angle = td->COG * PI / 180.;
1279 float theta2 = theta;
1280 if (td->SOG >= g_SOGminCOG_kts)
1281 theta2 = cog_angle - (PI / 2);
1285 theta2 += (float)PI / 2;
1287 theta2 -= (float)PI / 2;
1289 int xrot = (int)round(pixx1 + (nv * cosf(theta2)));
1290 int yrot = (int)round(pixy1 + (nv * sinf(theta2)));
1291 dc.StrokeLine(pixx1, pixy1, xrot, yrot);
1297 if (td->Class == AIS_ARPA || td->Class == AIS_BUOY) {
1298 wxPen target_pen(UBLCK, 2);
1300 dc.SetPen(target_pen);
1301 dc.SetBrush(target_brush);
1302 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1.8 * AIS_icon_diameter);
1304 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1);
1306 if (!td->b_active) {
1307 dc.SetPen(wxPen(UBLCK, 2));
1308 dc.StrokeLine(TargetPoint.x - 14, TargetPoint.y, TargetPoint.x + 14,
1310 dc.SetPen(wxPen(UBLCK, 1));
1313 }
else if (td->Class == AIS_METEO) {
1314 wxPen met(UBLCK, (wxMax(target_outline_pen.GetWidth(), 2.5)));
1316 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1317 double met_radius = 1.5 * AIS_ATON_reference;
1318 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, met_radius);
1321 dc.SetPen(wxPen(wxMax(target_outline_pen.GetWidth(), 1)));
1323 dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4,
1324 TargetPoint.x - met_radius / 3,
1325 TargetPoint.y + met_radius / 2);
1327 TargetPoint.x - met_radius / 3, TargetPoint.y + met_radius / 2,
1328 TargetPoint.x - met_radius / 2, TargetPoint.y - met_radius / 2);
1330 dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4,
1331 TargetPoint.x + met_radius / 3,
1332 TargetPoint.y + met_radius / 2);
1334 TargetPoint.x + met_radius / 3, TargetPoint.y + met_radius / 2,
1335 TargetPoint.x + met_radius / 2, TargetPoint.y - met_radius / 2);
1337 }
else if (td->Class == AIS_ATON) {
1338 AtoN_Diamond(dc, wxPen(UBLCK, AIS_width_target_outline), TargetPoint.x,
1339 TargetPoint.y, AIS_ATON_reference * 1.5, td);
1340 }
else if (td->Class == AIS_BASE) {
1341 Base_Square(dc, wxPen(UBLCK, AIS_width_target_outline), TargetPoint.x,
1342 TargetPoint.y, AIS_ATON_reference);
1343 }
else if (td->Class == AIS_SART) {
1344 if (td->NavStatus == 14)
1345 SART_Render(dc, wxPen(URED, AIS_width_target_outline), TargetPoint.x,
1346 TargetPoint.y, AIS_ATON_reference);
1348 SART_Render(dc, wxPen(GetGlobalColor(
"UGREN"), AIS_width_target_outline),
1349 TargetPoint.x, TargetPoint.y, AIS_ATON_reference);
1351 }
else if (td->b_SarAircraftPosnReport) {
1352 int airtype = (td->MMSI % 1000) / 100;
1353 int ar = airtype == 5 ? 15 : 9;
1354 wxPoint SarIcon[15];
1356 double scaleplus = 1.4;
1358 SarIcon[0] = wxPoint(0, 9) * AIS_scale_factor * scaleplus;
1359 SarIcon[1] = wxPoint(1, 1) * AIS_scale_factor * scaleplus;
1360 SarIcon[2] = wxPoint(2, 1) * AIS_scale_factor * scaleplus;
1361 SarIcon[3] = wxPoint(9, 8) * AIS_scale_factor * scaleplus;
1362 SarIcon[4] = wxPoint(9, 7) * AIS_scale_factor * scaleplus;
1363 SarIcon[5] = wxPoint(3, 0) * AIS_scale_factor * scaleplus;
1364 SarIcon[6] = wxPoint(3, -5) * AIS_scale_factor * scaleplus;
1365 SarIcon[7] = wxPoint(9, -12) * AIS_scale_factor * scaleplus;
1366 SarIcon[8] = wxPoint(9, -13) * AIS_scale_factor * scaleplus;
1367 SarIcon[9] = wxPoint(2, -5) * AIS_scale_factor * scaleplus;
1368 SarIcon[10] = wxPoint(1, -15) * AIS_scale_factor * scaleplus;
1369 SarIcon[11] = wxPoint(3, -16) * AIS_scale_factor * scaleplus;
1370 SarIcon[12] = wxPoint(4, -18) * AIS_scale_factor * scaleplus;
1371 SarIcon[13] = wxPoint(1, -18) * AIS_scale_factor * scaleplus;
1372 SarIcon[14] = wxPoint(0, -19) * AIS_scale_factor * scaleplus;
1374 SarIcon[0] = wxPoint(0, 12) * AIS_scale_factor;
1375 SarIcon[1] = wxPoint(4, 2) * AIS_scale_factor;
1376 SarIcon[2] = wxPoint(16, -2) * AIS_scale_factor;
1377 SarIcon[3] = wxPoint(16, -8) * AIS_scale_factor;
1378 SarIcon[4] = wxPoint(4, -8) * AIS_scale_factor;
1379 SarIcon[5] = wxPoint(3, -16) * AIS_scale_factor;
1380 SarIcon[6] = wxPoint(10, -18) * AIS_scale_factor;
1381 SarIcon[7] = wxPoint(10, -22) * AIS_scale_factor;
1382 SarIcon[8] = wxPoint(0, -22) * AIS_scale_factor;
1389 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1390 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1392 wxPen tri_pen(target_brush.GetColour(), 1);
1394 dc.SetBrush(target_brush);
1397 int mappings[14][3] = {
1398 {0, 1, 10}, {0, 10, 14}, {1, 2, 9}, {1, 9, 10}, {10, 13, 14},
1399 {10, 11, 13}, {11, 12, 13}, {1, 14, 10}, {2, 5, 9}, {5, 6, 9},
1400 {2, 3, 5}, {3, 4, 5}, {6, 7, 8}, {6, 9, 8}};
1403 for (
int i = 0; i < nmap; i++) {
1404 wxPoint ais_tri_icon[3];
1405 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1406 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1409 dc.SetPen(target_outline_pen);
1410 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1411 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1415 for (
int i = 0; i < ar; i++)
1417 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1419 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1422 dc.SetBrush(target_brush);
1424 for (
int i = 0; i < nmap; i++) {
1425 wxPoint ais_tri_icon[3];
1426 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1427 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1430 dc.SetPen(target_outline_pen);
1431 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1432 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1437 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1438 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1440 wxPen tri_pen(target_brush.GetColour(), 1);
1442 dc.SetBrush(target_brush);
1445 int mappings[7][3] = {{0, 1, 4}, {1, 2, 3}, {1, 3, 4}, {0, 4, 5},
1446 {0, 5, 8}, {5, 6, 7}, {5, 7, 8}};
1447 for (
int i = 0; i < 7; i++) {
1448 wxPoint ais_tri_icon[3];
1449 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1450 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1453 dc.SetPen(target_outline_pen);
1454 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1455 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1459 for (
int i = 0; i < ar; i++)
1461 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1463 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1466 dc.SetBrush(target_brush);
1468 for (
int i = 0; i < 7; i++) {
1469 wxPoint ais_tri_icon[3];
1470 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1471 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1474 dc.SetPen(target_outline_pen);
1475 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1476 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1480 if (!td->b_active) {
1481 wxPoint linepoint = TargetPoint;
1482 wxPoint p1 = transrot(
1483 wxPoint((
int)-14 * targetscale / 100, (
int)-5 * targetscale / 100),
1484 sin_theta, cos_theta, TargetPoint);
1485 wxPoint p2 = transrot(
1486 wxPoint((
int)14 * targetscale / 100, (
int)-5 * targetscale / 100),
1487 sin_theta, cos_theta, TargetPoint);
1489 dc.SetPen(wxPen(UBLCK, 2));
1490 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1494 wxPen target_pen(UBLCK, 1);
1495 dc.SetPen(target_pen);
1497 wxPoint Point = TargetPoint;
1498 if (g_bDrawAISRealtime &&
1499 (td->Class == AIS_CLASS_A || td->Class == AIS_CLASS_B) &&
1500 td->SOG > g_AIS_RealtPred_Kts && td->SOG < 102.2) {
1501 wxDateTime now = wxDateTime::Now();
1503 int target_age = now.GetTicks() - td->PositionReportTicks;
1506 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
1507 td->SOG * target_age / 3600.0, &lat, &lon);
1509 GetCanvasPointPix(vp, cp, lat, lon, &Point);
1511 wxBrush realtime_brush = wxBrush(GetGlobalColor(
"GREY1"));
1512 dc.SetBrush(realtime_brush);
1513 dc.StrokePolygon(nPoints, iconPoints, Point.x, Point.y, AIS_scale_factor);
1515 dc.SetBrush(target_brush);
1518 dc.StrokePolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1523#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1525 wxColour c = target_brush.GetColour();
1526 glColor3ub(c.Red(), c.Green(), c.Blue());
1529 glTranslated(TargetPoint.x, TargetPoint.y, 0);
1530 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1532 glBegin(GL_TRIANGLE_FAN);
1535 glVertex2i(ais_quad_icon[3].x, ais_quad_icon[3].y);
1536 glVertex2i(ais_quad_icon[0].x, ais_quad_icon[0].y);
1537 glVertex2i(ais_quad_icon[1].x, ais_quad_icon[1].y);
1538 glVertex2i(ais_quad_icon[2].x, ais_quad_icon[2].y);
1540 for (
int i = 0; i < 8; i++) {
1541 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1546 glLineWidth(AIS_width_target_outline);
1548 glColor3ub(UBLCK.Red(), UBLCK.Green(), UBLCK.Blue());
1550 glBegin(GL_LINE_LOOP);
1551 for (
int i = 0; i < nPoints; i++)
1552 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1557 dc.SetPen(target_outline_pen);
1558 dc.DrawPolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1564 if (td->b_isFollower) {
1565 wxPoint ais_follow_stroke[3];
1566 ais_follow_stroke[0] = wxPoint(-3, -20) * AIS_scale_factor;
1567 ais_follow_stroke[1] = wxPoint(0, 0) * AIS_scale_factor;
1568 ais_follow_stroke[2] = wxPoint(3, -20) * AIS_scale_factor;
1570 transrot_pts(3, ais_follow_stroke, sin_theta, cos_theta);
1572 int penWidth = wxMax(target_outline_pen.GetWidth(), 2);
1573 dc.SetPen(wxPen(UBLCK, penWidth));
1574 dc.StrokeLine(ais_follow_stroke[0].x + TargetPoint.x,
1575 ais_follow_stroke[0].y + TargetPoint.y,
1576 ais_follow_stroke[1].x + TargetPoint.x,
1577 ais_follow_stroke[1].y + TargetPoint.y);
1578 dc.StrokeLine(ais_follow_stroke[1].x + TargetPoint.x,
1579 ais_follow_stroke[1].y + TargetPoint.y,
1580 ais_follow_stroke[2].x + TargetPoint.x,
1581 ais_follow_stroke[2].y + TargetPoint.y);
1584 if (g_bDrawAISSize && bcan_draw_size) {
1585 dc.SetPen(target_outline_pen);
1586 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1587 if (!g_bInlandEcdis) {
1588 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1591 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1596 dc.SetBrush(wxBrush(GetGlobalColor(
"SHIPS")));
1597 int navstatus = td->NavStatus;
1601 if (((td->ShipType >= 40) && (td->ShipType < 50)) &&
1602 (navstatus == UNDERWAY_USING_ENGINE || td->Class == AIS_CLASS_B))
1605 if (targetscale > 90) {
1606 switch (navstatus) {
1609 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1612 case RESTRICTED_MANOEUVRABILITY: {
1614 diamond[0] = wxPoint(4, 0) * AIS_scale_factor;
1615 diamond[1] = wxPoint(0, -6) * AIS_scale_factor;
1616 diamond[2] = wxPoint(-4, 0) * AIS_scale_factor;
1617 diamond[3] = wxPoint(0, 6) * AIS_scale_factor;
1618 dc.StrokePolygon(4, diamond, TargetPoint.x,
1619 TargetPoint.y - (11 * AIS_scale_factor));
1620 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1621 dc.StrokeCircle(TargetPoint.x,
1622 TargetPoint.y - (22 * AIS_scale_factor),
1623 4 * AIS_scale_factor);
1627 case CONSTRAINED_BY_DRAFT: {
1628 wxPoint can[4] = {wxPoint(-3, 0) * AIS_scale_factor,
1629 wxPoint(3, 0) * AIS_scale_factor,
1630 wxPoint(3, -16) * AIS_scale_factor,
1631 wxPoint(-3, -16) * AIS_scale_factor};
1632 dc.StrokePolygon(4, can, TargetPoint.x, TargetPoint.y);
1635 case NOT_UNDER_COMMAND: {
1636 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1637 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1638 4 * AIS_scale_factor);
1643 tri[0] = wxPoint(-4, 0) * AIS_scale_factor;
1644 tri[1] = wxPoint(4, 0) * AIS_scale_factor;
1645 tri[2] = wxPoint(0, -9) * AIS_scale_factor;
1646 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1647 tri[0] = wxPoint(0, -9) * AIS_scale_factor;
1648 tri[1] = wxPoint(4, -18) * AIS_scale_factor;
1649 tri[2] = wxPoint(-4, -18) * AIS_scale_factor;
1650 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1654 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1655 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1656 4 * AIS_scale_factor);
1657 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 18,
1658 4 * AIS_scale_factor);
1663 dc.SetBrush(target_brush);
1665 wxPoint arrow1[3] = {wxPoint(-4, 20) * AIS_scale_factor,
1666 wxPoint(0, 27) * AIS_scale_factor,
1667 wxPoint(4, 20) * AIS_scale_factor};
1668 transrot_pts(3, arrow1, sin_theta, cos_theta, TargetPoint);
1669 dc.StrokePolygon(3, arrow1);
1671 wxPoint arrow2[3] = {wxPoint(-4, 27) * AIS_scale_factor,
1672 wxPoint(0, 34) * AIS_scale_factor,
1673 wxPoint(4, 27) * AIS_scale_factor};
1674 transrot_pts(3, arrow2, sin_theta, cos_theta, TargetPoint);
1675 dc.StrokePolygon(3, arrow2);
1682 if (!td->b_active) {
1683 wxPoint p1 = transrot(wxPoint((
int)-14 * targetscale / 100, 0), sin_theta,
1684 cos_theta, TargetPoint);
1685 wxPoint p2 = transrot(wxPoint((
int)14 * targetscale / 100, 0), sin_theta,
1686 cos_theta, TargetPoint);
1688 dc.SetPen(wxPen(UBLCK, 2));
1689 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1696 if (td->blue_paddle && td->blue_paddle < 3) {
1697 wxPoint ais_flag_icon[4];
1700 if (g_bInlandEcdis) {
1702 ais_flag_icon[0] = wxPoint(-4, 4);
1703 ais_flag_icon[1] = wxPoint(-4, 11);
1704 ais_flag_icon[2] = wxPoint(-11, 11);
1705 ais_flag_icon[3] = wxPoint(-11, 4);
1706 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1708 ais_flag_icon[0] = wxPoint(TargetPoint.x - 4, TargetPoint.y + 4);
1709 ais_flag_icon[1] = wxPoint(TargetPoint.x - 4, TargetPoint.y - 3);
1710 ais_flag_icon[2] = wxPoint(TargetPoint.x + 3, TargetPoint.y - 3);
1711 ais_flag_icon[3] = wxPoint(TargetPoint.x + 3, TargetPoint.y + 4);
1714 dc.SetPen(wxPen(GetGlobalColor(
"CHWHT"), penWidth));
1718 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
1720 wxPoint((
int)-2 * targetscale / 100, (
int)18 * targetscale / 100);
1721 ais_flag_icon[2] = wxPoint((
int)-2 * targetscale / 100, 0);
1723 wxPoint((
int)-2 * targetscale / 100, (
int)-6 * targetscale / 100);
1724 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1726 if (targetscale < 100) penWidth = 1;
1727 dc.SetPen(wxPen(GetGlobalColor(
"CHWHT"), penWidth));
1729 if (td->blue_paddle == 1) {
1730 ais_flag_icon[1] = ais_flag_icon[0];
1731 ais_flag_icon[2] = ais_flag_icon[3];
1734 dc.SetBrush(wxBrush(GetGlobalColor(
"UINFB")));
1735 dc.StrokePolygon(4, ais_flag_icon);
1739 if ((g_bShowAISName) && (targetscale > 75)) {
1740 int true_scale_display = (int)(floor(vp.
chart_scale / 100.) * 100);
1741 if (true_scale_display <
1742 g_Show_Target_Name_Scale) {
1744 wxString tgt_name = td->GetFullName();
1745 tgt_name = tgt_name.substr(0, tgt_name.find(
"Unknown", 0));
1747 if (tgt_name != wxEmptyString) {
1748 dc.SetFont(*AIS_NameFont);
1749 dc.SetTextForeground(FontMgr::Get().GetFontColor(_(
"AIS Target Name")));
1752 dc.GetTextExtent(
"W", &w, &h);
1756 if ((td->COG > 90) && (td->COG < 180))
1757 dc.DrawText(tgt_name, TargetPoint.x + w, TargetPoint.y - h);
1759 dc.DrawText(tgt_name, TargetPoint.x + w,
1768 bool b_noshow =
false;
1769 bool b_forceshow =
false;
1771 if (td->MMSI == g_MMSI_Props_Array[i]->MMSI) {
1773 if (TRACKTYPE_NEVER == props->TrackType) {
1776 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
1784 int TrackLength = td->m_ptrack.size();
1785 if (((!b_noshow && td->b_show_track) || b_forceshow) && (TrackLength > 1)) {
1787 int TrackPointCount;
1788 wxPoint *TrackPoints = 0;
1789 TrackPoints =
new wxPoint[TrackLength];
1790 auto it = td->m_ptrack.begin();
1791 for (TrackPointCount = 0;
1792 it != td->m_ptrack.end() && (TrackPointCount < TrackLength);
1793 TrackPointCount++, ++it) {
1795 GetCanvasPointPix(vp, cp, ptrack_point.m_lat, ptrack_point.m_lon,
1796 &TrackPoints[TrackPointCount]);
1799 wxColour c = GetGlobalColor(
"CHMGD");
1800 dc.SetPen(wxPen(c, 1.5 * AIS_nominal_line_width_pix));
1804 std::map<int, Track *>::iterator itt;
1805 itt =
g_pAIS->m_persistent_tracks.find(td->MMSI);
1806 if (itt !=
g_pAIS->m_persistent_tracks.end()) {
1807 auto *ptrack = itt->second;
1808 if (ptrack->m_Colour == wxEmptyString) {
1809 c = GetGlobalColor(
"TEAL1");
1810 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1812 for (
unsigned int i = 0;
1813 i <
sizeof(::GpxxColorNames) /
sizeof(wxString); i++) {
1814 if (ptrack->m_Colour == ::GpxxColorNames[i]) {
1815 c = ::GpxxColors[i];
1816 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1824#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1828 glColor3ub(c.Red(), c.Green(), c.Blue());
1829 glBegin(GL_LINE_STRIP);
1831 for (TrackPointCount = 0; TrackPointCount < TrackLength;
1833 glVertex2i(TrackPoints[TrackPointCount].x,
1834 TrackPoints[TrackPointCount].y);
1838 dc.DrawLines(TrackPointCount, TrackPoints);
1841 dc.DrawLines(TrackPointCount, TrackPoints);
1845 if (dc.GetDC()) dc.StrokeLines(TrackPointCount, TrackPoints);
1849 delete[] TrackPoints;
1859 if (!cp->GetShowAIS())
return;
1864 const auto ¤t_targets =
g_pAIS->GetTargetList();
1872 }
else if (cp->m_canvasIndex == 0) {
1877 for (
const auto &it : current_targets) {
1879 auto td = it.second;
1880 double So, Cpa, Rang, Siz = 0.0;
1881 So = g_ScaledNumWeightSOG / 12 *
1883 if (So > g_ScaledNumWeightSOG) So = g_ScaledNumWeightSOG;
1885 if (td->bCPA_Valid) {
1886 Cpa = g_ScaledNumWeightCPA - g_ScaledNumWeightCPA / 4 * td->CPA;
1889 if (td->TCPA > .0) Cpa = Cpa + Cpa * g_ScaledNumWeightTCPA / 100;
1890 if (Cpa < .0) Cpa = .0;
1894 Rang = g_ScaledNumWeightRange / 10 * td->Range_NM;
1895 if (Rang > g_ScaledNumWeightRange) Rang = g_ScaledNumWeightRange;
1896 Rang = g_ScaledNumWeightRange - Rang;
1898 Siz = g_ScaledNumWeightSizeOfT / 30 * (td->DimA + td->DimB);
1899 if (Siz > g_ScaledNumWeightSizeOfT) Siz = g_ScaledNumWeightSizeOfT;
1900 td->importance = (float)So + Cpa + Rang + Siz;
1906 AISImportanceSwitchPoint = 0.0;
1908 float *Array =
new float[g_ShowScaled_Num];
1909 for (
int i = 0; i < g_ShowScaled_Num; i++) Array[i] = 0.0;
1913 if (cp->GetAttenAIS()) {
1914 for (
const auto &it : current_targets) {
1915 auto td = it.second;
1916 if (vp.GetBBox().Contains(td->Lat, td->Lon)) {
1917 if (td->importance > AISImportanceSwitchPoint) {
1918 Array[LowestInd] = td->importance;
1920 AISImportanceSwitchPoint = Array[0];
1922 for (
int i = 1; i < g_ShowScaled_Num; i++) {
1923 if (Array[i] < AISImportanceSwitchPoint) {
1924 AISImportanceSwitchPoint = Array[i];
1938 for (
const auto &it : current_targets) {
1939 auto td = it.second;
1940 if ((td->SOG < g_SOGminCOG_kts) &&
1941 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1942 AISDrawTarget(td.get(), dc, vp, cp);
1946 for (
const auto &it : current_targets) {
1947 auto td = it.second;
1948 if ((td->SOG >= g_SOGminCOG_kts) &&
1949 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1950 AISDrawTarget(td.get(), dc, vp,
1952 if (td->importance > 0) AISDrawTarget(td.get(), dc, vp, cp);
1956 for (
const auto &it : current_targets) {
1957 auto td = it.second;
1958 if ((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))
1959 AISDrawTarget(td.get(), dc, vp, cp);
1965 if (!
g_pAIS)
return false;
1967 if (!cc->GetShowAIS())
return false;
1970 for (
const auto &it :
g_pAIS->GetTargetList()) {
1971 auto td = it.second;
1972 if (vp.GetBBox().Contains(td->Lat, td->Lon))
return true;
ArrayOfMmsiProperties g_MMSI_Props_Array
Global instance.
AisDecoder * g_pAIS
Global instance.
Class AisDecoder and helpers.
Global state for AIS decoder.
Class AISTargetAlertDialog and helpers.
Class AISTargetQueryDialog.
Generic Chart canvas base.
Dialog for displaying AIS target alerts.
Dialog for querying detailed information about an AIS target.
ChartCanvas - Main chart display and interaction component.
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
double GetPixPerMM()
Get the number of logical pixels per millimeter on the screen.
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Get a font object for a UI element.
Process incoming AIS messages.
ViewPort - Core geographic projection and coordinate transformation engine.
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
int pix_height
Height of the viewport in physical pixels.
double rotation
Rotation angle of the viewport in radians.
int pix_width
Width of the viewport in physical pixels.
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
wxPoint GetPixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates.
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Device context class that can use either wxDC or OpenGL for drawing.
void DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, bool b_hiqual=true)
Draw a line between two points using either wxDC or OpenGL.
Extern C linked utilities.
double g_current_monitor_dip_px_ratio
ratio to convert between DIP and physical pixels.
Miscellaneous globals primarely used by gui layer, not persisted in configuration file.
PlugIn Object Definition/API.
wxFont * FindOrCreateFont_PlugIn(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline, const wxString &facename, wxFontEncoding encoding)
Creates or finds a font in the font cache.
double gLat
Vessel's current latitude in decimal degrees.
double gCog
Course over ground in degrees (0-359.99).
double gSog
Speed over ground in knots.
double gLon
Vessel's current longitude in decimal degrees.
Position, course, speed, etc.
Selected route, segment, waypoint, etc.