37#include <wx/tokenzr.h>
38#include <wx/datetime.h>
39#include <wx/wfstream.h>
40#include <wx/imaglist.h>
69int ImportanceSwitchPoint = 100;
74extern float g_ShipScaleFactorExp;
75extern float g_MarkScaleFactorExp;
77float AISImportanceSwitchPoint = 0.0;
80static const long long lNaN = 0xfff8000000000000;
81#define NAN (*(double *)&lNaN)
84wxString ais8_001_22_notice_names[] = {
86 _(
"Caution Area: Marine mammals habitat (implies whales NOT "
88 _(
"Caution Area: Marine mammals in area - reduce speed"),
89 _(
"Caution Area: Marine mammals in area - stay clear"),
90 _(
"Caution Area: Marine mammals in area - report sightings"),
91 _(
"Caution Area: Protected habitat - reduce speed"),
92 _(
"Caution Area: Protected habitat - stay clear"),
93 _(
"Caution Area: Protected habitat - no fishing or anchoring"),
94 _(
"Caution Area: Derelicts (drifting objects)"),
95 _(
"Caution Area: Traffic congestion"),
96 _(
"Caution Area: Marine event"),
97 _(
"Caution Area: Divers down"),
98 _(
"Caution Area: Swim area"),
99 _(
"Caution Area: Dredge operations"),
100 _(
"Caution Area: Survey operations"),
101 _(
"Caution Area: Underwater operation"),
102 _(
"Caution Area: Seaplane operations"),
103 _(
"Caution Area: Fishery - nets in water"),
104 _(
"Caution Area: Cluster of fishing vessels"),
105 _(
"Caution Area: Fairway closed"),
106 _(
"Caution Area: Harbour closed"),
107 _(
"Caution Area: Risk (define in Associated text field)"),
108 _(
"Caution Area: Underwater vehicle operation"),
109 _(
"(reserved for future use)"),
110 _(
"Environmental Caution Area: Storm front (line squall)"),
111 _(
"Environmental Caution Area: Hazardous sea ice"),
112 _(
"Environmental Caution Area: Storm warning (storm cell or line "
114 _(
"Environmental Caution Area: High wind"),
115 _(
"Environmental Caution Area: High waves"),
116 _(
"Environmental Caution Area: Restricted visibility (fog, rain, "
118 _(
"Environmental Caution Area: Strong currents"),
119 _(
"Environmental Caution Area: Heavy icing"),
120 _(
"(reserved for future use)"),
121 _(
"Restricted Area: Fishing prohibited"),
122 _(
"Restricted Area: No anchoring."),
123 _(
"Restricted Area: Entry approval required prior to transit"),
124 _(
"Restricted Area: Entry prohibited"),
125 _(
"Restricted Area: Active military OPAREA"),
126 _(
"Restricted Area: Firing - danger area."),
127 _(
"Restricted Area: Drifting Mines"),
128 _(
"(reserved for future use)"),
129 _(
"Anchorage Area: Anchorage open"),
130 _(
"Anchorage Area: Anchorage closed"),
131 _(
"Anchorage Area: Anchoring prohibited"),
132 _(
"Anchorage Area: Deep draft anchorage"),
133 _(
"Anchorage Area: Shallow draft anchorage"),
134 _(
"Anchorage Area: Vessel transfer operations"),
135 _(
"(reserved for future use)"),
136 _(
"(reserved for future use)"),
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 _(
"Security Alert - Level 1"),
146 _(
"Security Alert - Level 2"),
147 _(
"Security Alert - Level 3"),
148 _(
"(reserved for future use)"),
149 _(
"(reserved for future use)"),
150 _(
"(reserved for future use)"),
151 _(
"(reserved for future use)"),
152 _(
"(reserved for future use)"),
153 _(
"Distress Area: Vessel disabled and adrift"),
154 _(
"Distress Area: Vessel sinking"),
155 _(
"Distress Area: Vessel abandoning ship"),
156 _(
"Distress Area: Vessel requests medical assistance"),
157 _(
"Distress Area: Vessel flooding"),
158 _(
"Distress Area: Vessel fire/explosion"),
159 _(
"Distress Area: Vessel grounding"),
160 _(
"Distress Area: Vessel collision"),
161 _(
"Distress Area: Vessel listing/capsizing"),
162 _(
"Distress Area: Vessel under assault"),
163 _(
"Distress Area: Person overboard"),
164 _(
"Distress Area: SAR area"),
165 _(
"Distress Area: Pollution response area"),
166 _(
"(reserved for future use)"),
167 _(
"(reserved for future use)"),
168 _(
"(reserved for future use)"),
169 _(
"Instruction: Contact VTS at this point/juncture"),
170 _(
"Instruction: Contact Port Administration at this "
172 _(
"Instruction: Do not proceed beyond this point/juncture"),
173 _(
"Instruction: Await instructions prior to proceeding beyond this "
175 _(
"Proceed to this location - await instructions"),
176 _(
"Clearance granted - proceed to berth"),
177 _(
"(reserved for future use)"),
178 _(
"(reserved for future use)"),
179 _(
"Information: Pilot boarding position"),
180 _(
"Information: Icebreaker waiting area"),
181 _(
"Information: Places of refuge"),
182 _(
"Information: Position of icebreakers"),
183 _(
"Information: Location of response units"),
184 _(
"VTS active target"),
185 _(
"Rogue or suspicious vessel"),
186 _(
"Vessel requesting non-distress assistance"),
187 _(
"Chart Feature: Sunken vessel"),
188 _(
"Chart Feature: Submerged object"),
189 _(
"Chart Feature: Semi-submerged object"),
190 _(
"Chart Feature: Shoal area"),
191 _(
"Chart Feature: Shoal area due north"),
192 _(
"Chart Feature: Shoal area due east"),
193 _(
"Chart Feature: Shoal area due south"),
194 _(
"Chart Feature: Shoal area due west"),
195 _(
"Chart Feature: Channel obstruction"),
196 _(
"Chart Feature: Reduced vertical clearance"),
197 _(
"Chart Feature: Bridge closed"),
198 _(
"Chart Feature: Bridge partially open"),
199 _(
"Chart Feature: Bridge fully open"),
200 _(
"(reserved for future use)"),
201 _(
"(reserved for future use)"),
202 _(
"(reserved for future use)"),
203 _(
"Report from ship: Icing info"),
204 _(
"(reserved for future use)"),
205 _(
"Report from ship: Miscellaneous information - define in "
206 "Associated text field"),
207 _(
"(reserved for future use)"),
208 _(
"(reserved for future use)"),
209 _(
"(reserved for future use)"),
210 _(
"(reserved for future use)"),
211 _(
"(reserved for future use)"),
212 _(
"Route: Recommended route"),
213 _(
"Route: Alternative route"),
214 _(
"Route: Recommended route through ice"),
215 _(
"(reserved for future use)"),
216 _(
"(reserved for future use)"),
217 _(
"Other - Define in associated text field"),
218 _(
"Cancellation - cancel area as identified by Message Linkage "
220 _(
"Undefined (default)")
224 double rlon, wxPoint *r) {
232static wxPoint transrot(wxPoint pt,
float sin_theta,
float cos_theta,
233 wxPoint offset = wxPoint(0, 0)) {
235 float px = (float)(pt.x * sin_theta) + (float)(pt.y * cos_theta);
236 float py = (float)(pt.y * sin_theta) - (float)(pt.x * cos_theta);
237 ret.x = (int)wxRound(px);
238 ret.y = (int)wxRound(py);
245static void transrot_pts(
int n, wxPoint *pt,
float sin_theta,
float cos_theta,
246 wxPoint offset = wxPoint(0, 0)) {
247 for (
int i = 0; i < n; i++)
248 pt[i] = transrot(pt[i], sin_theta, cos_theta, offset);
252 if (cp == NULL)
return;
253 if (!
g_pAIS || !cp->GetShowAIS() || !g_bShowAreaNotices)
return;
255 wxDateTime now = wxDateTime::Now();
258 bool b_pens_set =
false;
264 wxBrush *yellow_brush = wxTheBrushList->FindOrCreateBrush(
265 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
266 wxBrush *green_brush = wxTheBrushList->FindOrCreateBrush(
267 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
273 for (
const auto &target :
g_pAIS->GetAreaNoticeSourcesList()) {
274 auto target_data = target.second;
275 if (!target_data->area_notices.empty()) {
277 pen_save = dc.GetPen();
278 brush_save = dc.GetBrush();
280 yellow = GetGlobalColor(
"YELO1");
281 yellow.Set(yellow.Red(), yellow.Green(), yellow.Blue(), 64);
283 green = GetGlobalColor(
"GREEN4");
284 green.Set(green.Red(), green.Green(), green.Blue(), 64);
286 pen.SetColour(yellow);
289 yellow_brush = wxTheBrushList->FindOrCreateBrush(
290 yellow, wxBRUSHSTYLE_CROSSDIAG_HATCH);
292 wxTheBrushList->FindOrCreateBrush(green, wxBRUSHSTYLE_TRANSPARENT);
293 brush = yellow_brush;
298 for (
auto &ani : target_data->area_notices) {
301 if (area_notice.expiry_time > now) {
302 std::vector<wxPoint> points;
303 bool draw_polygon =
false;
305 switch (area_notice.notice_type) {
307 pen.SetColour(green);
311 pen.SetColour(yellow);
312 brush = yellow_brush;
315 pen.SetColour(yellow);
316 brush = yellow_brush;
321 for (Ais8_001_22_SubAreaList::iterator sa =
322 area_notice.sub_areas.begin();
323 sa != area_notice.sub_areas.end(); ++sa) {
325 case AIS8_001_22_SHAPE_CIRCLE: {
326 wxPoint target_point;
327 GetCanvasPointPix(vp, cp, sa->latitude, sa->longitude,
329 points.push_back(target_point);
330 if (sa->radius_m > 0.0)
331 dc.DrawCircle(target_point, sa->radius_m * vp_scale);
334 case AIS8_001_22_SHAPE_RECT: {
335 wxPoint target_point;
336 double lat = sa->latitude;
337 double lon = sa->longitude;
338 int orient_east = 90 + sa->orient_deg;
339 if (orient_east > 360) orient_east -= 360;
340 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
341 points.push_back(target_point);
342 ll_gc_ll(lat, lon, orient_east, sa->e_dim_m / 1852.0, &lat,
344 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
345 points.push_back(target_point);
346 ll_gc_ll(lat, lon, sa->orient_deg, sa->n_dim_m / 1852.0, &lat,
348 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
349 points.push_back(target_point);
350 ll_gc_ll(sa->latitude, sa->longitude, sa->orient_deg,
351 sa->n_dim_m / 1852.0, &lat, &lon);
352 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
353 points.push_back(target_point);
357 case AIS8_001_22_SHAPE_SECTOR: {
358 wxPoint target_point;
360 double lat1 = sa->latitude;
361 double lon1 = sa->longitude;
362 GetCanvasPointPix(vp, cp, lat1, lon1, &target_point);
363 points.push_back(target_point);
365 for (
int i = 0; i < 18; ++i) {
369 i * (sa->right_bound_deg - sa->left_bound_deg) / 18,
370 sa->radius_m / 1852.0, &lat, &lon);
371 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
372 points.push_back(target_point);
375 ll_gc_ll(lat1, lon1, sa->right_bound_deg, sa->radius_m / 1852.0,
377 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
378 points.push_back(target_point);
383 case AIS8_001_22_SHAPE_POLYGON:
386 case AIS8_001_22_SHAPE_POLYLINE: {
387 double lat = sa->latitude;
388 double lon = sa->longitude;
389 for (
int i = 0; i < 4; ++i) {
390 ll_gc_ll(lat, lon, sa->angles[i], sa->dists_m[i] / 1852.0,
392 wxPoint target_point;
393 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
394 points.push_back(target_point);
396 dc.DrawLines(points.size(), &points.front());
400 if (draw_polygon) dc.DrawPolygon(points.size(), &points.front());
408 dc.SetBrush(brush_save);
412static void TargetFrame(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
414 int gap2 = 2 * radius / 6;
416 wxPen pen_save = dc.GetPen();
420 dc.
DrawLine(x - radius, y + gap2, x - radius, y + radius);
421 dc.
DrawLine(x - radius, y + radius, x - gap2, y + radius);
422 dc.
DrawLine(x + gap2, y + radius, x + radius, y + radius);
423 dc.
DrawLine(x + radius, y + radius, x + radius, y + gap2);
424 dc.
DrawLine(x + radius, y - gap2, x + radius, y - radius);
425 dc.
DrawLine(x + radius, y - radius, x + gap2, y - radius);
426 dc.
DrawLine(x - gap2, y - radius, x - radius, y - radius);
427 dc.
DrawLine(x - radius, y - radius, x - radius, y - gap2);
432static void AtoN_Diamond(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius,
435 radius *= gFrame->GetPrimaryCanvas()->GetContentScaleFactor();
438 wxPen pen_save = dc.GetPen();
441 wxPen aton_WhiteBorderPen;
444 int rad1a = radius / 2;
445 int rad2a = radius / 4;
451 if ((td->NavStatus == ATON_VIRTUAL_OFFPOSITION) ||
452 (td->NavStatus == ATON_REAL_OFFPOSITION))
453 aton_DrawPen = wxPen(GetGlobalColor(
"URED"), pen.GetWidth());
455 aton_DrawPen = wxPen(GetGlobalColor(
"UBLCK"), pen.GetWidth());
457 bool b_virt = (td->NavStatus == ATON_VIRTUAL) |
458 (td->NavStatus == ATON_VIRTUAL_ONPOSITION) |
459 (td->NavStatus == ATON_VIRTUAL_OFFPOSITION);
461 if (b_virt) aton_DrawPen.SetStyle(wxPENSTYLE_SHORT_DASH);
463 aton_WhiteBorderPen =
464 wxPen(GetGlobalColor(
"CHWHT"), aton_DrawPen.GetWidth() + 2);
469 diamond[0] = wxPoint(radius, 0);
470 diamond[1] = wxPoint(0, -radius);
471 diamond[2] = wxPoint(-radius, 0);
472 diamond[3] = wxPoint(0, radius);
473 diamond[4] = wxPoint(radius, 0);
474 dc.SetPen(aton_WhiteBorderPen);
475 dc.DrawLines(5, diamond, x, y);
476 dc.SetPen(aton_DrawPen);
477 dc.DrawLines(5, diamond, x, y);
480 wxPen(GetGlobalColor(
"UBLCK"),
482 aton_WhiteBorderPen =
483 wxPen(GetGlobalColor(
"CHWHT"), aton_DrawPen.GetWidth() + 2);
487 cross[0] = wxPoint(-rad2a, 0);
488 cross[1] = wxPoint(rad2a, 0);
489 cross[2] = wxPoint(0, 0);
490 cross[3] = wxPoint(0, rad2a);
491 cross[4] = wxPoint(0, -rad2a);
492 dc.SetPen(aton_WhiteBorderPen);
493 dc.DrawLines(5, cross, x, y);
494 dc.SetPen(aton_DrawPen);
495 dc.DrawLines(5, cross, x, y);
497 wxPoint TriPointUp[4];
498 TriPointUp[0] = wxPoint(-rad1a, 0);
499 TriPointUp[1] = wxPoint(rad1a, 0);
500 TriPointUp[2] = wxPoint(0, -rad1a);
501 TriPointUp[3] = wxPoint(-rad1a, 0);
503 wxPoint TriPointDown[4];
504 TriPointDown[0] = wxPoint(-rad1a, -rad1a);
505 TriPointDown[1] = wxPoint(rad1a, -rad1a);
506 TriPointDown[2] = wxPoint(0, 0);
507 TriPointDown[3] = wxPoint(-rad1a, -rad1a);
509 wxPoint CircleOpen[16];
510 CircleOpen[0] = wxPoint(-1, 5);
511 CircleOpen[1] = wxPoint(1, 5);
512 CircleOpen[2] = wxPoint(3, 4);
513 CircleOpen[3] = wxPoint(4, 3);
514 CircleOpen[4] = wxPoint(5, 1);
515 CircleOpen[5] = wxPoint(5, -1);
516 CircleOpen[6] = wxPoint(4, -3);
517 CircleOpen[7] = wxPoint(3, -4);
518 CircleOpen[8] = wxPoint(1, -5);
519 CircleOpen[9] = wxPoint(-1, -5);
520 CircleOpen[10] = wxPoint(-3, -4);
521 CircleOpen[11] = wxPoint(-4, -3);
522 CircleOpen[12] = wxPoint(-5, -1);
523 CircleOpen[13] = wxPoint(-4, 3);
524 CircleOpen[14] = wxPoint(-3, 4);
525 CircleOpen[15] = wxPoint(-1, 5);
527 switch (td->ShipType) {
530 dc.SetPen(aton_WhiteBorderPen);
531 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
532 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
533 dc.SetPen(aton_DrawPen);
534 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
535 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
539 dc.SetPen(aton_WhiteBorderPen);
540 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
541 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
542 dc.SetPen(aton_DrawPen);
543 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
544 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
548 dc.SetPen(aton_WhiteBorderPen);
549 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
550 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
551 dc.SetPen(aton_DrawPen);
552 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
553 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
557 dc.SetPen(aton_WhiteBorderPen);
558 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
559 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
560 dc.SetPen(aton_DrawPen);
561 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
562 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
567 aRect[0] = wxPoint(-rad3a, 0);
568 aRect[1] = wxPoint(-rad3a, -rad3a - rad3a);
569 aRect[2] = wxPoint(rad3a, -rad3a - rad3a);
570 aRect[3] = wxPoint(rad3a, 0);
571 aRect[4] = wxPoint(-rad3a, 0);
572 dc.SetPen(aton_WhiteBorderPen);
573 dc.DrawLines(5, aRect, x, y - radius - 1);
574 dc.SetPen(aton_DrawPen);
575 dc.DrawLines(5, aRect, x, y - radius - 1);
579 dc.SetPen(aton_WhiteBorderPen);
580 dc.DrawLines(4, TriPointUp, x, y - radius);
581 dc.SetPen(aton_DrawPen);
582 dc.DrawLines(4, TriPointUp, x, y - radius);
586 dc.SetPen(aton_WhiteBorderPen);
587 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
588 dc.SetPen(aton_DrawPen);
589 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
590 dc.SetPen(aton_WhiteBorderPen);
591 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
592 dc.SetPen(aton_DrawPen);
593 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
597 dc.SetPen(aton_WhiteBorderPen);
598 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
599 dc.SetPen(aton_DrawPen);
600 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
604 cross[0] = wxPoint(-rad2a, -rad2a);
605 cross[1] = wxPoint(rad2a, rad2a);
606 cross[2] = wxPoint(0, 0);
607 cross[3] = wxPoint(-rad2a, rad2a);
608 cross[4] = wxPoint(rad2a, -rad2a);
609 dc.SetPen(aton_WhiteBorderPen);
610 dc.DrawLines(5, cross, x, y - radius - rad3a);
611 dc.SetPen(aton_DrawPen);
612 dc.DrawLines(5, cross, x, y - radius - rad3a);
620static void Base_Square(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
622 int gap2 = 2 * radius / 6;
623 int pen_width = pen.GetWidth();
625 wxPen pen_save = dc.GetPen();
629 dc.
DrawLine(x - radius, y - radius, x - radius, y + radius);
630 dc.
DrawLine(x - radius, y + radius, x + radius, y + radius);
631 dc.
DrawLine(x + radius, y + radius, x + radius, y - radius);
632 dc.
DrawLine(x + radius, y - radius, x - radius, y - radius);
636 pen.SetWidth(pen_width);
639 dc.
DrawLine(x - gap2, y, x + gap2, y);
640 dc.
DrawLine(x, y - gap2, x, y + gap2);
645static void SART_Render(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
647 int gap = (radius * 12) / 10;
648 int pen_width = pen.GetWidth();
650 wxPen pen_save = dc.GetPen();
654 wxBrush brush_save = dc.GetBrush();
655 wxBrush *ppBrush = wxTheBrushList->FindOrCreateBrush(
656 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
657 dc.SetBrush(*ppBrush);
659 dc.DrawCircle(x, y, radius);
663 pen.SetWidth(pen_width);
669 dc.SetBrush(brush_save);
675static void spherical_ll_gc_ll(
float lat,
float lon,
float brg,
float dist,
676 float *dlat,
float *dlon) {
677 float angr = brg / 180 * M_PI;
678 float latr = lat * M_PI / 180;
679 float D = dist / 3443;
680 float sD = sinf(D), cD = cosf(D);
681 float sy = sinf(latr), cy = cosf(latr);
682 float sa = sinf(angr), ca = cosf(angr);
684 *dlon = lon + asinf(sa * sD / cy) * 180 / M_PI;
685 *dlat = asinf(sy * cD + cy * sD * ca) * 180 / M_PI;
689float AIS_scale_factor;
690float AIS_ATON_scale_factor;
691float AIS_nominal_target_size_mm;
692float AIS_nominal_icon_size_pixels;
694float AIS_user_scale_factor;
695double AIS_nominal_line_width_pix;
697float AIS_width_interceptbar_base;
698float AIS_width_interceptbar_top;
699float AIS_intercept_bar_circle_diameter;
700float AIS_width_interceptline;
701float AIS_width_cogpredictor_base;
702float AIS_width_cogpredictor_line;
703float AIS_width_target_outline;
704float AIS_icon_diameter;
705float AIS_ATON_reference;
709static void AISSetMetrics() {
712 double DPIscale = 1.0;
723 AIS_nominal_target_size_mm = 30.0 / g_Platform->GetDisplayDPmm();
727 AIS_nominal_target_size_mm = wxMin(AIS_nominal_target_size_mm, 10.0);
728 AIS_nominal_target_size_mm = wxMax(AIS_nominal_target_size_mm, 5.0);
730 AIS_nominal_icon_size_pixels =
731 wxMax(4.0, g_Platform->GetDisplayDPmm() *
732 AIS_nominal_target_size_mm);
734 AIS_pix_factor = AIS_nominal_icon_size_pixels /
737 AIS_scale_factor *= AIS_pix_factor;
739 AIS_user_scale_factor = g_ShipScaleFactorExp;
740 if (g_ShipScaleFactorExp > 1.0)
741 AIS_user_scale_factor = (log(g_ShipScaleFactorExp) + 1.0) *
743 AIS_scale_factor *= AIS_user_scale_factor;
746 AIS_ATON_scale_factor = g_MarkScaleFactorExp;
747 if (g_MarkScaleFactorExp > 1.0)
748 AIS_ATON_scale_factor = (log(g_MarkScaleFactorExp) + 1.0) *
753 AIS_nominal_line_width_pix =
754 wxMax(2, g_Platform->GetDisplayDPmm() / (4.0 / DPIscale));
757 AIS_width_interceptbar_base = 3 * AIS_nominal_line_width_pix;
758 AIS_width_interceptbar_top = 1.5 * AIS_nominal_line_width_pix;
759 AIS_intercept_bar_circle_diameter = 3.5 * AIS_nominal_line_width_pix;
760 AIS_width_interceptline = 2 * AIS_nominal_line_width_pix;
761 AIS_width_cogpredictor_base = 3 * AIS_nominal_line_width_pix;
762 AIS_width_cogpredictor_line = 1.3 * AIS_nominal_line_width_pix;
763 AIS_width_target_outline = 1.4 * AIS_nominal_line_width_pix;
764 AIS_icon_diameter = AIS_intercept_bar_circle_diameter * AIS_user_scale_factor;
768 AIS_intercept_bar_circle_diameter * AIS_ATON_scale_factor;
770 wxFont *font = FontMgr::Get().
GetFont(_(
"AIS Target Name"), 0);
771 double scaler = DPIscale;
774 font->GetPointSize() / scaler, font->GetFamily(), font->GetStyle(),
775 font->GetWeight(),
false, font->GetFaceName());
781 if (NULL == td)
return;
783 static bool firstTimeUse =
true;
786 g_AisFirstTimeUse =
true;
789 g_AisFirstTimeUse =
false;
790 firstTimeUse =
false;
794 if (td->b_lost)
return;
799 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts) &&
800 (td->NavStatus != NOT_UNDER_COMMAND) &&
801 ((td->Class == AIS_CLASS_A) || (td->Class == AIS_CLASS_B)))
805 if (!td->b_positionOnceValid)
return;
808 if (td->b_OwnShip)
return;
811 float target_sog = td->SOG;
812 if ((td->SOG > 102.2) && !td->b_SarAircraftPosnReport) target_sog = 0.;
815 wxPoint TargetPoint, PredPoint;
818 if (td->n_alert_state == AIS_ALERT_SET) {
822 if (vp.GetBBox().Contains(td->Lat, td->Lon))
826 if (1 && td->b_show_track) {
827 if (td->m_ptrack.size() > 0) {
829 if (vp.GetBBox().Contains(ptrack_point.m_lat, ptrack_point.m_lon))
839 float pred_lat, pred_lon;
840 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
841 target_sog * g_ShowCOG_Mins / 60., &pred_lat, &pred_lon);
844 if (vp.GetBBox().Contains(pred_lat, pred_lon))
848 box.SetFromSegment(td->Lat, td->Lon, pred_lat, pred_lon);
851 if (!vp.GetBBox().IntersectOut(box)) drawit++;
857 GetCanvasPointPix(vp, cp, td->Lat, td->Lon, &TargetPoint);
858 GetCanvasPointPix(vp, cp, pred_lat, pred_lon, &PredPoint);
860 bool b_hdgValid =
true;
862 float theta = (float)-PI / 2.;
864 if ((
int)(td->HDG) != 511) {
865 theta = ((td->HDG - 90) * PI / 180.) + vp.
rotation;
869 if (!g_bInlandEcdis) {
875 float angle_lat, angle_lon;
876 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG, angle_distance_nm,
877 &angle_lat, &angle_lon);
880 GetCanvasPointPix(vp, cp, angle_lat, angle_lon, &AnglePoint);
882 if (abs(AnglePoint.x - TargetPoint.x) > 0) {
883 if (target_sog > g_SOGminCOG_kts) {
884 theta = atan2f((
double)(AnglePoint.y - TargetPoint.y),
885 (
double)(AnglePoint.x - TargetPoint.x));
888 theta = (float)-PI / 2.;
890 if (AnglePoint.y > TargetPoint.y)
891 theta = (float)PI / 2.;
895 if (td->SOG >= g_SOGminCOG_kts)
904 float sin_theta = sinf(theta), cos_theta = cosf(theta);
907 dash_long[0] = (int)(1.0 * gFrame->GetPrimaryCanvas()
910 (int)(0.5 * gFrame->GetPrimaryCanvas()->
GetPixPerMM());
912 int targetscale = 100;
915 idxCC = cp->m_canvasIndex;
917 if (idxCC > AIS_TARGETDATA_MAX_CANVAS - 1)
920 if (cp->GetAttenAIS()) {
921 if (td->NavStatus <= 15) {
925 if (td->importance < AISImportanceSwitchPoint)
926 targetscale = td->last_scale[idxCC] - 2;
928 if (td->importance > AISImportanceSwitchPoint)
929 targetscale = td->last_scale[idxCC] + 5;
930 if (targetscale > 100) targetscale = 100;
931 if (targetscale < 50) targetscale = 50;
932 td->last_scale[idxCC] = targetscale;
938 wxPoint ais_real_size[6];
939 bool bcan_draw_size =
true;
940 if (g_bDrawAISSize) {
941 if (td->DimA + td->DimB == 0 || td->DimC + td->DimD == 0) {
942 bcan_draw_size =
false;
944 double ref_lat, ref_lon;
945 ll_gc_ll(td->Lat, td->Lon, 0, 100. / 1852., &ref_lat, &ref_lon);
948 double ppm = r_point.GetDistance(b_point) / 100.;
949 double offwid = (td->DimC + td->DimD) * ppm * 0.25;
950 double offlen = (td->DimA + td->DimB) * ppm * 0.15;
951 ais_real_size[0].x = -td->DimD * ppm;
952 ais_real_size[0].y = -td->DimB * ppm;
953 ais_real_size[1].x = -td->DimD * ppm;
954 ais_real_size[1].y = td->DimA * ppm - offlen;
955 ais_real_size[2].x = -td->DimD * ppm + offwid;
956 ais_real_size[2].y = td->DimA * ppm;
957 ais_real_size[3].x = td->DimC * ppm - offwid;
958 ais_real_size[3].y = td->DimA * ppm;
959 ais_real_size[4].x = td->DimC * ppm;
960 ais_real_size[4].y = td->DimA * ppm - offlen;
961 ais_real_size[5].x = td->DimC * ppm;
962 ais_real_size[5].y = -td->DimB * ppm;
964 if (ais_real_size[4].x - ais_real_size[0].x < 16 ||
965 ais_real_size[2].y - ais_real_size[0].y < 30)
966 bcan_draw_size =
false;
968 bcan_draw_size =
true;
969 transrot_pts(6, ais_real_size, sin_theta, cos_theta);
976 wxPoint ais_quad_icon[4] = {wxPoint(-8, -6), wxPoint(0, 24), wxPoint(8, -6),
978 wxPoint ais_octo_icon[8] = {wxPoint(4, 8), wxPoint(8, 4), wxPoint(8, -4),
979 wxPoint(4, -8), wxPoint(-4, -8), wxPoint(-8, -4),
980 wxPoint(-8, 4), wxPoint(-4, 8)};
982 if (!g_bInlandEcdis) {
984 if (targetscale == 50) {
985 ais_quad_icon[0] = wxPoint(-4, -3);
986 ais_quad_icon[1] = wxPoint(0, 12);
987 ais_quad_icon[2] = wxPoint(4, -3);
988 ais_quad_icon[3] = wxPoint(0, -3);
989 }
else if (targetscale != 100) {
991 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
992 ais_quad_icon[1] = wxPoint(0, (
int)24 * targetscale / 100);
994 wxPoint((
int)8 * targetscale / 100, (
int)-6 * targetscale / 100);
995 ais_quad_icon[3] = wxPoint(0, (
int)-6 * targetscale / 100);
999 if (td->Class == AIS_CLASS_B) ais_quad_icon[3].y = 0;
1001 if ((td->Class == AIS_GPSG_BUDDY) || (td->b_isFollower)) {
1002 ais_quad_icon[0] = wxPoint(-5, -12);
1003 ais_quad_icon[1] = wxPoint(-3, 12);
1004 ais_quad_icon[2] = wxPoint(3, 12);
1005 ais_quad_icon[3] = wxPoint(5, -12);
1006 }
else if (td->Class == AIS_DSC) {
1007 ais_quad_icon[0].y = 0;
1008 ais_quad_icon[1].y = 8;
1009 ais_quad_icon[2].y = 0;
1010 ais_quad_icon[3].y = -8;
1011 }
else if (td->Class == AIS_APRS) {
1012 ais_quad_icon[0] = wxPoint(-8, -8);
1013 ais_quad_icon[1] = wxPoint(-8, 8);
1014 ais_quad_icon[2] = wxPoint(8, 8);
1015 ais_quad_icon[3] = wxPoint(8, -8);
1018 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1021 iconPoints = ais_quad_icon;
1025 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1027 iconPoints = ais_quad_icon;
1030 iconPoints = ais_octo_icon;
1034 wxColour UBLCK = GetGlobalColor(
"UBLCK");
1035 dc.SetPen(wxPen(UBLCK));
1038 wxColour UINFG = GetGlobalColor(
"UINFG");
1039 wxBrush target_brush = wxBrush(UINFG);
1042 if (td->b_isEuroInland && !g_bInlandEcdis)
1043 target_brush = wxBrush(GetGlobalColor(
"TEAL1"));
1046 if (td->b_nameFromCache) target_brush = wxBrush(GetGlobalColor(
"GREEN5"));
1049 wxColour URED = GetGlobalColor(
"URED");
1050 if (!td->b_nameValid) target_brush = wxBrush(GetGlobalColor(
"CHYLW"));
1052 if ((td->Class == AIS_DSC) &&
1053 ((td->ShipType == 12) || (td->ShipType == 16)))
1054 target_brush = wxBrush(URED);
1056 if (td->b_SarAircraftPosnReport) target_brush = wxBrush(UINFG);
1058 if ((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid))
1059 target_brush = wxBrush(URED);
1061 if ((td->n_alert_state == AIS_ALERT_NO_DIALOG_SET) && (td->bCPA_Valid) &&
1062 (!td->b_isFollower))
1063 target_brush = wxBrush(URED);
1065 if (td->b_positionDoubtful) target_brush = wxBrush(GetGlobalColor(
"UINFF"));
1067 wxPen target_outline_pen(UBLCK, AIS_width_target_outline);
1070 if (((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid)) ||
1071 (td->b_show_AIS_CPA && (td->bCPA_Valid))) {
1073 double tcpa_lat, tcpa_lon;
1074 ll_gc_ll(td->Lat, td->Lon, td->COG, target_sog * td->TCPA / 60., &tcpa_lat,
1077 wxPoint TPoint = TargetPoint;
1078 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1081 ClipResult res = cohen_sutherland_line_clip_i(
1082 &TPoint.x, &TPoint.y, &tCPAPoint.x, &tCPAPoint.y, 0, vp.
pix_width, 0,
1085 if (res != Invisible) {
1086 wxPen ppPen2(URED, AIS_width_cogpredictor_line, wxPENSTYLE_USER_DASH);
1087 ppPen2.SetDashes(2, dash_long);
1090 dc.StrokeLine(TPoint.x, TPoint.y, tCPAPoint.x, tCPAPoint.y);
1094 double ocpa_lat, ocpa_lon;
1097 if (std::isnan(
gCog) || std::isnan(
gSog)) {
1106 GetCanvasPointPix(vp, cp, ocpa_lat, ocpa_lon, &oCPAPoint);
1107 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1110 wxPoint oCPAPoint_unclipped = oCPAPoint;
1111 wxPoint tCPAPoint_unclipped = tCPAPoint;
1114 ClipResult ores = cohen_sutherland_line_clip_i(
1115 &tCPAPoint.x, &tCPAPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0, vp.
pix_width,
1118 if (ores != Invisible) {
1119 wxColour yellow = GetGlobalColor(
"YELO1");
1120 dc.SetPen(wxPen(yellow, AIS_width_interceptbar_base));
1121 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1123 wxPen ppPen2(URED, AIS_width_interceptbar_top, wxPENSTYLE_USER_DASH);
1124 ppPen2.SetDashes(2, dash_long);
1126 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1129 wxBrush br(GetGlobalColor(
"BLUE3"));
1131 dc.SetPen(wxPen(UBLCK, AIS_width_target_outline));
1135 tCPAPoint_unclipped.x, tCPAPoint_unclipped.y,
1136 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1138 oCPAPoint_unclipped.x, oCPAPoint_unclipped.y,
1139 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1144 GetCanvasPointPix(vp, cp,
gLat,
gLon, &oShipPoint);
1145 oCPAPoint = oCPAPoint_unclipped;
1147 ClipResult ownres = cohen_sutherland_line_clip_i(
1148 &oShipPoint.x, &oShipPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0,
1151 if (ownres != Invisible) {
1152 wxPen ppPen2(URED, AIS_width_interceptline, wxPENSTYLE_USER_DASH);
1153 ppPen2.SetDashes(2, dash_long);
1156 dc.StrokeLine(oShipPoint.x, oShipPoint.y, oCPAPoint.x, oCPAPoint.y);
1159 dc.SetPen(wxPen(UBLCK));
1160 dc.SetBrush(wxBrush(URED));
1166 auto alert_dlg_active =
1168 if (alert_dlg_active && alert_dlg_active->IsShown() && cp) {
1169 if (alert_dlg_active->Get_Dialog_MMSI() == td->MMSI)
1170 cp->JaggyCircle(dc, wxPen(URED, 2), TargetPoint.x, TargetPoint.y, 100);
1175 if (g_pais_query_dialog_active && g_pais_query_dialog_active->IsShown()) {
1176 if (g_pais_query_dialog_active->GetMMSI() == td->MMSI)
1177 TargetFrame(dc, wxPen(UBLCK, 2), TargetPoint.x, TargetPoint.y, 25);
1182 if ((g_bShowCOG) && (target_sog > g_SOGminCOG_kts) && td->b_active) {
1183 int pixx = TargetPoint.x;
1184 int pixy = TargetPoint.y;
1185 int pixx1 = PredPoint.x;
1186 int pixy1 = PredPoint.y;
1190 float l = sqrtf(powf((
float)(PredPoint.x - TargetPoint.x), 2) +
1191 powf((
float)(PredPoint.y - TargetPoint.y), 2));
1194 ClipResult res = cohen_sutherland_line_clip_i(
1197 if (res != Invisible) {
1199 if (targetscale >= 75) {
1200 wxPen wide_pen(target_brush.GetColour(), AIS_width_cogpredictor_base);
1201 dc.SetPen(wide_pen);
1202 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1205 if (AIS_width_cogpredictor_base > 1) {
1207 wxPen narrow_pen(UBLCK, AIS_width_cogpredictor_line);
1208 if (targetscale < 75) {
1209 narrow_pen.SetWidth(1);
1210 narrow_pen.SetStyle(wxPENSTYLE_USER_DASH);
1214 narrow_pen.SetDashes(2, dash_dot);
1216 dc.SetPen(narrow_pen);
1217 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1221 dc.SetBrush(target_brush);
1222 dc.StrokeCircle(PredPoint.x, PredPoint.y, 5 * targetscale / 100);
1227#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1230 glTranslated(PredPoint.x, PredPoint.y, 0);
1231 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1233 float points[] = {0.0f, 5.0f, 2.5f, 4.330127f, 4.330127f,
1234 2.5f, 5.0f, 0, 4.330127f, -2.5f,
1235 2.5f, -4.330127f, 0, -5.1f, -2.5f,
1236 -4.330127f, -4.330127f, -2.5f, -5.0f, 0,
1237 -4.330127f, 2.5f, -2.5f, 4.330127f, 0,
1239 if (targetscale <= 75) {
1240 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1242 points[i] = points[i] / 2;
1245 wxColour c = target_brush.GetColour();
1246 glColor3ub(c.Red(), c.Green(), c.Blue());
1248 glBegin(GL_TRIANGLE_FAN);
1249 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1251 glVertex2i(points[i], points[i + 1]);
1254 glColor3ub(0, 0, 0);
1255 glLineWidth(AIS_width_target_outline);
1256 glBegin(GL_LINE_LOOP);
1257 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1259 glVertex2i(points[i], points[i + 1]);
1264 dc.SetBrush(target_brush);
1265 dc.StrokeCircle(PredPoint.x, PredPoint.y,
1266 AIS_intercept_bar_circle_diameter *
1267 AIS_user_scale_factor * targetscale / 100);
1274 if ((td->ROTAIS != 0) && (td->ROTAIS != -128) && (!g_bShowScaled)) {
1275 float cog_angle = td->COG * PI / 180.;
1277 float theta2 = theta;
1278 if (td->SOG >= g_SOGminCOG_kts)
1279 theta2 = cog_angle - (PI / 2);
1283 theta2 += (float)PI / 2;
1285 theta2 -= (float)PI / 2;
1287 int xrot = (int)round(pixx1 + (nv * cosf(theta2)));
1288 int yrot = (int)round(pixy1 + (nv * sinf(theta2)));
1289 dc.StrokeLine(pixx1, pixy1, xrot, yrot);
1295 if (td->Class == AIS_ARPA || td->Class == AIS_BUOY) {
1296 wxPen target_pen(UBLCK, 2);
1298 dc.SetPen(target_pen);
1299 dc.SetBrush(target_brush);
1300 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1.8 * AIS_icon_diameter);
1302 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1);
1304 if (!td->b_active) {
1305 dc.SetPen(wxPen(UBLCK, 2));
1306 dc.StrokeLine(TargetPoint.x - 14, TargetPoint.y, TargetPoint.x + 14,
1308 dc.SetPen(wxPen(UBLCK, 1));
1311 }
else if (td->Class == AIS_METEO) {
1312 wxPen met(UBLCK, (wxMax(target_outline_pen.GetWidth(), 2.5)));
1314 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1315 double met_radius = 1.5 * AIS_ATON_reference;
1316 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, met_radius);
1319 dc.SetPen(wxPen(wxMax(target_outline_pen.GetWidth(), 1)));
1321 dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4,
1322 TargetPoint.x - met_radius / 3,
1323 TargetPoint.y + met_radius / 2);
1325 TargetPoint.x - met_radius / 3, TargetPoint.y + met_radius / 2,
1326 TargetPoint.x - met_radius / 2, TargetPoint.y - met_radius / 2);
1328 dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4,
1329 TargetPoint.x + met_radius / 3,
1330 TargetPoint.y + met_radius / 2);
1332 TargetPoint.x + met_radius / 3, TargetPoint.y + met_radius / 2,
1333 TargetPoint.x + met_radius / 2, TargetPoint.y - met_radius / 2);
1335 }
else if (td->Class == AIS_ATON) {
1336 AtoN_Diamond(dc, wxPen(UBLCK, AIS_width_target_outline), TargetPoint.x,
1337 TargetPoint.y, AIS_ATON_reference * 1.5, td);
1338 }
else if (td->Class == AIS_BASE) {
1339 Base_Square(dc, wxPen(UBLCK, AIS_width_target_outline), TargetPoint.x,
1340 TargetPoint.y, AIS_ATON_reference);
1341 }
else if (td->Class == AIS_SART) {
1342 if (td->NavStatus == 14)
1343 SART_Render(dc, wxPen(URED, AIS_width_target_outline), TargetPoint.x,
1344 TargetPoint.y, AIS_ATON_reference);
1346 SART_Render(dc, wxPen(GetGlobalColor(
"UGREN"), AIS_width_target_outline),
1347 TargetPoint.x, TargetPoint.y, AIS_ATON_reference);
1349 }
else if (td->b_SarAircraftPosnReport) {
1350 int airtype = (td->MMSI % 1000) / 100;
1351 int ar = airtype == 5 ? 15 : 9;
1352 wxPoint SarIcon[15];
1354 double scaleplus = 1.4;
1356 SarIcon[0] = wxPoint(0, 9) * AIS_scale_factor * scaleplus;
1357 SarIcon[1] = wxPoint(1, 1) * AIS_scale_factor * scaleplus;
1358 SarIcon[2] = wxPoint(2, 1) * AIS_scale_factor * scaleplus;
1359 SarIcon[3] = wxPoint(9, 8) * AIS_scale_factor * scaleplus;
1360 SarIcon[4] = wxPoint(9, 7) * AIS_scale_factor * scaleplus;
1361 SarIcon[5] = wxPoint(3, 0) * AIS_scale_factor * scaleplus;
1362 SarIcon[6] = wxPoint(3, -5) * AIS_scale_factor * scaleplus;
1363 SarIcon[7] = wxPoint(9, -12) * AIS_scale_factor * scaleplus;
1364 SarIcon[8] = wxPoint(9, -13) * AIS_scale_factor * scaleplus;
1365 SarIcon[9] = wxPoint(2, -5) * AIS_scale_factor * scaleplus;
1366 SarIcon[10] = wxPoint(1, -15) * AIS_scale_factor * scaleplus;
1367 SarIcon[11] = wxPoint(3, -16) * AIS_scale_factor * scaleplus;
1368 SarIcon[12] = wxPoint(4, -18) * AIS_scale_factor * scaleplus;
1369 SarIcon[13] = wxPoint(1, -18) * AIS_scale_factor * scaleplus;
1370 SarIcon[14] = wxPoint(0, -19) * AIS_scale_factor * scaleplus;
1372 SarIcon[0] = wxPoint(0, 12) * AIS_scale_factor;
1373 SarIcon[1] = wxPoint(4, 2) * AIS_scale_factor;
1374 SarIcon[2] = wxPoint(16, -2) * AIS_scale_factor;
1375 SarIcon[3] = wxPoint(16, -8) * AIS_scale_factor;
1376 SarIcon[4] = wxPoint(4, -8) * AIS_scale_factor;
1377 SarIcon[5] = wxPoint(3, -16) * AIS_scale_factor;
1378 SarIcon[6] = wxPoint(10, -18) * AIS_scale_factor;
1379 SarIcon[7] = wxPoint(10, -22) * AIS_scale_factor;
1380 SarIcon[8] = wxPoint(0, -22) * AIS_scale_factor;
1387 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1388 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1390 wxPen tri_pen(target_brush.GetColour(), 1);
1392 dc.SetBrush(target_brush);
1395 int mappings[14][3] = {
1396 {0, 1, 10}, {0, 10, 14}, {1, 2, 9}, {1, 9, 10}, {10, 13, 14},
1397 {10, 11, 13}, {11, 12, 13}, {1, 14, 10}, {2, 5, 9}, {5, 6, 9},
1398 {2, 3, 5}, {3, 4, 5}, {6, 7, 8}, {6, 9, 8}};
1401 for (
int i = 0; i < nmap; i++) {
1402 wxPoint ais_tri_icon[3];
1403 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1404 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1407 dc.SetPen(target_outline_pen);
1408 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1409 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1413 for (
int i = 0; i < ar; i++)
1415 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1417 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1420 dc.SetBrush(target_brush);
1422 for (
int i = 0; i < nmap; i++) {
1423 wxPoint ais_tri_icon[3];
1424 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1425 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1428 dc.SetPen(target_outline_pen);
1429 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1430 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1435 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1436 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1438 wxPen tri_pen(target_brush.GetColour(), 1);
1440 dc.SetBrush(target_brush);
1443 int mappings[7][3] = {{0, 1, 4}, {1, 2, 3}, {1, 3, 4}, {0, 4, 5},
1444 {0, 5, 8}, {5, 6, 7}, {5, 7, 8}};
1445 for (
int i = 0; i < 7; i++) {
1446 wxPoint ais_tri_icon[3];
1447 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1448 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1451 dc.SetPen(target_outline_pen);
1452 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1453 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1457 for (
int i = 0; i < ar; i++)
1459 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1461 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1464 dc.SetBrush(target_brush);
1466 for (
int i = 0; i < 7; i++) {
1467 wxPoint ais_tri_icon[3];
1468 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1469 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1472 dc.SetPen(target_outline_pen);
1473 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1474 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1478 if (!td->b_active) {
1479 wxPoint linepoint = TargetPoint;
1480 wxPoint p1 = transrot(
1481 wxPoint((
int)-14 * targetscale / 100, (
int)-5 * targetscale / 100),
1482 sin_theta, cos_theta, TargetPoint);
1483 wxPoint p2 = transrot(
1484 wxPoint((
int)14 * targetscale / 100, (
int)-5 * targetscale / 100),
1485 sin_theta, cos_theta, TargetPoint);
1487 dc.SetPen(wxPen(UBLCK, 2));
1488 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1492 wxPen target_pen(UBLCK, 1);
1493 dc.SetPen(target_pen);
1495 wxPoint Point = TargetPoint;
1496 if (g_bDrawAISRealtime &&
1497 (td->Class == AIS_CLASS_A || td->Class == AIS_CLASS_B) &&
1498 td->SOG > g_AIS_RealtPred_Kts && td->SOG < 102.2) {
1499 wxDateTime now = wxDateTime::Now();
1501 int target_age = now.GetTicks() - td->PositionReportTicks;
1504 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
1505 td->SOG * target_age / 3600.0, &lat, &lon);
1507 GetCanvasPointPix(vp, cp, lat, lon, &Point);
1509 wxBrush realtime_brush = wxBrush(GetGlobalColor(
"GREY1"));
1510 dc.SetBrush(realtime_brush);
1511 dc.StrokePolygon(nPoints, iconPoints, Point.x, Point.y, AIS_scale_factor);
1513 dc.SetBrush(target_brush);
1516 dc.StrokePolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1521#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1523 wxColour c = target_brush.GetColour();
1524 glColor3ub(c.Red(), c.Green(), c.Blue());
1527 glTranslated(TargetPoint.x, TargetPoint.y, 0);
1528 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1530 glBegin(GL_TRIANGLE_FAN);
1533 glVertex2i(ais_quad_icon[3].x, ais_quad_icon[3].y);
1534 glVertex2i(ais_quad_icon[0].x, ais_quad_icon[0].y);
1535 glVertex2i(ais_quad_icon[1].x, ais_quad_icon[1].y);
1536 glVertex2i(ais_quad_icon[2].x, ais_quad_icon[2].y);
1538 for (
int i = 0; i < 8; i++) {
1539 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1544 glLineWidth(AIS_width_target_outline);
1546 glColor3ub(UBLCK.Red(), UBLCK.Green(), UBLCK.Blue());
1548 glBegin(GL_LINE_LOOP);
1549 for (
int i = 0; i < nPoints; i++)
1550 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1555 dc.SetPen(target_outline_pen);
1556 dc.DrawPolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1562 if (td->b_isFollower) {
1563 wxPoint ais_follow_stroke[3];
1564 ais_follow_stroke[0] = wxPoint(-3, -20) * AIS_scale_factor;
1565 ais_follow_stroke[1] = wxPoint(0, 0) * AIS_scale_factor;
1566 ais_follow_stroke[2] = wxPoint(3, -20) * AIS_scale_factor;
1568 transrot_pts(3, ais_follow_stroke, sin_theta, cos_theta);
1570 int penWidth = wxMax(target_outline_pen.GetWidth(), 2);
1571 dc.SetPen(wxPen(UBLCK, penWidth));
1572 dc.StrokeLine(ais_follow_stroke[0].x + TargetPoint.x,
1573 ais_follow_stroke[0].y + TargetPoint.y,
1574 ais_follow_stroke[1].x + TargetPoint.x,
1575 ais_follow_stroke[1].y + TargetPoint.y);
1576 dc.StrokeLine(ais_follow_stroke[1].x + TargetPoint.x,
1577 ais_follow_stroke[1].y + TargetPoint.y,
1578 ais_follow_stroke[2].x + TargetPoint.x,
1579 ais_follow_stroke[2].y + TargetPoint.y);
1582 if (g_bDrawAISSize && bcan_draw_size) {
1583 dc.SetPen(target_outline_pen);
1584 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1585 if (!g_bInlandEcdis) {
1586 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1589 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1594 dc.SetBrush(wxBrush(GetGlobalColor(
"SHIPS")));
1595 int navstatus = td->NavStatus;
1599 if (((td->ShipType >= 40) && (td->ShipType < 50)) &&
1600 (navstatus == UNDERWAY_USING_ENGINE || td->Class == AIS_CLASS_B))
1603 if (targetscale > 90) {
1604 switch (navstatus) {
1607 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1610 case RESTRICTED_MANOEUVRABILITY: {
1612 diamond[0] = wxPoint(4, 0) * AIS_scale_factor;
1613 diamond[1] = wxPoint(0, -6) * AIS_scale_factor;
1614 diamond[2] = wxPoint(-4, 0) * AIS_scale_factor;
1615 diamond[3] = wxPoint(0, 6) * AIS_scale_factor;
1616 dc.StrokePolygon(4, diamond, TargetPoint.x,
1617 TargetPoint.y - (11 * AIS_scale_factor));
1618 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1619 dc.StrokeCircle(TargetPoint.x,
1620 TargetPoint.y - (22 * AIS_scale_factor),
1621 4 * AIS_scale_factor);
1625 case CONSTRAINED_BY_DRAFT: {
1626 wxPoint can[4] = {wxPoint(-3, 0) * AIS_scale_factor,
1627 wxPoint(3, 0) * AIS_scale_factor,
1628 wxPoint(3, -16) * AIS_scale_factor,
1629 wxPoint(-3, -16) * AIS_scale_factor};
1630 dc.StrokePolygon(4, can, TargetPoint.x, TargetPoint.y);
1633 case NOT_UNDER_COMMAND: {
1634 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1635 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1636 4 * AIS_scale_factor);
1641 tri[0] = wxPoint(-4, 0) * AIS_scale_factor;
1642 tri[1] = wxPoint(4, 0) * AIS_scale_factor;
1643 tri[2] = wxPoint(0, -9) * AIS_scale_factor;
1644 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1645 tri[0] = wxPoint(0, -9) * AIS_scale_factor;
1646 tri[1] = wxPoint(4, -18) * AIS_scale_factor;
1647 tri[2] = wxPoint(-4, -18) * AIS_scale_factor;
1648 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1652 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1653 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1654 4 * AIS_scale_factor);
1655 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 18,
1656 4 * AIS_scale_factor);
1661 dc.SetBrush(target_brush);
1663 wxPoint arrow1[3] = {wxPoint(-4, 20) * AIS_scale_factor,
1664 wxPoint(0, 27) * AIS_scale_factor,
1665 wxPoint(4, 20) * AIS_scale_factor};
1666 transrot_pts(3, arrow1, sin_theta, cos_theta, TargetPoint);
1667 dc.StrokePolygon(3, arrow1);
1669 wxPoint arrow2[3] = {wxPoint(-4, 27) * AIS_scale_factor,
1670 wxPoint(0, 34) * AIS_scale_factor,
1671 wxPoint(4, 27) * AIS_scale_factor};
1672 transrot_pts(3, arrow2, sin_theta, cos_theta, TargetPoint);
1673 dc.StrokePolygon(3, arrow2);
1680 if (!td->b_active) {
1681 wxPoint p1 = transrot(wxPoint((
int)-14 * targetscale / 100, 0), sin_theta,
1682 cos_theta, TargetPoint);
1683 wxPoint p2 = transrot(wxPoint((
int)14 * targetscale / 100, 0), sin_theta,
1684 cos_theta, TargetPoint);
1686 dc.SetPen(wxPen(UBLCK, 2));
1687 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1694 if (td->blue_paddle && td->blue_paddle < 3) {
1695 wxPoint ais_flag_icon[4];
1698 if (g_bInlandEcdis) {
1700 ais_flag_icon[0] = wxPoint(-4, 4);
1701 ais_flag_icon[1] = wxPoint(-4, 11);
1702 ais_flag_icon[2] = wxPoint(-11, 11);
1703 ais_flag_icon[3] = wxPoint(-11, 4);
1704 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1706 ais_flag_icon[0] = wxPoint(TargetPoint.x - 4, TargetPoint.y + 4);
1707 ais_flag_icon[1] = wxPoint(TargetPoint.x - 4, TargetPoint.y - 3);
1708 ais_flag_icon[2] = wxPoint(TargetPoint.x + 3, TargetPoint.y - 3);
1709 ais_flag_icon[3] = wxPoint(TargetPoint.x + 3, TargetPoint.y + 4);
1712 dc.SetPen(wxPen(GetGlobalColor(
"CHWHT"), penWidth));
1716 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
1718 wxPoint((
int)-2 * targetscale / 100, (
int)18 * targetscale / 100);
1719 ais_flag_icon[2] = wxPoint((
int)-2 * targetscale / 100, 0);
1721 wxPoint((
int)-2 * targetscale / 100, (
int)-6 * targetscale / 100);
1722 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1724 if (targetscale < 100) penWidth = 1;
1725 dc.SetPen(wxPen(GetGlobalColor(
"CHWHT"), penWidth));
1727 if (td->blue_paddle == 1) {
1728 ais_flag_icon[1] = ais_flag_icon[0];
1729 ais_flag_icon[2] = ais_flag_icon[3];
1732 dc.SetBrush(wxBrush(GetGlobalColor(
"UINFB")));
1733 dc.StrokePolygon(4, ais_flag_icon);
1737 if ((g_bShowAISName) && (targetscale > 75)) {
1738 int true_scale_display = (int)(floor(vp.
chart_scale / 100.) * 100);
1739 if (true_scale_display <
1740 g_Show_Target_Name_Scale) {
1742 wxString tgt_name = td->GetFullName();
1743 tgt_name = tgt_name.substr(0, tgt_name.find(
"Unknown", 0));
1745 if (tgt_name != wxEmptyString) {
1746 dc.SetFont(*AIS_NameFont);
1747 dc.SetTextForeground(FontMgr::Get().GetFontColor(_(
"AIS Target Name")));
1750 dc.GetTextExtent(
"W", &w, &h);
1754 if ((td->COG > 90) && (td->COG < 180))
1755 dc.DrawText(tgt_name, TargetPoint.x + w, TargetPoint.y - h);
1757 dc.DrawText(tgt_name, TargetPoint.x + w,
1766 bool b_noshow =
false;
1767 bool b_forceshow =
false;
1769 if (td->MMSI == g_MMSI_Props_Array[i]->MMSI) {
1771 if (TRACKTYPE_NEVER == props->TrackType) {
1774 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
1782 int TrackLength = td->m_ptrack.size();
1783 if (((!b_noshow && td->b_show_track) || b_forceshow) && (TrackLength > 1)) {
1785 int TrackPointCount;
1786 wxPoint *TrackPoints = 0;
1787 TrackPoints =
new wxPoint[TrackLength];
1788 auto it = td->m_ptrack.begin();
1789 for (TrackPointCount = 0;
1790 it != td->m_ptrack.end() && (TrackPointCount < TrackLength);
1791 TrackPointCount++, ++it) {
1793 GetCanvasPointPix(vp, cp, ptrack_point.m_lat, ptrack_point.m_lon,
1794 &TrackPoints[TrackPointCount]);
1797 wxColour c = GetGlobalColor(
"CHMGD");
1798 dc.SetPen(wxPen(c, 1.5 * AIS_nominal_line_width_pix));
1802 std::map<int, Track *>::iterator itt;
1803 itt =
g_pAIS->m_persistent_tracks.find(td->MMSI);
1804 if (itt !=
g_pAIS->m_persistent_tracks.end()) {
1805 auto *ptrack = itt->second;
1806 if (ptrack->m_Colour == wxEmptyString) {
1807 c = GetGlobalColor(
"TEAL1");
1808 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1810 for (
unsigned int i = 0;
1811 i <
sizeof(::GpxxColorNames) /
sizeof(wxString); i++) {
1812 if (ptrack->m_Colour == ::GpxxColorNames[i]) {
1813 c = ::GpxxColors[i];
1814 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1822#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1826 glColor3ub(c.Red(), c.Green(), c.Blue());
1827 glBegin(GL_LINE_STRIP);
1829 for (TrackPointCount = 0; TrackPointCount < TrackLength;
1831 glVertex2i(TrackPoints[TrackPointCount].x,
1832 TrackPoints[TrackPointCount].y);
1836 dc.DrawLines(TrackPointCount, TrackPoints);
1839 dc.DrawLines(TrackPointCount, TrackPoints);
1843 if (dc.GetDC()) dc.StrokeLines(TrackPointCount, TrackPoints);
1847 delete[] TrackPoints;
1857 if (!cp->GetShowAIS())
return;
1862 const auto ¤t_targets =
g_pAIS->GetTargetList();
1870 }
else if (cp->m_canvasIndex == 0) {
1875 for (
const auto &it : current_targets) {
1877 auto td = it.second;
1878 double So, Cpa, Rang, Siz = 0.0;
1879 So = g_ScaledNumWeightSOG / 12 *
1881 if (So > g_ScaledNumWeightSOG) So = g_ScaledNumWeightSOG;
1883 if (td->bCPA_Valid) {
1884 Cpa = g_ScaledNumWeightCPA - g_ScaledNumWeightCPA / 4 * td->CPA;
1887 if (td->TCPA > .0) Cpa = Cpa + Cpa * g_ScaledNumWeightTCPA / 100;
1888 if (Cpa < .0) Cpa = .0;
1892 Rang = g_ScaledNumWeightRange / 10 * td->Range_NM;
1893 if (Rang > g_ScaledNumWeightRange) Rang = g_ScaledNumWeightRange;
1894 Rang = g_ScaledNumWeightRange - Rang;
1896 Siz = g_ScaledNumWeightSizeOfT / 30 * (td->DimA + td->DimB);
1897 if (Siz > g_ScaledNumWeightSizeOfT) Siz = g_ScaledNumWeightSizeOfT;
1898 td->importance = (float)So + Cpa + Rang + Siz;
1904 AISImportanceSwitchPoint = 0.0;
1906 float *Array =
new float[g_ShowScaled_Num];
1907 for (
int i = 0; i < g_ShowScaled_Num; i++) Array[i] = 0.0;
1911 if (cp->GetAttenAIS()) {
1912 for (
const auto &it : current_targets) {
1913 auto td = it.second;
1914 if (vp.GetBBox().Contains(td->Lat, td->Lon)) {
1915 if (td->importance > AISImportanceSwitchPoint) {
1916 Array[LowestInd] = td->importance;
1918 AISImportanceSwitchPoint = Array[0];
1920 for (
int i = 1; i < g_ShowScaled_Num; i++) {
1921 if (Array[i] < AISImportanceSwitchPoint) {
1922 AISImportanceSwitchPoint = Array[i];
1936 for (
const auto &it : current_targets) {
1937 auto td = it.second;
1938 if ((td->SOG < g_SOGminCOG_kts) &&
1939 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1940 AISDrawTarget(td.get(), dc, vp, cp);
1944 for (
const auto &it : current_targets) {
1945 auto td = it.second;
1946 if ((td->SOG >= g_SOGminCOG_kts) &&
1947 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1948 AISDrawTarget(td.get(), dc, vp,
1950 if (td->importance > 0) AISDrawTarget(td.get(), dc, vp, cp);
1954 for (
const auto &it : current_targets) {
1955 auto td = it.second;
1956 if ((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))
1957 AISDrawTarget(td.get(), dc, vp, cp);
1963 if (!
g_pAIS)
return false;
1965 if (!cc->GetShowAIS())
return false;
1968 for (
const auto &it :
g_pAIS->GetTargetList()) {
1969 auto td = it.second;
1970 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.