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 wxPoint linepoint = TargetPoint;
1467 wxPoint p1 = transrot(
1468 wxPoint((
int)-14 * targetscale / 100, (
int)-5 * targetscale / 100),
1469 sin_theta, cos_theta, TargetPoint);
1470 wxPoint p2 = transrot(
1471 wxPoint((
int)14 * targetscale / 100, (
int)-5 * targetscale / 100),
1472 sin_theta, cos_theta, TargetPoint);
1474 dc.SetPen(wxPen(UBLCK, 2));
1475 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1479 wxPen target_pen(UBLCK, 1);
1480 dc.SetPen(target_pen);
1482 wxPoint Point = TargetPoint;
1483 if (g_bDrawAISRealtime &&
1484 (td->Class == AIS_CLASS_A || td->Class == AIS_CLASS_B) &&
1485 td->SOG > g_AIS_RealtPred_Kts && td->SOG < 102.2) {
1486 wxDateTime now = wxDateTime::Now();
1488 int target_age = now.GetTicks() - td->PositionReportTicks;
1491 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
1492 td->SOG * target_age / 3600.0, &lat, &lon);
1494 GetCanvasPointPix(vp, cp, lat, lon, &Point);
1496 wxBrush realtime_brush = wxBrush(GetGlobalColor(
"GREY1"));
1497 dc.SetBrush(realtime_brush);
1498 dc.StrokePolygon(nPoints, iconPoints, Point.x, Point.y, AIS_scale_factor);
1500 dc.SetBrush(target_brush);
1503 dc.StrokePolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1508#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1510 wxColour c = target_brush.GetColour();
1511 glColor3ub(c.Red(), c.Green(), c.Blue());
1514 glTranslated(TargetPoint.x, TargetPoint.y, 0);
1515 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1517 glBegin(GL_TRIANGLE_FAN);
1520 glVertex2i(ais_quad_icon[3].x, ais_quad_icon[3].y);
1521 glVertex2i(ais_quad_icon[0].x, ais_quad_icon[0].y);
1522 glVertex2i(ais_quad_icon[1].x, ais_quad_icon[1].y);
1523 glVertex2i(ais_quad_icon[2].x, ais_quad_icon[2].y);
1525 for (
int i = 0; i < 8; i++) {
1526 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1531 glLineWidth(AIS_width_target_outline);
1533 glColor3ub(UBLCK.Red(), UBLCK.Green(), UBLCK.Blue());
1535 glBegin(GL_LINE_LOOP);
1536 for (
int i = 0; i < nPoints; i++)
1537 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1542 dc.SetPen(target_outline_pen);
1543 dc.DrawPolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1549 if (td->b_isFollower) {
1550 wxPoint ais_follow_stroke[3];
1551 ais_follow_stroke[0] = wxPoint(-3, -20) * AIS_scale_factor;
1552 ais_follow_stroke[1] = wxPoint(0, 0) * AIS_scale_factor;
1553 ais_follow_stroke[2] = wxPoint(3, -20) * AIS_scale_factor;
1555 transrot_pts(3, ais_follow_stroke, sin_theta, cos_theta);
1557 int penWidth = wxMax(target_outline_pen.GetWidth(), 2);
1558 dc.SetPen(wxPen(UBLCK, penWidth));
1559 dc.StrokeLine(ais_follow_stroke[0].x + TargetPoint.x,
1560 ais_follow_stroke[0].y + TargetPoint.y,
1561 ais_follow_stroke[1].x + TargetPoint.x,
1562 ais_follow_stroke[1].y + TargetPoint.y);
1563 dc.StrokeLine(ais_follow_stroke[1].x + TargetPoint.x,
1564 ais_follow_stroke[1].y + TargetPoint.y,
1565 ais_follow_stroke[2].x + TargetPoint.x,
1566 ais_follow_stroke[2].y + TargetPoint.y);
1569 if (g_bDrawAISSize && bcan_draw_size) {
1570 dc.SetPen(target_outline_pen);
1571 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1572 if (!g_bInlandEcdis) {
1573 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1576 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1581 dc.SetBrush(wxBrush(GetGlobalColor(_T (
"SHIPS" ))));
1582 int navstatus = td->NavStatus;
1586 if (((td->ShipType >= 40) && (td->ShipType < 50)) &&
1587 (navstatus == UNDERWAY_USING_ENGINE || td->Class == AIS_CLASS_B))
1590 if (targetscale > 90) {
1591 switch (navstatus) {
1594 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1597 case RESTRICTED_MANOEUVRABILITY: {
1599 diamond[0] = wxPoint(4, 0) * AIS_scale_factor;
1600 diamond[1] = wxPoint(0, -6) * AIS_scale_factor;
1601 diamond[2] = wxPoint(-4, 0) * AIS_scale_factor;
1602 diamond[3] = wxPoint(0, 6) * AIS_scale_factor;
1603 dc.StrokePolygon(4, diamond, TargetPoint.x,
1604 TargetPoint.y - (11 * AIS_scale_factor));
1605 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1606 dc.StrokeCircle(TargetPoint.x,
1607 TargetPoint.y - (22 * AIS_scale_factor),
1608 4 * AIS_scale_factor);
1612 case CONSTRAINED_BY_DRAFT: {
1613 wxPoint can[4] = {wxPoint(-3, 0) * AIS_scale_factor,
1614 wxPoint(3, 0) * AIS_scale_factor,
1615 wxPoint(3, -16) * AIS_scale_factor,
1616 wxPoint(-3, -16) * AIS_scale_factor};
1617 dc.StrokePolygon(4, can, TargetPoint.x, TargetPoint.y);
1620 case NOT_UNDER_COMMAND: {
1621 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1622 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1623 4 * AIS_scale_factor);
1628 tri[0] = wxPoint(-4, 0) * AIS_scale_factor;
1629 tri[1] = wxPoint(4, 0) * AIS_scale_factor;
1630 tri[2] = wxPoint(0, -9) * AIS_scale_factor;
1631 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1632 tri[0] = wxPoint(0, -9) * AIS_scale_factor;
1633 tri[1] = wxPoint(4, -18) * AIS_scale_factor;
1634 tri[2] = wxPoint(-4, -18) * AIS_scale_factor;
1635 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1639 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1640 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1641 4 * AIS_scale_factor);
1642 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 18,
1643 4 * AIS_scale_factor);
1648 dc.SetBrush(target_brush);
1650 wxPoint arrow1[3] = {wxPoint(-4, 20) * AIS_scale_factor,
1651 wxPoint(0, 27) * AIS_scale_factor,
1652 wxPoint(4, 20) * AIS_scale_factor};
1653 transrot_pts(3, arrow1, sin_theta, cos_theta, TargetPoint);
1654 dc.StrokePolygon(3, arrow1);
1656 wxPoint arrow2[3] = {wxPoint(-4, 27) * AIS_scale_factor,
1657 wxPoint(0, 34) * AIS_scale_factor,
1658 wxPoint(4, 27) * AIS_scale_factor};
1659 transrot_pts(3, arrow2, sin_theta, cos_theta, TargetPoint);
1660 dc.StrokePolygon(3, arrow2);
1667 if (!td->b_active) {
1668 wxPoint p1 = transrot(wxPoint((
int)-14 * targetscale / 100, 0), sin_theta,
1669 cos_theta, TargetPoint);
1670 wxPoint p2 = transrot(wxPoint((
int)14 * targetscale / 100, 0), sin_theta,
1671 cos_theta, TargetPoint);
1673 dc.SetPen(wxPen(UBLCK, 2));
1674 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1681 if (td->blue_paddle && td->blue_paddle < 3) {
1682 wxPoint ais_flag_icon[4];
1685 if (g_bInlandEcdis) {
1687 ais_flag_icon[0] = wxPoint(-4, 4);
1688 ais_flag_icon[1] = wxPoint(-4, 11);
1689 ais_flag_icon[2] = wxPoint(-11, 11);
1690 ais_flag_icon[3] = wxPoint(-11, 4);
1691 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1693 ais_flag_icon[0] = wxPoint(TargetPoint.x - 4, TargetPoint.y + 4);
1694 ais_flag_icon[1] = wxPoint(TargetPoint.x - 4, TargetPoint.y - 3);
1695 ais_flag_icon[2] = wxPoint(TargetPoint.x + 3, TargetPoint.y - 3);
1696 ais_flag_icon[3] = wxPoint(TargetPoint.x + 3, TargetPoint.y + 4);
1699 dc.SetPen(wxPen(GetGlobalColor(_T (
"CHWHT" )), penWidth));
1703 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
1705 wxPoint((
int)-2 * targetscale / 100, (
int)18 * targetscale / 100);
1706 ais_flag_icon[2] = wxPoint((
int)-2 * targetscale / 100, 0);
1708 wxPoint((
int)-2 * targetscale / 100, (
int)-6 * targetscale / 100);
1709 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1711 if (targetscale < 100) penWidth = 1;
1712 dc.SetPen(wxPen(GetGlobalColor(_T (
"CHWHT" )), penWidth));
1714 if (td->blue_paddle == 1) {
1715 ais_flag_icon[1] = ais_flag_icon[0];
1716 ais_flag_icon[2] = ais_flag_icon[3];
1719 dc.SetBrush(wxBrush(GetGlobalColor(_T (
"UINFB" ))));
1720 dc.StrokePolygon(4, ais_flag_icon);
1724 if ((g_bShowAISName) && (targetscale > 75)) {
1725 int true_scale_display = (int)(floor(vp.
chart_scale / 100.) * 100);
1726 if (true_scale_display <
1727 g_Show_Target_Name_Scale) {
1729 wxString tgt_name = td->GetFullName();
1730 tgt_name = tgt_name.substr(0, tgt_name.find(_T (
"Unknown" ), 0));
1732 if (tgt_name != wxEmptyString) {
1733 dc.SetFont(*AIS_NameFont);
1734 dc.SetTextForeground(FontMgr::Get().GetFontColor(_(
"AIS Target Name")));
1737 dc.GetTextExtent(_T(
"W"), &w, &h);
1741 if ((td->COG > 90) && (td->COG < 180))
1742 dc.DrawText(tgt_name, TargetPoint.x + w, TargetPoint.y - h);
1744 dc.DrawText(tgt_name, TargetPoint.x + w,
1753 bool b_noshow =
false;
1754 bool b_forceshow =
false;
1755 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
1756 if (td->MMSI == g_MMSI_Props_Array[i]->MMSI) {
1758 if (TRACKTYPE_NEVER == props->TrackType) {
1761 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
1769 int TrackLength = td->m_ptrack.size();
1770 if (((!b_noshow && td->b_show_track) || b_forceshow) && (TrackLength > 1)) {
1772 int TrackPointCount;
1773 wxPoint *TrackPoints = 0;
1774 TrackPoints =
new wxPoint[TrackLength];
1775 auto it = td->m_ptrack.begin();
1776 for (TrackPointCount = 0;
1777 it != td->m_ptrack.end() && (TrackPointCount < TrackLength);
1778 TrackPointCount++, ++it) {
1780 GetCanvasPointPix(vp, cp, ptrack_point.m_lat, ptrack_point.m_lon,
1781 &TrackPoints[TrackPointCount]);
1784 wxColour c = GetGlobalColor(_T (
"CHMGD" ));
1785 dc.SetPen(wxPen(c, 1.5 * AIS_nominal_line_width_pix));
1789 std::map<int, Track *>::iterator itt;
1790 itt = g_pAIS->m_persistent_tracks.find(td->MMSI);
1791 if (itt != g_pAIS->m_persistent_tracks.end()) {
1792 auto *ptrack = itt->second;
1793 if (ptrack->m_Colour == wxEmptyString) {
1794 c = GetGlobalColor(_T (
"TEAL1" ));
1795 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1797 for (
unsigned int i = 0;
1798 i <
sizeof(::GpxxColorNames) /
sizeof(wxString); i++) {
1799 if (ptrack->m_Colour == ::GpxxColorNames[i]) {
1800 c = ::GpxxColors[i];
1801 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1809#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1813 glColor3ub(c.Red(), c.Green(), c.Blue());
1814 glBegin(GL_LINE_STRIP);
1816 for (TrackPointCount = 0; TrackPointCount < TrackLength;
1818 glVertex2i(TrackPoints[TrackPointCount].x,
1819 TrackPoints[TrackPointCount].y);
1823 dc.DrawLines(TrackPointCount, TrackPoints);
1826 dc.DrawLines(TrackPointCount, TrackPoints);
1830 if (dc.GetDC()) dc.StrokeLines(TrackPointCount, TrackPoints);
1834 delete[] TrackPoints;
1840 if (!g_pAIS)
return;
1844 if (!cp->GetShowAIS())
return;
1849 const auto ¤t_targets = g_pAIS->GetTargetList();
1857 }
else if (cp->m_canvasIndex == 0) {
1862 for (
const auto &it : current_targets) {
1864 auto td = it.second;
1865 double So, Cpa, Rang, Siz = 0.0;
1866 So = g_ScaledNumWeightSOG / 12 *
1868 if (So > g_ScaledNumWeightSOG) So = g_ScaledNumWeightSOG;
1870 if (td->bCPA_Valid) {
1871 Cpa = g_ScaledNumWeightCPA - g_ScaledNumWeightCPA / 4 * td->CPA;
1874 if (td->TCPA > .0) Cpa = Cpa + Cpa * g_ScaledNumWeightTCPA / 100;
1875 if (Cpa < .0) Cpa = .0;
1879 Rang = g_ScaledNumWeightRange / 10 * td->Range_NM;
1880 if (Rang > g_ScaledNumWeightRange) Rang = g_ScaledNumWeightRange;
1881 Rang = g_ScaledNumWeightRange - Rang;
1883 Siz = g_ScaledNumWeightSizeOfT / 30 * (td->DimA + td->DimB);
1884 if (Siz > g_ScaledNumWeightSizeOfT) Siz = g_ScaledNumWeightSizeOfT;
1885 td->importance = (float)So + Cpa + Rang + Siz;
1891 AISImportanceSwitchPoint = 0.0;
1893 float *Array =
new float[g_ShowScaled_Num];
1894 for (
int i = 0; i < g_ShowScaled_Num; i++) Array[i] = 0.0;
1898 if (cp->GetAttenAIS()) {
1899 for (
const auto &it : current_targets) {
1900 auto td = it.second;
1901 if (vp.GetBBox().Contains(td->Lat, td->Lon)) {
1902 if (td->importance > AISImportanceSwitchPoint) {
1903 Array[LowestInd] = td->importance;
1905 AISImportanceSwitchPoint = Array[0];
1907 for (
int i = 1; i < g_ShowScaled_Num; i++) {
1908 if (Array[i] < AISImportanceSwitchPoint) {
1909 AISImportanceSwitchPoint = Array[i];
1922 for (
const auto &it : current_targets) {
1923 auto td = it.second;
1924 if ((td->SOG < g_SOGminCOG_kts) &&
1925 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1926 AISDrawTarget(td.get(), dc, vp, cp);
1930 for (
const auto &it : current_targets) {
1931 auto td = it.second;
1932 if ((td->SOG >= g_SOGminCOG_kts) &&
1933 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1934 AISDrawTarget(td.get(), dc, vp, cp);
1935 if (td->importance > 0) AISDrawTarget(td.get(), dc, vp, cp);
1939 for (
const auto &it : current_targets) {
1940 auto td = it.second;
1941 if ((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))
1942 AISDrawTarget(td.get(), dc, vp, cp);
1947 if (!g_pAIS)
return false;
1949 if (!cc->GetShowAIS())
return false;
1952 for (
const auto &it : g_pAIS->GetTargetList()) {
1953 auto td = it.second;
1954 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.
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.
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Gets a font object for a UI element.
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.
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.