37#include <wx/tokenzr.h>
38#include <wx/datetime.h>
39#include <wx/wfstream.h>
40#include <wx/imaglist.h>
42#include "model/ais_decoder.h"
44#include "model/ais_target_data.h"
45#include "model/cutil.h"
46#include "model/georef.h"
47#include "model/own_ship.h"
48#include "model/select.h"
49#include "model/wx28compat.h"
52#include "AISTargetAlertDialog.h"
53#include "AISTargetQueryDialog.h"
58#include "ocpn_frame.h"
59#include "OCPNPlatform.h"
66int g_ais_cog_predictor_width;
69int ImportanceSwitchPoint = 100;
71extern ArrayOfMmsiProperties g_MMSI_Props_Array;
74extern float g_ShipScaleFactorExp;
76float AISImportanceSwitchPoint = 0.0;
79static const long long lNaN = 0xfff8000000000000;
80#define NAN (*(double *)&lNaN)
83wxString ais8_001_22_notice_names[] = {
85 _(
"Caution Area: Marine mammals habitat (implies whales NOT "
87 _(
"Caution Area: Marine mammals in area - reduce speed"),
88 _(
"Caution Area: Marine mammals in area - stay clear"),
89 _(
"Caution Area: Marine mammals in area - report sightings"),
90 _(
"Caution Area: Protected habitat - reduce speed"),
91 _(
"Caution Area: Protected habitat - stay clear"),
92 _(
"Caution Area: Protected habitat - no fishing or anchoring"),
93 _(
"Caution Area: Derelicts (drifting objects)"),
94 _(
"Caution Area: Traffic congestion"),
95 _(
"Caution Area: Marine event"),
96 _(
"Caution Area: Divers down"),
97 _(
"Caution Area: Swim area"),
98 _(
"Caution Area: Dredge operations"),
99 _(
"Caution Area: Survey operations"),
100 _(
"Caution Area: Underwater operation"),
101 _(
"Caution Area: Seaplane operations"),
102 _(
"Caution Area: Fishery - nets in water"),
103 _(
"Caution Area: Cluster of fishing vessels"),
104 _(
"Caution Area: Fairway closed"),
105 _(
"Caution Area: Harbour closed"),
106 _(
"Caution Area: Risk (define in Associated text field)"),
107 _(
"Caution Area: Underwater vehicle operation"),
108 _(
"(reserved for future use)"),
109 _(
"Environmental Caution Area: Storm front (line squall)"),
110 _(
"Environmental Caution Area: Hazardous sea ice"),
111 _(
"Environmental Caution Area: Storm warning (storm cell or line "
113 _(
"Environmental Caution Area: High wind"),
114 _(
"Environmental Caution Area: High waves"),
115 _(
"Environmental Caution Area: Restricted visibility (fog, rain, "
117 _(
"Environmental Caution Area: Strong currents"),
118 _(
"Environmental Caution Area: Heavy icing"),
119 _(
"(reserved for future use)"),
120 _(
"Restricted Area: Fishing prohibited"),
121 _(
"Restricted Area: No anchoring."),
122 _(
"Restricted Area: Entry approval required prior to transit"),
123 _(
"Restricted Area: Entry prohibited"),
124 _(
"Restricted Area: Active military OPAREA"),
125 _(
"Restricted Area: Firing - danger area."),
126 _(
"Restricted Area: Drifting Mines"),
127 _(
"(reserved for future use)"),
128 _(
"Anchorage Area: Anchorage open"),
129 _(
"Anchorage Area: Anchorage closed"),
130 _(
"Anchorage Area: Anchoring prohibited"),
131 _(
"Anchorage Area: Deep draft anchorage"),
132 _(
"Anchorage Area: Shallow draft anchorage"),
133 _(
"Anchorage Area: Vessel transfer operations"),
134 _(
"(reserved for future use)"),
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 _(
"Security Alert - Level 1"),
145 _(
"Security Alert - Level 2"),
146 _(
"Security Alert - Level 3"),
147 _(
"(reserved for future use)"),
148 _(
"(reserved for future use)"),
149 _(
"(reserved for future use)"),
150 _(
"(reserved for future use)"),
151 _(
"(reserved for future use)"),
152 _(
"Distress Area: Vessel disabled and adrift"),
153 _(
"Distress Area: Vessel sinking"),
154 _(
"Distress Area: Vessel abandoning ship"),
155 _(
"Distress Area: Vessel requests medical assistance"),
156 _(
"Distress Area: Vessel flooding"),
157 _(
"Distress Area: Vessel fire/explosion"),
158 _(
"Distress Area: Vessel grounding"),
159 _(
"Distress Area: Vessel collision"),
160 _(
"Distress Area: Vessel listing/capsizing"),
161 _(
"Distress Area: Vessel under assault"),
162 _(
"Distress Area: Person overboard"),
163 _(
"Distress Area: SAR area"),
164 _(
"Distress Area: Pollution response area"),
165 _(
"(reserved for future use)"),
166 _(
"(reserved for future use)"),
167 _(
"(reserved for future use)"),
168 _(
"Instruction: Contact VTS at this point/juncture"),
169 _(
"Instruction: Contact Port Administration at this "
171 _(
"Instruction: Do not proceed beyond this point/juncture"),
172 _(
"Instruction: Await instructions prior to proceeding beyond this "
174 _(
"Proceed to this location - await instructions"),
175 _(
"Clearance granted - proceed to berth"),
176 _(
"(reserved for future use)"),
177 _(
"(reserved for future use)"),
178 _(
"Information: Pilot boarding position"),
179 _(
"Information: Icebreaker waiting area"),
180 _(
"Information: Places of refuge"),
181 _(
"Information: Position of icebreakers"),
182 _(
"Information: Location of response units"),
183 _(
"VTS active target"),
184 _(
"Rogue or suspicious vessel"),
185 _(
"Vessel requesting non-distress assistance"),
186 _(
"Chart Feature: Sunken vessel"),
187 _(
"Chart Feature: Submerged object"),
188 _(
"Chart Feature: Semi-submerged object"),
189 _(
"Chart Feature: Shoal area"),
190 _(
"Chart Feature: Shoal area due north"),
191 _(
"Chart Feature: Shoal area due east"),
192 _(
"Chart Feature: Shoal area due south"),
193 _(
"Chart Feature: Shoal area due west"),
194 _(
"Chart Feature: Channel obstruction"),
195 _(
"Chart Feature: Reduced vertical clearance"),
196 _(
"Chart Feature: Bridge closed"),
197 _(
"Chart Feature: Bridge partially open"),
198 _(
"Chart Feature: Bridge fully open"),
199 _(
"(reserved for future use)"),
200 _(
"(reserved for future use)"),
201 _(
"(reserved for future use)"),
202 _(
"Report from ship: Icing info"),
203 _(
"(reserved for future use)"),
204 _(
"Report from ship: Miscellaneous information - define in "
205 "Associated text field"),
206 _(
"(reserved for future use)"),
207 _(
"(reserved for future use)"),
208 _(
"(reserved for future use)"),
209 _(
"(reserved for future use)"),
210 _(
"(reserved for future use)"),
211 _(
"Route: Recommended route"),
212 _(
"Route: Alternative route"),
213 _(
"Route: Recommended route through ice"),
214 _(
"(reserved for future use)"),
215 _(
"(reserved for future use)"),
216 _(
"Other - Define in associated text field"),
217 _(
"Cancellation - cancel area as identified by Message Linkage "
219 _(
"Undefined (default)")
223 double rlon, wxPoint *r) {
231static wxPoint transrot(wxPoint pt,
float sin_theta,
float cos_theta,
232 wxPoint offset = wxPoint(0, 0)) {
234 float px = (float)(pt.x * sin_theta) + (float)(pt.y * cos_theta);
235 float py = (float)(pt.y * sin_theta) - (float)(pt.x * cos_theta);
236 ret.x = (int)wxRound(px);
237 ret.y = (int)wxRound(py);
244static void transrot_pts(
int n, wxPoint *pt,
float sin_theta,
float cos_theta,
245 wxPoint offset = wxPoint(0, 0)) {
246 for (
int i = 0; i < n; i++)
247 pt[i] = transrot(pt[i], sin_theta, cos_theta, offset);
251 if (cp == NULL)
return;
252 if (!g_pAIS || !cp->GetShowAIS() || !g_bShowAreaNotices)
return;
254 wxDateTime now = wxDateTime::Now();
257 bool b_pens_set =
false;
263 wxBrush *yellow_brush = wxTheBrushList->FindOrCreateBrush(
264 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
265 wxBrush *green_brush = wxTheBrushList->FindOrCreateBrush(
266 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
272 for (
const auto &target : g_pAIS->GetAreaNoticeSourcesList()) {
273 auto target_data = target.second;
274 if (!target_data->area_notices.empty()) {
276 pen_save = dc.GetPen();
277 brush_save = dc.GetBrush();
279 yellow = GetGlobalColor(_T (
"YELO1" ));
280 yellow.Set(yellow.Red(), yellow.Green(), yellow.Blue(), 64);
282 green = GetGlobalColor(_T (
"GREEN4" ));
283 green.Set(green.Red(), green.Green(), green.Blue(), 64);
285 pen.SetColour(yellow);
288 yellow_brush = wxTheBrushList->FindOrCreateBrush(
289 yellow, wxBRUSHSTYLE_CROSSDIAG_HATCH);
291 wxTheBrushList->FindOrCreateBrush(green, wxBRUSHSTYLE_TRANSPARENT);
292 brush = yellow_brush;
297 for (
auto &ani : target_data->area_notices) {
300 if (area_notice.expiry_time > now) {
301 std::vector<wxPoint> points;
302 bool draw_polygon =
false;
304 switch (area_notice.notice_type) {
306 pen.SetColour(green);
310 pen.SetColour(yellow);
311 brush = yellow_brush;
314 pen.SetColour(yellow);
315 brush = yellow_brush;
320 for (Ais8_001_22_SubAreaList::iterator sa =
321 area_notice.sub_areas.begin();
322 sa != area_notice.sub_areas.end(); ++sa) {
324 case AIS8_001_22_SHAPE_CIRCLE: {
325 wxPoint target_point;
326 GetCanvasPointPix(vp, cp, sa->latitude, sa->longitude,
328 points.push_back(target_point);
329 if (sa->radius_m > 0.0)
330 dc.DrawCircle(target_point, sa->radius_m * vp_scale);
333 case AIS8_001_22_SHAPE_RECT: {
334 wxPoint target_point;
335 double lat = sa->latitude;
336 double lon = sa->longitude;
337 int orient_east = 90 + sa->orient_deg;
338 if (orient_east > 360) orient_east -= 360;
339 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
340 points.push_back(target_point);
341 ll_gc_ll(lat, lon, orient_east, sa->e_dim_m / 1852.0, &lat,
343 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
344 points.push_back(target_point);
345 ll_gc_ll(lat, lon, sa->orient_deg, sa->n_dim_m / 1852.0, &lat,
347 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
348 points.push_back(target_point);
349 ll_gc_ll(sa->latitude, sa->longitude, sa->orient_deg,
350 sa->n_dim_m / 1852.0, &lat, &lon);
351 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
352 points.push_back(target_point);
356 case AIS8_001_22_SHAPE_SECTOR: {
357 wxPoint target_point;
359 double lat1 = sa->latitude;
360 double lon1 = sa->longitude;
361 GetCanvasPointPix(vp, cp, lat1, lon1, &target_point);
362 points.push_back(target_point);
364 for (
int i = 0; i < 18; ++i) {
368 i * (sa->right_bound_deg - sa->left_bound_deg) / 18,
369 sa->radius_m / 1852.0, &lat, &lon);
370 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
371 points.push_back(target_point);
374 ll_gc_ll(lat1, lon1, sa->right_bound_deg, sa->radius_m / 1852.0,
376 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
377 points.push_back(target_point);
382 case AIS8_001_22_SHAPE_POLYGON:
385 case AIS8_001_22_SHAPE_POLYLINE: {
386 double lat = sa->latitude;
387 double lon = sa->longitude;
388 for (
int i = 0; i < 4; ++i) {
389 ll_gc_ll(lat, lon, sa->angles[i], sa->dists_m[i] / 1852.0,
391 wxPoint target_point;
392 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
393 points.push_back(target_point);
395 dc.DrawLines(points.size(), &points.front());
399 if (draw_polygon) dc.DrawPolygon(points.size(), &points.front());
407 dc.SetBrush(brush_save);
411static void TargetFrame(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
413 int gap2 = 2 * radius / 6;
415 wxPen pen_save = dc.GetPen();
419 dc.
DrawLine(x - radius, y + gap2, x - radius, y + radius);
420 dc.
DrawLine(x - radius, y + radius, x - gap2, y + radius);
421 dc.
DrawLine(x + gap2, y + radius, x + radius, y + radius);
422 dc.
DrawLine(x + radius, y + radius, x + radius, y + gap2);
423 dc.
DrawLine(x + radius, y - gap2, x + radius, y - radius);
424 dc.
DrawLine(x + radius, y - radius, x + gap2, y - radius);
425 dc.
DrawLine(x - gap2, y - radius, x - radius, y - radius);
426 dc.
DrawLine(x - radius, y - radius, x - radius, y - gap2);
431static void AtoN_Diamond(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius,
434 wxPen pen_save = dc.GetPen();
437 wxPen aton_WhiteBorderPen;
440 int rad1a = radius / 2;
441 int rad2a = radius / 4;
447 if ((td->NavStatus == ATON_VIRTUAL_OFFPOSITION) ||
448 (td->NavStatus == ATON_REAL_OFFPOSITION))
449 aton_DrawPen = wxPen(GetGlobalColor(_T (
"URED" )), pen.GetWidth());
451 aton_DrawPen = wxPen(GetGlobalColor(_T (
"UBLCK" )), pen.GetWidth());
453 bool b_virt = (td->NavStatus == ATON_VIRTUAL) |
454 (td->NavStatus == ATON_VIRTUAL_ONPOSITION) |
455 (td->NavStatus == ATON_VIRTUAL_OFFPOSITION);
457 if (b_virt) aton_DrawPen.SetStyle(wxPENSTYLE_SHORT_DASH);
459 aton_WhiteBorderPen =
460 wxPen(GetGlobalColor(_T (
"CHWHT" )), aton_DrawPen.GetWidth() + 2);
465 diamond[0] = wxPoint(radius, 0);
466 diamond[1] = wxPoint(0, -radius);
467 diamond[2] = wxPoint(-radius, 0);
468 diamond[3] = wxPoint(0, radius);
469 diamond[4] = wxPoint(radius, 0);
470 dc.SetPen(aton_WhiteBorderPen);
471 dc.DrawLines(5, diamond, x, y);
472 dc.SetPen(aton_DrawPen);
473 dc.DrawLines(5, diamond, x, y);
476 wxPen(GetGlobalColor(_T (
"UBLCK" )),
478 aton_WhiteBorderPen =
479 wxPen(GetGlobalColor(_T (
"CHWHT" )), aton_DrawPen.GetWidth() + 2);
483 cross[0] = wxPoint(-rad2a, 0);
484 cross[1] = wxPoint(rad2a, 0);
485 cross[2] = wxPoint(0, 0);
486 cross[3] = wxPoint(0, rad2a);
487 cross[4] = wxPoint(0, -rad2a);
488 dc.SetPen(aton_WhiteBorderPen);
489 dc.DrawLines(5, cross, x, y);
490 dc.SetPen(aton_DrawPen);
491 dc.DrawLines(5, cross, x, y);
493 wxPoint TriPointUp[4];
494 TriPointUp[0] = wxPoint(-rad1a, 0);
495 TriPointUp[1] = wxPoint(rad1a, 0);
496 TriPointUp[2] = wxPoint(0, -rad1a);
497 TriPointUp[3] = wxPoint(-rad1a, 0);
499 wxPoint TriPointDown[4];
500 TriPointDown[0] = wxPoint(-rad1a, -rad1a);
501 TriPointDown[1] = wxPoint(rad1a, -rad1a);
502 TriPointDown[2] = wxPoint(0, 0);
503 TriPointDown[3] = wxPoint(-rad1a, -rad1a);
505 wxPoint CircleOpen[16];
506 CircleOpen[0] = wxPoint(-1, 5);
507 CircleOpen[1] = wxPoint(1, 5);
508 CircleOpen[2] = wxPoint(3, 4);
509 CircleOpen[3] = wxPoint(4, 3);
510 CircleOpen[4] = wxPoint(5, 1);
511 CircleOpen[5] = wxPoint(5, -1);
512 CircleOpen[6] = wxPoint(4, -3);
513 CircleOpen[7] = wxPoint(3, -4);
514 CircleOpen[8] = wxPoint(1, -5);
515 CircleOpen[9] = wxPoint(-1, -5);
516 CircleOpen[10] = wxPoint(-3, -4);
517 CircleOpen[11] = wxPoint(-4, -3);
518 CircleOpen[12] = wxPoint(-5, -1);
519 CircleOpen[13] = wxPoint(-4, 3);
520 CircleOpen[14] = wxPoint(-3, 4);
521 CircleOpen[15] = wxPoint(-1, 5);
523 switch (td->ShipType) {
526 dc.SetPen(aton_WhiteBorderPen);
527 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
528 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
529 dc.SetPen(aton_DrawPen);
530 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
531 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
535 dc.SetPen(aton_WhiteBorderPen);
536 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
537 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
538 dc.SetPen(aton_DrawPen);
539 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
540 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
544 dc.SetPen(aton_WhiteBorderPen);
545 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
546 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
547 dc.SetPen(aton_DrawPen);
548 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
549 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
553 dc.SetPen(aton_WhiteBorderPen);
554 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
555 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
556 dc.SetPen(aton_DrawPen);
557 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
558 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
563 aRect[0] = wxPoint(-rad3a, 0);
564 aRect[1] = wxPoint(-rad3a, -rad3a - rad3a);
565 aRect[2] = wxPoint(rad3a, -rad3a - rad3a);
566 aRect[3] = wxPoint(rad3a, 0);
567 aRect[4] = wxPoint(-rad3a, 0);
568 dc.SetPen(aton_WhiteBorderPen);
569 dc.DrawLines(5, aRect, x, y - radius - 1);
570 dc.SetPen(aton_DrawPen);
571 dc.DrawLines(5, aRect, x, y - radius - 1);
575 dc.SetPen(aton_WhiteBorderPen);
576 dc.DrawLines(4, TriPointUp, x, y - radius);
577 dc.SetPen(aton_DrawPen);
578 dc.DrawLines(4, TriPointUp, x, y - radius);
582 dc.SetPen(aton_WhiteBorderPen);
583 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
584 dc.SetPen(aton_DrawPen);
585 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
586 dc.SetPen(aton_WhiteBorderPen);
587 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
588 dc.SetPen(aton_DrawPen);
589 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
593 dc.SetPen(aton_WhiteBorderPen);
594 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
595 dc.SetPen(aton_DrawPen);
596 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
600 cross[0] = wxPoint(-rad2a, -rad2a);
601 cross[1] = wxPoint(rad2a, rad2a);
602 cross[2] = wxPoint(0, 0);
603 cross[3] = wxPoint(-rad2a, rad2a);
604 cross[4] = wxPoint(rad2a, -rad2a);
605 dc.SetPen(aton_WhiteBorderPen);
606 dc.DrawLines(5, cross, x, y - radius - rad3a);
607 dc.SetPen(aton_DrawPen);
608 dc.DrawLines(5, cross, x, y - radius - rad3a);
616static void Base_Square(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
618 int gap2 = 2 * radius / 6;
619 int pen_width = pen.GetWidth();
621 wxPen pen_save = dc.GetPen();
625 dc.
DrawLine(x - radius, y - radius, x - radius, y + radius);
626 dc.
DrawLine(x - radius, y + radius, x + radius, y + radius);
627 dc.
DrawLine(x + radius, y + radius, x + radius, y - radius);
628 dc.
DrawLine(x + radius, y - radius, x - radius, y - radius);
632 pen.SetWidth(pen_width);
635 dc.
DrawLine(x - gap2, y, x + gap2, y);
636 dc.
DrawLine(x, y - gap2, x, y + gap2);
641static void SART_Render(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
643 int gap = (radius * 12) / 10;
644 int pen_width = pen.GetWidth();
646 wxPen pen_save = dc.GetPen();
650 wxBrush brush_save = dc.GetBrush();
651 wxBrush *ppBrush = wxTheBrushList->FindOrCreateBrush(
652 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
653 dc.SetBrush(*ppBrush);
655 dc.DrawCircle(x, y, radius);
659 pen.SetWidth(pen_width);
665 dc.SetBrush(brush_save);
671static void spherical_ll_gc_ll(
float lat,
float lon,
float brg,
float dist,
672 float *dlat,
float *dlon) {
673 float angr = brg / 180 * M_PI;
674 float latr = lat * M_PI / 180;
675 float D = dist / 3443;
676 float sD = sinf(D), cD = cosf(D);
677 float sy = sinf(latr), cy = cosf(latr);
678 float sa = sinf(angr), ca = cosf(angr);
680 *dlon = lon + asinf(sa * sD / cy) * 180 / M_PI;
681 *dlat = asinf(sy * cD + cy * sD * ca) * 180 / M_PI;
685float AIS_scale_factor;
686float AIS_nominal_target_size_mm;
687float AIS_nominal_icon_size_pixels;
689float AIS_user_scale_factor;
690double AIS_nominal_line_width_pix;
692float AIS_width_interceptbar_base;
693float AIS_width_interceptbar_top;
694float AIS_intercept_bar_circle_diameter;
695float AIS_width_interceptline;
696float AIS_width_cogpredictor_base;
697float AIS_width_cogpredictor_line;
698float AIS_width_target_outline;
699float AIS_icon_diameter;
702static void AISSetMetrics() {
703 AIS_scale_factor = g_current_monitor_dip_px_ratio;
705 double DPIscale = 1.0;
716 AIS_nominal_target_size_mm = 30.0 / g_Platform->GetDisplayDPmm();
720 AIS_nominal_target_size_mm = wxMin(AIS_nominal_target_size_mm, 10.0);
721 AIS_nominal_target_size_mm = wxMax(AIS_nominal_target_size_mm, 5.0);
723 AIS_nominal_icon_size_pixels =
724 wxMax(4.0, g_Platform->GetDisplayDPmm() *
725 AIS_nominal_target_size_mm);
727 AIS_pix_factor = AIS_nominal_icon_size_pixels /
730 AIS_scale_factor *= AIS_pix_factor;
732 AIS_user_scale_factor = g_ShipScaleFactorExp;
733 if (g_ShipScaleFactorExp > 1.0)
734 AIS_user_scale_factor = (log(g_ShipScaleFactorExp) + 1.0) *
737 AIS_scale_factor *= AIS_user_scale_factor;
741 AIS_nominal_line_width_pix =
742 wxMax(2, g_Platform->GetDisplayDPmm() / (4.0 / DPIscale));
745 AIS_width_interceptbar_base = 3 * AIS_nominal_line_width_pix;
746 AIS_width_interceptbar_top = 1.5 * AIS_nominal_line_width_pix;
747 AIS_intercept_bar_circle_diameter = 3.5 * AIS_nominal_line_width_pix;
748 AIS_width_interceptline = 2 * AIS_nominal_line_width_pix;
749 AIS_width_cogpredictor_base = 3 * AIS_nominal_line_width_pix;
750 AIS_width_cogpredictor_line = 1.3 * AIS_nominal_line_width_pix;
751 AIS_width_target_outline = 1.4 * AIS_nominal_line_width_pix;
752 AIS_icon_diameter = AIS_intercept_bar_circle_diameter * AIS_user_scale_factor;
754 wxFont *font = FontMgr::Get().
GetFont(_(
"AIS Target Name"), 0);
755 double scaler = DPIscale;
758 font->GetPointSize() / scaler, font->GetFamily(), font->GetStyle(),
759 font->GetWeight(),
false, font->GetFaceName());
765 if (NULL == td)
return;
767 static bool firstTimeUse =
true;
770 g_AisFirstTimeUse =
true;
773 g_AisFirstTimeUse =
false;
774 firstTimeUse =
false;
778 if (td->b_lost)
return;
783 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts) &&
784 (td->NavStatus != NOT_UNDER_COMMAND) &&
785 ((td->Class == AIS_CLASS_A) || (td->Class == AIS_CLASS_B)))
789 if (!td->b_positionOnceValid)
return;
792 if (td->b_OwnShip)
return;
795 float target_sog = td->SOG;
796 if ((td->SOG > 102.2) && !td->b_SarAircraftPosnReport) target_sog = 0.;
799 wxPoint TargetPoint, PredPoint;
802 if (td->n_alert_state == AIS_ALERT_SET) {
806 if (vp.GetBBox().Contains(td->Lat, td->Lon))
810 if (1 && td->b_show_track) {
811 if (td->m_ptrack.size() > 0) {
813 if (vp.GetBBox().Contains(ptrack_point.m_lat, ptrack_point.m_lon))
823 float pred_lat, pred_lon;
824 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
825 target_sog * g_ShowCOG_Mins / 60., &pred_lat, &pred_lon);
828 if (vp.GetBBox().Contains(pred_lat, pred_lon))
832 box.SetFromSegment(td->Lat, td->Lon, pred_lat, pred_lon);
835 if (!vp.GetBBox().IntersectOut(box)) drawit++;
841 GetCanvasPointPix(vp, cp, td->Lat, td->Lon, &TargetPoint);
842 GetCanvasPointPix(vp, cp, pred_lat, pred_lon, &PredPoint);
844 bool b_hdgValid =
true;
846 float theta = (float)-PI / 2.;
848 if ((
int)(td->HDG) != 511) {
849 theta = ((td->HDG - 90) * PI / 180.) + vp.
rotation;
853 if (!g_bInlandEcdis) {
859 float angle_lat, angle_lon;
860 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG, angle_distance_nm,
861 &angle_lat, &angle_lon);
864 GetCanvasPointPix(vp, cp, angle_lat, angle_lon, &AnglePoint);
866 if (abs(AnglePoint.x - TargetPoint.x) > 0) {
867 if (target_sog > g_SOGminCOG_kts) {
868 theta = atan2f((
double)(AnglePoint.y - TargetPoint.y),
869 (
double)(AnglePoint.x - TargetPoint.x));
872 theta = (float)-PI / 2.;
874 if (AnglePoint.y > TargetPoint.y)
875 theta = (float)PI / 2.;
879 if (td->SOG >= g_SOGminCOG_kts)
888 float sin_theta = sinf(theta), cos_theta = cosf(theta);
891 dash_long[0] = (int)(1.0 * gFrame->GetPrimaryCanvas()
894 (int)(0.5 * gFrame->GetPrimaryCanvas()->GetPixPerMM());
896 int targetscale = 100;
899 idxCC = cp->m_canvasIndex;
901 if (idxCC > AIS_TARGETDATA_MAX_CANVAS - 1)
904 if (cp->GetAttenAIS()) {
905 if (td->NavStatus <= 15) {
909 if (td->importance < AISImportanceSwitchPoint)
910 targetscale = td->last_scale[idxCC] - 2;
912 if (td->importance > AISImportanceSwitchPoint)
913 targetscale = td->last_scale[idxCC] + 5;
914 if (targetscale > 100) targetscale = 100;
915 if (targetscale < 50) targetscale = 50;
916 td->last_scale[idxCC] = targetscale;
922 wxPoint ais_real_size[6];
923 bool bcan_draw_size =
true;
924 if (g_bDrawAISSize) {
925 if (td->DimA + td->DimB == 0 || td->DimC + td->DimD == 0) {
926 bcan_draw_size =
false;
928 double ref_lat, ref_lon;
929 ll_gc_ll(td->Lat, td->Lon, 0, 100. / 1852., &ref_lat, &ref_lon);
932 double ppm = r_point.GetDistance(b_point) / 100.;
933 double offwid = (td->DimC + td->DimD) * ppm * 0.25;
934 double offlen = (td->DimA + td->DimB) * ppm * 0.15;
935 ais_real_size[0].x = -td->DimD * ppm;
936 ais_real_size[0].y = -td->DimB * ppm;
937 ais_real_size[1].x = -td->DimD * ppm;
938 ais_real_size[1].y = td->DimA * ppm - offlen;
939 ais_real_size[2].x = -td->DimD * ppm + offwid;
940 ais_real_size[2].y = td->DimA * ppm;
941 ais_real_size[3].x = td->DimC * ppm - offwid;
942 ais_real_size[3].y = td->DimA * ppm;
943 ais_real_size[4].x = td->DimC * ppm;
944 ais_real_size[4].y = td->DimA * ppm - offlen;
945 ais_real_size[5].x = td->DimC * ppm;
946 ais_real_size[5].y = -td->DimB * ppm;
948 if (ais_real_size[4].x - ais_real_size[0].x < 16 ||
949 ais_real_size[2].y - ais_real_size[0].y < 30)
950 bcan_draw_size =
false;
952 bcan_draw_size =
true;
953 transrot_pts(6, ais_real_size, sin_theta, cos_theta);
960 wxPoint ais_quad_icon[4] = {wxPoint(-8, -6), wxPoint(0, 24), wxPoint(8, -6),
962 wxPoint ais_octo_icon[8] = {wxPoint(4, 8), wxPoint(8, 4), wxPoint(8, -4),
963 wxPoint(4, -8), wxPoint(-4, -8), wxPoint(-8, -4),
964 wxPoint(-8, 4), wxPoint(-4, 8)};
966 if (!g_bInlandEcdis) {
968 if (targetscale == 50) {
969 ais_quad_icon[0] = wxPoint(-4, -3);
970 ais_quad_icon[1] = wxPoint(0, 12);
971 ais_quad_icon[2] = wxPoint(4, -3);
972 ais_quad_icon[3] = wxPoint(0, -3);
973 }
else if (targetscale != 100) {
975 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
976 ais_quad_icon[1] = wxPoint(0, (
int)24 * targetscale / 100);
978 wxPoint((
int)8 * targetscale / 100, (
int)-6 * targetscale / 100);
979 ais_quad_icon[3] = wxPoint(0, (
int)-6 * targetscale / 100);
983 if (td->Class == AIS_CLASS_B) ais_quad_icon[3].y = 0;
985 if ((td->Class == AIS_GPSG_BUDDY) || (td->b_isFollower)) {
986 ais_quad_icon[0] = wxPoint(-5, -12);
987 ais_quad_icon[1] = wxPoint(-3, 12);
988 ais_quad_icon[2] = wxPoint(3, 12);
989 ais_quad_icon[3] = wxPoint(5, -12);
990 }
else if (td->Class == AIS_DSC) {
991 ais_quad_icon[0].y = 0;
992 ais_quad_icon[1].y = 8;
993 ais_quad_icon[2].y = 0;
994 ais_quad_icon[3].y = -8;
995 }
else if (td->Class == AIS_APRS) {
996 ais_quad_icon[0] = wxPoint(-8, -8);
997 ais_quad_icon[1] = wxPoint(-8, 8);
998 ais_quad_icon[2] = wxPoint(8, 8);
999 ais_quad_icon[3] = wxPoint(8, -8);
1002 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1005 iconPoints = ais_quad_icon;
1009 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1011 iconPoints = ais_quad_icon;
1014 iconPoints = ais_octo_icon;
1018 wxColour UBLCK = GetGlobalColor(_T (
"UBLCK" ));
1019 dc.SetPen(wxPen(UBLCK));
1022 wxColour UINFG = GetGlobalColor(_T (
"UINFG" ));
1023 wxBrush target_brush = wxBrush(UINFG);
1026 if (td->b_isEuroInland && !g_bInlandEcdis)
1027 target_brush = wxBrush(GetGlobalColor(_T (
"TEAL1" )));
1030 if (td->b_nameFromCache)
1031 target_brush = wxBrush(GetGlobalColor(_T (
"GREEN5" )));
1034 wxColour URED = GetGlobalColor(_T (
"URED" ));
1035 if (!td->b_nameValid) target_brush = wxBrush(GetGlobalColor(_T (
"CHYLW" )));
1037 if ((td->Class == AIS_DSC) &&
1038 ((td->ShipType == 12) || (td->ShipType == 16)))
1039 target_brush = wxBrush(URED);
1041 if (td->b_SarAircraftPosnReport) target_brush = wxBrush(UINFG);
1043 if ((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid))
1044 target_brush = wxBrush(URED);
1046 if ((td->n_alert_state == AIS_ALERT_NO_DIALOG_SET) && (td->bCPA_Valid) &&
1047 (!td->b_isFollower))
1048 target_brush = wxBrush(URED);
1050 if (td->b_positionDoubtful)
1051 target_brush = wxBrush(GetGlobalColor(_T (
"UINFF" )));
1053 wxPen target_outline_pen(UBLCK, AIS_width_target_outline);
1056 if (((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid)) ||
1057 (td->b_show_AIS_CPA && (td->bCPA_Valid))) {
1059 double tcpa_lat, tcpa_lon;
1060 ll_gc_ll(td->Lat, td->Lon, td->COG, target_sog * td->TCPA / 60., &tcpa_lat,
1063 wxPoint TPoint = TargetPoint;
1064 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1067 ClipResult res = cohen_sutherland_line_clip_i(
1068 &TPoint.x, &TPoint.y, &tCPAPoint.x, &tCPAPoint.y, 0, vp.
pix_width, 0,
1071 if (res != Invisible) {
1072 wxPen ppPen2(URED, AIS_width_cogpredictor_line, wxPENSTYLE_USER_DASH);
1073 ppPen2.SetDashes(2, dash_long);
1076 dc.StrokeLine(TPoint.x, TPoint.y, tCPAPoint.x, tCPAPoint.y);
1080 double ocpa_lat, ocpa_lon;
1083 if (std::isnan(gCog) || std::isnan(gSog)) {
1087 ll_gc_ll(gLat, gLon, gCog, gSog * td->TCPA / 60., &ocpa_lat, &ocpa_lon);
1092 GetCanvasPointPix(vp, cp, ocpa_lat, ocpa_lon, &oCPAPoint);
1093 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1096 wxPoint oCPAPoint_unclipped = oCPAPoint;
1097 wxPoint tCPAPoint_unclipped = tCPAPoint;
1100 ClipResult ores = cohen_sutherland_line_clip_i(
1101 &tCPAPoint.x, &tCPAPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0, vp.
pix_width,
1104 if (ores != Invisible) {
1105 wxColour yellow = GetGlobalColor(_T (
"YELO1" ));
1106 dc.SetPen(wxPen(yellow, AIS_width_interceptbar_base));
1107 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1109 wxPen ppPen2(URED, AIS_width_interceptbar_top, wxPENSTYLE_USER_DASH);
1110 ppPen2.SetDashes(2, dash_long);
1112 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1115 wxBrush br(GetGlobalColor(_T (
"BLUE3" )));
1117 dc.SetPen(wxPen(UBLCK, AIS_width_target_outline));
1121 tCPAPoint_unclipped.x, tCPAPoint_unclipped.y,
1122 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1124 oCPAPoint_unclipped.x, oCPAPoint_unclipped.y,
1125 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1130 GetCanvasPointPix(vp, cp, gLat, gLon, &oShipPoint);
1131 oCPAPoint = oCPAPoint_unclipped;
1133 ClipResult ownres = cohen_sutherland_line_clip_i(
1134 &oShipPoint.x, &oShipPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0,
1137 if (ownres != Invisible) {
1138 wxPen ppPen2(URED, AIS_width_interceptline, wxPENSTYLE_USER_DASH);
1139 ppPen2.SetDashes(2, dash_long);
1142 dc.StrokeLine(oShipPoint.x, oShipPoint.y, oCPAPoint.x, oCPAPoint.y);
1145 dc.SetPen(wxPen(UBLCK));
1146 dc.SetBrush(wxBrush(URED));
1152 auto alert_dlg_active =
1154 if (alert_dlg_active && alert_dlg_active->IsShown() && cp) {
1155 if (alert_dlg_active->Get_Dialog_MMSI() == td->MMSI)
1156 cp->JaggyCircle(dc, wxPen(URED, 2), TargetPoint.x, TargetPoint.y, 100);
1161 if (g_pais_query_dialog_active && g_pais_query_dialog_active->IsShown()) {
1162 if (g_pais_query_dialog_active->GetMMSI() == td->MMSI)
1163 TargetFrame(dc, wxPen(UBLCK, 2), TargetPoint.x, TargetPoint.y, 25);
1168 if ((g_bShowCOG) && (target_sog > g_SOGminCOG_kts) && td->b_active) {
1169 int pixx = TargetPoint.x;
1170 int pixy = TargetPoint.y;
1171 int pixx1 = PredPoint.x;
1172 int pixy1 = PredPoint.y;
1176 float l = sqrtf(powf((
float)(PredPoint.x - TargetPoint.x), 2) +
1177 powf((
float)(PredPoint.y - TargetPoint.y), 2));
1180 ClipResult res = cohen_sutherland_line_clip_i(
1183 if (res != Invisible) {
1185 if (targetscale >= 75) {
1186 wxPen wide_pen(target_brush.GetColour(), AIS_width_cogpredictor_base);
1187 dc.SetPen(wide_pen);
1188 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1191 if (AIS_width_cogpredictor_base > 1) {
1193 wxPen narrow_pen(UBLCK, AIS_width_cogpredictor_line);
1194 if (targetscale < 75) {
1195 narrow_pen.SetWidth(1);
1196 narrow_pen.SetStyle(wxPENSTYLE_USER_DASH);
1200 narrow_pen.SetDashes(2, dash_dot);
1202 dc.SetPen(narrow_pen);
1203 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1207 dc.SetBrush(target_brush);
1208 dc.StrokeCircle(PredPoint.x, PredPoint.y, 5 * targetscale / 100);
1213#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1216 glTranslated(PredPoint.x, PredPoint.y, 0);
1217 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1219 float points[] = {0.0f, 5.0f, 2.5f, 4.330127f, 4.330127f,
1220 2.5f, 5.0f, 0, 4.330127f, -2.5f,
1221 2.5f, -4.330127f, 0, -5.1f, -2.5f,
1222 -4.330127f, -4.330127f, -2.5f, -5.0f, 0,
1223 -4.330127f, 2.5f, -2.5f, 4.330127f, 0,
1225 if (targetscale <= 75) {
1226 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1228 points[i] = points[i] / 2;
1231 wxColour c = target_brush.GetColour();
1232 glColor3ub(c.Red(), c.Green(), c.Blue());
1234 glBegin(GL_TRIANGLE_FAN);
1235 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1237 glVertex2i(points[i], points[i + 1]);
1240 glColor3ub(0, 0, 0);
1241 glLineWidth(AIS_width_target_outline);
1242 glBegin(GL_LINE_LOOP);
1243 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1245 glVertex2i(points[i], points[i + 1]);
1250 dc.SetBrush(target_brush);
1251 dc.StrokeCircle(PredPoint.x, PredPoint.y,
1252 AIS_intercept_bar_circle_diameter *
1253 AIS_user_scale_factor * targetscale / 100);
1260 if ((td->ROTAIS != 0) && (td->ROTAIS != -128) && (!g_bShowScaled)) {
1261 float cog_angle = td->COG * PI / 180.;
1263 float theta2 = theta;
1264 if (td->SOG >= g_SOGminCOG_kts)
1265 theta2 = cog_angle - (PI / 2);
1269 theta2 += (float)PI / 2;
1271 theta2 -= (float)PI / 2;
1273 int xrot = (int)round(pixx1 + (nv * cosf(theta2)));
1274 int yrot = (int)round(pixy1 + (nv * sinf(theta2)));
1275 dc.StrokeLine(pixx1, pixy1, xrot, yrot);
1281 if (td->Class == AIS_ARPA || td->Class == AIS_BUOY) {
1282 wxPen target_pen(UBLCK, 2);
1284 dc.SetPen(target_pen);
1285 dc.SetBrush(target_brush);
1286 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1.8 * AIS_icon_diameter);
1288 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1);
1290 if (!td->b_active) {
1291 dc.SetPen(wxPen(UBLCK, 2));
1292 dc.StrokeLine(TargetPoint.x - 14, TargetPoint.y, TargetPoint.x + 14,
1294 dc.SetPen(wxPen(UBLCK, 1));
1297 }
else if (td->Class == AIS_METEO) {
1298 wxPen met(UBLCK, (wxMax(target_outline_pen.GetWidth(), 2.5)));
1300 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1301 double met_radius = 1.8 * AIS_icon_diameter;
1302 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, met_radius);
1305 dc.SetPen(wxPen(wxMax(target_outline_pen.GetWidth(), 1)));
1307 dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4,
1308 TargetPoint.x - met_radius / 3,
1309 TargetPoint.y + met_radius / 2);
1311 TargetPoint.x - met_radius / 3, TargetPoint.y + met_radius / 2,
1312 TargetPoint.x - met_radius / 2, TargetPoint.y - met_radius / 2);
1314 dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4,
1315 TargetPoint.x + met_radius / 3,
1316 TargetPoint.y + met_radius / 2);
1318 TargetPoint.x + met_radius / 3, TargetPoint.y + met_radius / 2,
1319 TargetPoint.x + met_radius / 2, TargetPoint.y - met_radius / 2);
1321 }
else if (td->Class == AIS_ATON) {
1322 AtoN_Diamond(dc, wxPen(UBLCK, AIS_width_target_outline), TargetPoint.x,
1323 TargetPoint.y, AIS_icon_diameter * 1.5, td);
1324 }
else if (td->Class == AIS_BASE) {
1325 Base_Square(dc, wxPen(UBLCK, AIS_width_target_outline), TargetPoint.x,
1326 TargetPoint.y, AIS_icon_diameter);
1327 }
else if (td->Class == AIS_SART) {
1328 if (td->NavStatus == 14)
1329 SART_Render(dc, wxPen(URED, AIS_width_target_outline), TargetPoint.x,
1330 TargetPoint.y, AIS_icon_diameter);
1333 dc, wxPen(GetGlobalColor(_T (
"UGREN" )), AIS_width_target_outline),
1334 TargetPoint.x, TargetPoint.y, AIS_icon_diameter);
1336 }
else if (td->b_SarAircraftPosnReport) {
1337 int airtype = (td->MMSI % 1000) / 100;
1338 int ar = airtype == 5 ? 15 : 9;
1339 wxPoint SarIcon[15];
1341 double scaleplus = 1.4;
1343 SarIcon[0] = wxPoint(0, 9) * AIS_scale_factor * scaleplus;
1344 SarIcon[1] = wxPoint(1, 1) * AIS_scale_factor * scaleplus;
1345 SarIcon[2] = wxPoint(2, 1) * AIS_scale_factor * scaleplus;
1346 SarIcon[3] = wxPoint(9, 8) * AIS_scale_factor * scaleplus;
1347 SarIcon[4] = wxPoint(9, 7) * AIS_scale_factor * scaleplus;
1348 SarIcon[5] = wxPoint(3, 0) * AIS_scale_factor * scaleplus;
1349 SarIcon[6] = wxPoint(3, -5) * AIS_scale_factor * scaleplus;
1350 SarIcon[7] = wxPoint(9, -12) * AIS_scale_factor * scaleplus;
1351 SarIcon[8] = wxPoint(9, -13) * AIS_scale_factor * scaleplus;
1352 SarIcon[9] = wxPoint(2, -5) * AIS_scale_factor * scaleplus;
1353 SarIcon[10] = wxPoint(1, -15) * AIS_scale_factor * scaleplus;
1354 SarIcon[11] = wxPoint(3, -16) * AIS_scale_factor * scaleplus;
1355 SarIcon[12] = wxPoint(4, -18) * AIS_scale_factor * scaleplus;
1356 SarIcon[13] = wxPoint(1, -18) * AIS_scale_factor * scaleplus;
1357 SarIcon[14] = wxPoint(0, -19) * AIS_scale_factor * scaleplus;
1359 SarIcon[0] = wxPoint(0, 12) * AIS_scale_factor;
1360 SarIcon[1] = wxPoint(4, 2) * AIS_scale_factor;
1361 SarIcon[2] = wxPoint(16, -2) * AIS_scale_factor;
1362 SarIcon[3] = wxPoint(16, -8) * AIS_scale_factor;
1363 SarIcon[4] = wxPoint(4, -8) * AIS_scale_factor;
1364 SarIcon[5] = wxPoint(3, -16) * AIS_scale_factor;
1365 SarIcon[6] = wxPoint(10, -18) * AIS_scale_factor;
1366 SarIcon[7] = wxPoint(10, -22) * AIS_scale_factor;
1367 SarIcon[8] = wxPoint(0, -22) * AIS_scale_factor;
1374 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1375 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1377 wxPen tri_pen(target_brush.GetColour(), 1);
1379 dc.SetBrush(target_brush);
1382 int mappings[14][3] = {
1383 {0, 1, 10}, {0, 10, 14}, {1, 2, 9}, {1, 9, 10}, {10, 13, 14},
1384 {10, 11, 13}, {11, 12, 13}, {1, 14, 10}, {2, 5, 9}, {5, 6, 9},
1385 {2, 3, 5}, {3, 4, 5}, {6, 7, 8}, {6, 9, 8}};
1388 for (
int i = 0; i < nmap; i++) {
1389 wxPoint ais_tri_icon[3];
1390 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1391 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1394 dc.SetPen(target_outline_pen);
1395 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1396 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1400 for (
int i = 0; i < ar; i++)
1402 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1404 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1407 dc.SetBrush(target_brush);
1409 for (
int i = 0; i < nmap; i++) {
1410 wxPoint ais_tri_icon[3];
1411 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1412 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1415 dc.SetPen(target_outline_pen);
1416 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1417 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1422 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1423 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1425 wxPen tri_pen(target_brush.GetColour(), 1);
1427 dc.SetBrush(target_brush);
1430 int mappings[7][3] = {{0, 1, 4}, {1, 2, 3}, {1, 3, 4}, {0, 4, 5},
1431 {0, 5, 8}, {5, 6, 7}, {5, 7, 8}};
1432 for (
int i = 0; i < 7; i++) {
1433 wxPoint ais_tri_icon[3];
1434 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1435 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1438 dc.SetPen(target_outline_pen);
1439 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1440 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1444 for (
int i = 0; i < ar; i++)
1446 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1448 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1451 dc.SetBrush(target_brush);
1453 for (
int i = 0; i < 7; i++) {
1454 wxPoint ais_tri_icon[3];
1455 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1456 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1459 dc.SetPen(target_outline_pen);
1460 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1461 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1465 if (!td->b_active) {
1466 dc.SetPen(wxPen(UBLCK, 3));
1467 dc.StrokeLine(TargetPoint.x - 16, TargetPoint.y, TargetPoint.x + 16,
1472 wxPen target_pen(UBLCK, 1);
1473 dc.SetPen(target_pen);
1475 wxPoint Point = TargetPoint;
1476 if (g_bDrawAISRealtime &&
1477 (td->Class == AIS_CLASS_A || td->Class == AIS_CLASS_B) &&
1478 td->SOG > g_AIS_RealtPred_Kts && td->SOG < 102.2) {
1479 wxDateTime now = wxDateTime::Now();
1481 int target_age = now.GetTicks() - td->PositionReportTicks;
1484 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
1485 td->SOG * target_age / 3600.0, &lat, &lon);
1487 GetCanvasPointPix(vp, cp, lat, lon, &Point);
1489 wxBrush realtime_brush = wxBrush(GetGlobalColor(
"GREY1"));
1490 dc.SetBrush(realtime_brush);
1491 dc.StrokePolygon(nPoints, iconPoints, Point.x, Point.y, AIS_scale_factor);
1493 dc.SetBrush(target_brush);
1496 dc.StrokePolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1501#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1503 wxColour c = target_brush.GetColour();
1504 glColor3ub(c.Red(), c.Green(), c.Blue());
1507 glTranslated(TargetPoint.x, TargetPoint.y, 0);
1508 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1510 glBegin(GL_TRIANGLE_FAN);
1513 glVertex2i(ais_quad_icon[3].x, ais_quad_icon[3].y);
1514 glVertex2i(ais_quad_icon[0].x, ais_quad_icon[0].y);
1515 glVertex2i(ais_quad_icon[1].x, ais_quad_icon[1].y);
1516 glVertex2i(ais_quad_icon[2].x, ais_quad_icon[2].y);
1518 for (
int i = 0; i < 8; i++) {
1519 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1524 glLineWidth(AIS_width_target_outline);
1526 glColor3ub(UBLCK.Red(), UBLCK.Green(), UBLCK.Blue());
1528 glBegin(GL_LINE_LOOP);
1529 for (
int i = 0; i < nPoints; i++)
1530 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1535 dc.SetPen(target_outline_pen);
1536 dc.DrawPolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1542 if (td->b_isFollower) {
1543 wxPoint ais_follow_stroke[3];
1544 ais_follow_stroke[0] = wxPoint(-3, -20) * AIS_scale_factor;
1545 ais_follow_stroke[1] = wxPoint(0, 0) * AIS_scale_factor;
1546 ais_follow_stroke[2] = wxPoint(3, -20) * AIS_scale_factor;
1548 transrot_pts(3, ais_follow_stroke, sin_theta, cos_theta);
1550 int penWidth = wxMax(target_outline_pen.GetWidth(), 2);
1551 dc.SetPen(wxPen(UBLCK, penWidth));
1552 dc.StrokeLine(ais_follow_stroke[0].x + TargetPoint.x,
1553 ais_follow_stroke[0].y + TargetPoint.y,
1554 ais_follow_stroke[1].x + TargetPoint.x,
1555 ais_follow_stroke[1].y + TargetPoint.y);
1556 dc.StrokeLine(ais_follow_stroke[1].x + TargetPoint.x,
1557 ais_follow_stroke[1].y + TargetPoint.y,
1558 ais_follow_stroke[2].x + TargetPoint.x,
1559 ais_follow_stroke[2].y + TargetPoint.y);
1562 if (g_bDrawAISSize && bcan_draw_size) {
1563 dc.SetPen(target_outline_pen);
1564 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1565 if (!g_bInlandEcdis) {
1566 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1569 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1574 dc.SetBrush(wxBrush(GetGlobalColor(_T (
"SHIPS" ))));
1575 int navstatus = td->NavStatus;
1579 if (((td->ShipType >= 40) && (td->ShipType < 50)) &&
1580 (navstatus == UNDERWAY_USING_ENGINE || td->Class == AIS_CLASS_B))
1583 if (targetscale > 90) {
1584 switch (navstatus) {
1587 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1590 case RESTRICTED_MANOEUVRABILITY: {
1592 diamond[0] = wxPoint(4, 0) * AIS_scale_factor;
1593 diamond[1] = wxPoint(0, -6) * AIS_scale_factor;
1594 diamond[2] = wxPoint(-4, 0) * AIS_scale_factor;
1595 diamond[3] = wxPoint(0, 6) * AIS_scale_factor;
1596 dc.StrokePolygon(4, diamond, TargetPoint.x,
1597 TargetPoint.y - (11 * AIS_scale_factor));
1598 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1599 dc.StrokeCircle(TargetPoint.x,
1600 TargetPoint.y - (22 * AIS_scale_factor),
1601 4 * AIS_scale_factor);
1605 case CONSTRAINED_BY_DRAFT: {
1606 wxPoint can[4] = {wxPoint(-3, 0) * AIS_scale_factor,
1607 wxPoint(3, 0) * AIS_scale_factor,
1608 wxPoint(3, -16) * AIS_scale_factor,
1609 wxPoint(-3, -16) * AIS_scale_factor};
1610 dc.StrokePolygon(4, can, TargetPoint.x, TargetPoint.y);
1613 case NOT_UNDER_COMMAND: {
1614 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1615 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1616 4 * AIS_scale_factor);
1621 tri[0] = wxPoint(-4, 0) * AIS_scale_factor;
1622 tri[1] = wxPoint(4, 0) * AIS_scale_factor;
1623 tri[2] = wxPoint(0, -9) * AIS_scale_factor;
1624 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1625 tri[0] = wxPoint(0, -9) * AIS_scale_factor;
1626 tri[1] = wxPoint(4, -18) * AIS_scale_factor;
1627 tri[2] = wxPoint(-4, -18) * AIS_scale_factor;
1628 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1632 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1633 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1634 4 * AIS_scale_factor);
1635 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 18,
1636 4 * AIS_scale_factor);
1641 dc.SetBrush(target_brush);
1643 wxPoint arrow1[3] = {wxPoint(-4, 20) * AIS_scale_factor,
1644 wxPoint(0, 27) * AIS_scale_factor,
1645 wxPoint(4, 20) * AIS_scale_factor};
1646 transrot_pts(3, arrow1, sin_theta, cos_theta, TargetPoint);
1647 dc.StrokePolygon(3, arrow1);
1649 wxPoint arrow2[3] = {wxPoint(-4, 27) * AIS_scale_factor,
1650 wxPoint(0, 34) * AIS_scale_factor,
1651 wxPoint(4, 27) * AIS_scale_factor};
1652 transrot_pts(3, arrow2, sin_theta, cos_theta, TargetPoint);
1653 dc.StrokePolygon(3, arrow2);
1660 if (!td->b_active) {
1661 wxPoint p1 = transrot(wxPoint((
int)-14 * targetscale / 100, 0), sin_theta,
1662 cos_theta, TargetPoint);
1663 wxPoint p2 = transrot(wxPoint((
int)14 * targetscale / 100, 0), sin_theta,
1664 cos_theta, TargetPoint);
1666 dc.SetPen(wxPen(UBLCK, 2));
1667 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1674 if (td->blue_paddle && td->blue_paddle < 3) {
1675 wxPoint ais_flag_icon[4];
1678 if (g_bInlandEcdis) {
1680 ais_flag_icon[0] = wxPoint(-4, 4);
1681 ais_flag_icon[1] = wxPoint(-4, 11);
1682 ais_flag_icon[2] = wxPoint(-11, 11);
1683 ais_flag_icon[3] = wxPoint(-11, 4);
1684 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1686 ais_flag_icon[0] = wxPoint(TargetPoint.x - 4, TargetPoint.y + 4);
1687 ais_flag_icon[1] = wxPoint(TargetPoint.x - 4, TargetPoint.y - 3);
1688 ais_flag_icon[2] = wxPoint(TargetPoint.x + 3, TargetPoint.y - 3);
1689 ais_flag_icon[3] = wxPoint(TargetPoint.x + 3, TargetPoint.y + 4);
1692 dc.SetPen(wxPen(GetGlobalColor(_T (
"CHWHT" )), penWidth));
1696 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
1698 wxPoint((
int)-2 * targetscale / 100, (
int)18 * targetscale / 100);
1699 ais_flag_icon[2] = wxPoint((
int)-2 * targetscale / 100, 0);
1701 wxPoint((
int)-2 * targetscale / 100, (
int)-6 * targetscale / 100);
1702 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1704 if (targetscale < 100) penWidth = 1;
1705 dc.SetPen(wxPen(GetGlobalColor(_T (
"CHWHT" )), penWidth));
1707 if (td->blue_paddle == 1) {
1708 ais_flag_icon[1] = ais_flag_icon[0];
1709 ais_flag_icon[2] = ais_flag_icon[3];
1712 dc.SetBrush(wxBrush(GetGlobalColor(_T (
"UINFB" ))));
1713 dc.StrokePolygon(4, ais_flag_icon);
1717 if ((g_bShowAISName) && (targetscale > 75)) {
1718 int true_scale_display = (int)(floor(vp.
chart_scale / 100.) * 100);
1719 if (true_scale_display <
1720 g_Show_Target_Name_Scale) {
1722 wxString tgt_name = td->GetFullName();
1723 tgt_name = tgt_name.substr(0, tgt_name.find(_T (
"Unknown" ), 0));
1725 if (tgt_name != wxEmptyString) {
1726 dc.SetFont(*AIS_NameFont);
1727 dc.SetTextForeground(FontMgr::Get().GetFontColor(_(
"AIS Target Name")));
1730 dc.GetTextExtent(_T(
"W"), &w, &h);
1734 if ((td->COG > 90) && (td->COG < 180))
1735 dc.DrawText(tgt_name, TargetPoint.x + w, TargetPoint.y - h);
1737 dc.DrawText(tgt_name, TargetPoint.x + w,
1746 bool b_noshow =
false;
1747 bool b_forceshow =
false;
1748 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
1749 if (td->MMSI == g_MMSI_Props_Array[i]->MMSI) {
1751 if (TRACKTYPE_NEVER == props->TrackType) {
1754 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
1762 int TrackLength = td->m_ptrack.size();
1763 if (((!b_noshow && td->b_show_track) || b_forceshow) && (TrackLength > 1)) {
1765 int TrackPointCount;
1766 wxPoint *TrackPoints = 0;
1767 TrackPoints =
new wxPoint[TrackLength];
1768 auto it = td->m_ptrack.begin();
1769 for (TrackPointCount = 0;
1770 it != td->m_ptrack.end() && (TrackPointCount < TrackLength);
1771 TrackPointCount++, ++it) {
1773 GetCanvasPointPix(vp, cp, ptrack_point.m_lat, ptrack_point.m_lon,
1774 &TrackPoints[TrackPointCount]);
1777 wxColour c = GetGlobalColor(_T (
"CHMGD" ));
1778 dc.SetPen(wxPen(c, 1.5 * AIS_nominal_line_width_pix));
1782 std::map<int, Track *>::iterator itt;
1783 itt = g_pAIS->m_persistent_tracks.find(td->MMSI);
1784 if (itt != g_pAIS->m_persistent_tracks.end()) {
1785 auto *ptrack = itt->second;
1786 if (ptrack->m_Colour == wxEmptyString) {
1787 c = GetGlobalColor(_T (
"TEAL1" ));
1788 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1790 for (
unsigned int i = 0;
1791 i <
sizeof(::GpxxColorNames) /
sizeof(wxString); i++) {
1792 if (ptrack->m_Colour == ::GpxxColorNames[i]) {
1793 c = ::GpxxColors[i];
1794 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1802#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1806 glColor3ub(c.Red(), c.Green(), c.Blue());
1807 glBegin(GL_LINE_STRIP);
1809 for (TrackPointCount = 0; TrackPointCount < TrackLength;
1811 glVertex2i(TrackPoints[TrackPointCount].x,
1812 TrackPoints[TrackPointCount].y);
1816 dc.DrawLines(TrackPointCount, TrackPoints);
1819 dc.DrawLines(TrackPointCount, TrackPoints);
1823 if (dc.GetDC()) dc.StrokeLines(TrackPointCount, TrackPoints);
1827 delete[] TrackPoints;
1833 if (!g_pAIS)
return;
1837 if (!cp->GetShowAIS())
return;
1842 const auto ¤t_targets = g_pAIS->GetTargetList();
1850 }
else if (cp->m_canvasIndex == 0) {
1855 for (
const auto &it : current_targets) {
1857 auto td = it.second;
1858 double So, Cpa, Rang, Siz = 0.0;
1859 So = g_ScaledNumWeightSOG / 12 *
1861 if (So > g_ScaledNumWeightSOG) So = g_ScaledNumWeightSOG;
1863 if (td->bCPA_Valid) {
1864 Cpa = g_ScaledNumWeightCPA - g_ScaledNumWeightCPA / 4 * td->CPA;
1867 if (td->TCPA > .0) Cpa = Cpa + Cpa * g_ScaledNumWeightTCPA / 100;
1868 if (Cpa < .0) Cpa = .0;
1872 Rang = g_ScaledNumWeightRange / 10 * td->Range_NM;
1873 if (Rang > g_ScaledNumWeightRange) Rang = g_ScaledNumWeightRange;
1874 Rang = g_ScaledNumWeightRange - Rang;
1876 Siz = g_ScaledNumWeightSizeOfT / 30 * (td->DimA + td->DimB);
1877 if (Siz > g_ScaledNumWeightSizeOfT) Siz = g_ScaledNumWeightSizeOfT;
1878 td->importance = (float)So + Cpa + Rang + Siz;
1884 AISImportanceSwitchPoint = 0.0;
1886 float *Array =
new float[g_ShowScaled_Num];
1887 for (
int i = 0; i < g_ShowScaled_Num; i++) Array[i] = 0.0;
1891 if (cp->GetAttenAIS()) {
1892 for (
const auto &it : current_targets) {
1893 auto td = it.second;
1894 if (vp.GetBBox().Contains(td->Lat, td->Lon)) {
1895 if (td->importance > AISImportanceSwitchPoint) {
1896 Array[LowestInd] = td->importance;
1898 AISImportanceSwitchPoint = Array[0];
1900 for (
int i = 1; i < g_ShowScaled_Num; i++) {
1901 if (Array[i] < AISImportanceSwitchPoint) {
1902 AISImportanceSwitchPoint = Array[i];
1915 for (
const auto &it : current_targets) {
1916 auto td = it.second;
1917 if ((td->SOG < g_SOGminCOG_kts) &&
1918 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1919 AISDrawTarget(td.get(), dc, vp, cp);
1923 for (
const auto &it : current_targets) {
1924 auto td = it.second;
1925 if ((td->SOG >= g_SOGminCOG_kts) &&
1926 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1927 AISDrawTarget(td.get(), dc, vp, cp);
1928 if (td->importance > 0) AISDrawTarget(td.get(), dc, vp, cp);
1932 for (
const auto &it : current_targets) {
1933 auto td = it.second;
1934 if ((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))
1935 AISDrawTarget(td.get(), dc, vp, cp);
1940 if (!g_pAIS)
return false;
1942 if (!cc->GetShowAIS())
return false;
1945 for (
const auto &it : g_pAIS->GetTargetList()) {
1946 auto td = it.second;
1947 if (vp.GetBBox().Contains(td->Lat, td->Lon))
return true;
Global state for AIS decoder.
Dialog for displaying AIS target alerts.
Dialog for querying detailed information about an AIS target.
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Gets a font object for a UI element.
Represents the view port for chart display in OpenCPN.
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.
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.