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"
56 #include "line_clip.h"
58 #include "ocpn_frame.h"
59 #include "OCPNPlatform.h"
66 int g_ais_cog_predictor_width;
69 int ImportanceSwitchPoint = 100;
71 extern ArrayOfMmsiProperties g_MMSI_Props_Array;
72 extern bool g_bopengl;
74 extern float g_ShipScaleFactorExp;
76 float AISImportanceSwitchPoint = 0.0;
79 static const long long lNaN = 0xfff8000000000000;
80 #define NAN (*(double *)&lNaN)
83 wxString 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) {
225 return cp->GetCanvasPointPix(rlat, rlon, r);
227 *r = vp.GetPixFromLL(rlat, rlon);
231 static 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);
244 static 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);
270 float vp_scale = vp.view_scale_ppm;
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) {
365 ll_gc_ll(lat1, lon1, sa->left_bound_deg + i * (sa->right_bound_deg - sa->left_bound_deg) / 18 , sa->radius_m / 1852.0,
367 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
368 points.push_back(target_point);
371 ll_gc_ll(lat1, lon1, sa->right_bound_deg , sa->radius_m / 1852.0,
373 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
374 points.push_back(target_point);
379 case AIS8_001_22_SHAPE_POLYGON:
382 case AIS8_001_22_SHAPE_POLYLINE: {
383 double lat = sa->latitude;
384 double lon = sa->longitude;
385 for (
int i = 0; i < 4; ++i) {
386 ll_gc_ll(lat, lon, sa->angles[i], sa->dists_m[i] / 1852.0,
388 wxPoint target_point;
389 GetCanvasPointPix(vp, cp, lat, lon, &target_point);
390 points.push_back(target_point);
392 dc.DrawLines(points.size(), &points.front());
396 if (draw_polygon) dc.DrawPolygon(points.size(), &points.front());
404 dc.SetBrush(brush_save);
408 static void TargetFrame(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
410 int gap2 = 2 * radius / 6;
412 wxPen pen_save = dc.GetPen();
416 dc.DrawLine(x - radius, y + gap2, x - radius, y + radius);
417 dc.DrawLine(x - radius, y + radius, x - gap2, y + radius);
418 dc.DrawLine(x + gap2, y + radius, x + radius, y + radius);
419 dc.DrawLine(x + radius, y + radius, x + radius, y + gap2);
420 dc.DrawLine(x + radius, y - gap2, x + radius, y - radius);
421 dc.DrawLine(x + radius, y - radius, x + gap2, y - radius);
422 dc.DrawLine(x - gap2, y - radius, x - radius, y - radius);
423 dc.DrawLine(x - radius, y - radius, x - radius, y - gap2);
428 static void AtoN_Diamond(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius,
431 wxPen pen_save = dc.GetPen();
434 wxPen aton_WhiteBorderPen;
437 int rad1a = radius / 2;
438 int rad2a = radius / 4;
444 if ((td->NavStatus == ATON_VIRTUAL_OFFPOSITION) ||
445 (td->NavStatus == ATON_REAL_OFFPOSITION))
446 aton_DrawPen = wxPen(GetGlobalColor(_T (
"URED" )), pen.GetWidth());
448 aton_DrawPen = wxPen(GetGlobalColor(_T (
"UBLCK" )), pen.GetWidth());
450 bool b_virt = (td->NavStatus == ATON_VIRTUAL) |
451 (td->NavStatus == ATON_VIRTUAL_ONPOSITION) |
452 (td->NavStatus == ATON_VIRTUAL_OFFPOSITION);
454 if (b_virt) aton_DrawPen.SetStyle(wxPENSTYLE_SHORT_DASH);
456 aton_WhiteBorderPen =
457 wxPen(GetGlobalColor(_T (
"CHWHT" )), aton_DrawPen.GetWidth() + 2);
462 diamond[0] = wxPoint(radius, 0);
463 diamond[1] = wxPoint(0, -radius);
464 diamond[2] = wxPoint(-radius, 0);
465 diamond[3] = wxPoint(0, radius);
466 diamond[4] = wxPoint(radius, 0);
467 dc.SetPen(aton_WhiteBorderPen);
468 dc.DrawLines(5, diamond, x, y);
469 dc.SetPen(aton_DrawPen);
470 dc.DrawLines(5, diamond, x, y);
472 aton_DrawPen = wxPen(GetGlobalColor(_T (
"UBLCK" )),
474 aton_WhiteBorderPen =
475 wxPen(GetGlobalColor(_T (
"CHWHT" )), aton_DrawPen.GetWidth() + 2);
479 cross[0] = wxPoint(-rad2a, 0);
480 cross[1] = wxPoint(rad2a, 0);
481 cross[2] = wxPoint(0, 0);
482 cross[3] = wxPoint(0, rad2a);
483 cross[4] = wxPoint(0, -rad2a);
484 dc.SetPen(aton_WhiteBorderPen);
485 dc.DrawLines(5, cross, x, y);
486 dc.SetPen(aton_DrawPen);
487 dc.DrawLines(5, cross, x, y);
489 wxPoint TriPointUp[4];
490 TriPointUp[0] = wxPoint(-rad1a, 0);
491 TriPointUp[1] = wxPoint(rad1a, 0);
492 TriPointUp[2] = wxPoint(0, -rad1a);
493 TriPointUp[3] = wxPoint(-rad1a, 0);
495 wxPoint TriPointDown[4];
496 TriPointDown[0] = wxPoint(-rad1a, -rad1a);
497 TriPointDown[1] = wxPoint(rad1a, -rad1a);
498 TriPointDown[2] = wxPoint(0, 0);
499 TriPointDown[3] = wxPoint(-rad1a, -rad1a);
501 wxPoint CircleOpen[16];
502 CircleOpen[0] = wxPoint(-1, 5);
503 CircleOpen[1] = wxPoint(1, 5);
504 CircleOpen[2] = wxPoint(3, 4);
505 CircleOpen[3] = wxPoint(4, 3);
506 CircleOpen[4] = wxPoint(5, 1);
507 CircleOpen[5] = wxPoint(5, -1);
508 CircleOpen[6] = wxPoint(4, -3);
509 CircleOpen[7] = wxPoint(3, -4);
510 CircleOpen[8] = wxPoint(1, -5);
511 CircleOpen[9] = wxPoint(-1, -5);
512 CircleOpen[10] = wxPoint(-3, -4);
513 CircleOpen[11] = wxPoint(-4, -3);
514 CircleOpen[12] = wxPoint(-5, -1);
515 CircleOpen[13] = wxPoint(-4, 3);
516 CircleOpen[14] = wxPoint(-3, 4);
517 CircleOpen[15] = wxPoint(-1, 5);
519 switch (td->ShipType) {
522 dc.SetPen(aton_WhiteBorderPen);
523 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
524 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
525 dc.SetPen(aton_DrawPen);
526 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
527 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
531 dc.SetPen(aton_WhiteBorderPen);
532 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
533 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
534 dc.SetPen(aton_DrawPen);
535 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
536 dc.DrawLines(4, TriPointUp, x, y - radius - rad1a - 3);
540 dc.SetPen(aton_WhiteBorderPen);
541 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
542 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
543 dc.SetPen(aton_DrawPen);
544 dc.DrawLines(4, TriPointDown, x, y - radius - 1);
545 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
549 dc.SetPen(aton_WhiteBorderPen);
550 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
551 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
552 dc.SetPen(aton_DrawPen);
553 dc.DrawLines(4, TriPointUp, x, y - radius - 1);
554 dc.DrawLines(4, TriPointDown, x, y - radius - rad1a - 3);
559 aRect[0] = wxPoint(-rad3a, 0);
560 aRect[1] = wxPoint(-rad3a, -rad3a - rad3a);
561 aRect[2] = wxPoint(rad3a, -rad3a - rad3a);
562 aRect[3] = wxPoint(rad3a, 0);
563 aRect[4] = wxPoint(-rad3a, 0);
564 dc.SetPen(aton_WhiteBorderPen);
565 dc.DrawLines(5, aRect, x, y - radius - 1);
566 dc.SetPen(aton_DrawPen);
567 dc.DrawLines(5, aRect, x, y - radius - 1);
571 dc.SetPen(aton_WhiteBorderPen);
572 dc.DrawLines(4, TriPointUp, x, y - radius);
573 dc.SetPen(aton_DrawPen);
574 dc.DrawLines(4, TriPointUp, x, y - radius);
578 dc.SetPen(aton_WhiteBorderPen);
579 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
580 dc.SetPen(aton_DrawPen);
581 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
582 dc.SetPen(aton_WhiteBorderPen);
583 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
584 dc.SetPen(aton_DrawPen);
585 dc.DrawLines(16, CircleOpen, x, y - radius - 16);
589 dc.SetPen(aton_WhiteBorderPen);
590 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
591 dc.SetPen(aton_DrawPen);
592 dc.DrawLines(16, CircleOpen, x, y - radius - 5);
596 cross[0] = wxPoint(-rad2a, -rad2a);
597 cross[1] = wxPoint(rad2a, rad2a);
598 cross[2] = wxPoint(0, 0);
599 cross[3] = wxPoint(-rad2a, rad2a);
600 cross[4] = wxPoint(rad2a, -rad2a);
601 dc.SetPen(aton_WhiteBorderPen);
602 dc.DrawLines(5, cross, x, y - radius - rad3a);
603 dc.SetPen(aton_DrawPen);
604 dc.DrawLines(5, cross, x, y - radius - rad3a);
612 static void Base_Square(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
614 int gap2 = 2 * radius / 6;
615 int pen_width = pen.GetWidth();
617 wxPen pen_save = dc.GetPen();
621 dc.DrawLine(x - radius, y - radius, x - radius, y + radius);
622 dc.DrawLine(x - radius, y + radius, x + radius, y + radius);
623 dc.DrawLine(x + radius, y + radius, x + radius, y - radius);
624 dc.DrawLine(x + radius, y - radius, x - radius, y - radius);
628 pen.SetWidth(pen_width);
631 dc.DrawLine(x - gap2, y, x + gap2, y);
632 dc.DrawLine(x, y - gap2, x, y + gap2);
637 static void SART_Render(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
639 int gap = (radius * 12) / 10;
640 int pen_width = pen.GetWidth();
642 wxPen pen_save = dc.GetPen();
646 wxBrush brush_save = dc.GetBrush();
647 wxBrush *ppBrush = wxTheBrushList->FindOrCreateBrush(
648 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
649 dc.SetBrush(*ppBrush);
651 dc.DrawCircle(x, y, radius);
655 pen.SetWidth(pen_width);
658 dc.DrawLine(x - gap, y - gap, x + gap, y + gap);
659 dc.DrawLine(x - gap, y + gap, x + gap, y - gap);
661 dc.SetBrush(brush_save);
667 static void spherical_ll_gc_ll(
float lat,
float lon,
float brg,
float dist,
668 float *dlat,
float *dlon) {
669 float angr = brg / 180 * M_PI;
670 float latr = lat * M_PI / 180;
671 float D = dist / 3443;
672 float sD = sinf(D), cD = cosf(D);
673 float sy = sinf(latr), cy = cosf(latr);
674 float sa = sinf(angr), ca = cosf(angr);
676 *dlon = lon + asinf(sa * sD / cy) * 180 / M_PI;
677 *dlat = asinf(sy * cD + cy * sD * ca) * 180 / M_PI;
681 float AIS_scale_factor;
682 float AIS_nominal_target_size_mm;
683 float AIS_nominal_icon_size_pixels;
684 float AIS_pix_factor;
685 float AIS_user_scale_factor;
686 double AIS_nominal_line_width_pix;
688 float AIS_width_interceptbar_base;
689 float AIS_width_interceptbar_top;
690 float AIS_intercept_bar_circle_diameter;
691 float AIS_width_interceptline;
692 float AIS_width_cogpredictor_base;
693 float AIS_width_cogpredictor_line;
694 float AIS_width_target_outline;
695 float AIS_icon_diameter;
696 wxFont *AIS_NameFont;
698 static void AISSetMetrics() {
699 AIS_scale_factor = g_current_monitor_dip_px_ratio;
701 double DPIscale = 1.0;
702 DPIscale = g_Platform->GetDisplayDIPMult(gFrame);
712 AIS_nominal_target_size_mm = 30.0 / g_Platform->GetDisplayDPmm();
716 AIS_nominal_target_size_mm = wxMin(AIS_nominal_target_size_mm, 10.0);
717 AIS_nominal_target_size_mm = wxMax(AIS_nominal_target_size_mm, 5.0);
719 AIS_nominal_icon_size_pixels =
720 wxMax(4.0, g_Platform->GetDisplayDPmm() *
721 AIS_nominal_target_size_mm);
723 AIS_pix_factor = AIS_nominal_icon_size_pixels /
726 AIS_scale_factor *= AIS_pix_factor;
728 AIS_user_scale_factor = g_ShipScaleFactorExp;
729 if (g_ShipScaleFactorExp > 1.0)
730 AIS_user_scale_factor = (log(g_ShipScaleFactorExp) + 1.0) *
733 AIS_scale_factor *= AIS_user_scale_factor;
737 AIS_nominal_line_width_pix =
738 wxMax(2, g_Platform->GetDisplayDPmm() / (4.0 / DPIscale));
741 AIS_width_interceptbar_base = 3 * AIS_nominal_line_width_pix;
742 AIS_width_interceptbar_top = 1.5 * AIS_nominal_line_width_pix;
743 AIS_intercept_bar_circle_diameter = 3.5 * AIS_nominal_line_width_pix;
744 AIS_width_interceptline = 2 * AIS_nominal_line_width_pix;
745 AIS_width_cogpredictor_base = 3 * AIS_nominal_line_width_pix;
746 AIS_width_cogpredictor_line = 1.3 * AIS_nominal_line_width_pix;
747 AIS_width_target_outline = 1.4 * AIS_nominal_line_width_pix;
748 AIS_icon_diameter = AIS_intercept_bar_circle_diameter * AIS_user_scale_factor * AIS_scale_factor;
750 wxFont *font = FontMgr::Get().GetFont(_(
"AIS Target Name"), 12);
751 double scaler = DPIscale;
753 AIS_NameFont = FindOrCreateFont_PlugIn(
754 font->GetPointSize() / scaler, font->GetFamily(), font->GetStyle(),
755 font->GetWeight(),
false, font->GetFaceName());
761 if (NULL == td)
return;
763 static bool firstTimeUse =
true;
766 g_AisFirstTimeUse =
true;
769 g_AisFirstTimeUse =
false;
770 firstTimeUse =
false;
774 if (td->b_lost)
return;
779 if ((g_bHideMoored) && (td->SOG <= g_ShowMoored_Kts) &&
780 (td->NavStatus != NOT_UNDER_COMMAND) &&
781 ((td->Class == AIS_CLASS_A) || (td->Class == AIS_CLASS_B)))
785 if (!td->b_positionOnceValid)
return;
788 if (td->b_OwnShip)
return;
791 float target_sog = td->SOG;
792 if ((td->SOG > 102.2) && !td->b_SarAircraftPosnReport) target_sog = 0.;
795 wxPoint TargetPoint, PredPoint;
798 if (td->n_alert_state == AIS_ALERT_SET) {
802 if (vp.GetBBox().Contains(td->Lat, td->Lon))
806 if (1 && td->b_show_track) {
807 if (td->m_ptrack.size() > 0) {
809 if (vp.GetBBox().Contains(ptrack_point.m_lat, ptrack_point.m_lon))
819 float pred_lat, pred_lon;
820 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
821 target_sog * g_ShowCOG_Mins / 60., &pred_lat, &pred_lon);
824 if (vp.GetBBox().Contains(pred_lat, pred_lon))
828 box.SetFromSegment(td->Lat, td->Lon, pred_lat, pred_lon);
831 if (!vp.GetBBox().IntersectOut(box)) drawit++;
837 GetCanvasPointPix(vp, cp, td->Lat, td->Lon, &TargetPoint);
838 GetCanvasPointPix(vp, cp, pred_lat, pred_lon, &PredPoint);
840 bool b_hdgValid =
true;
842 float theta = (float)-PI / 2.;
844 if ((
int)(td->HDG) != 511) {
845 theta = ((td->HDG - 90) * PI / 180.) + vp.rotation;
849 if (!g_bInlandEcdis) {
854 float angle_distance_nm = (100. / vp.view_scale_ppm) / 1852.;
855 float angle_lat, angle_lon;
856 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG, angle_distance_nm,
857 &angle_lat, &angle_lon);
860 GetCanvasPointPix(vp, cp, angle_lat, angle_lon, &AnglePoint);
862 if (abs(AnglePoint.x - TargetPoint.x) > 0) {
863 if (target_sog > g_SOGminCOG_kts) {
864 theta = atan2f((
double)(AnglePoint.y - TargetPoint.y),
865 (
double)(AnglePoint.x - TargetPoint.x));
868 theta = (float)-PI / 2.;
870 if (AnglePoint.y > TargetPoint.y)
871 theta = (float)PI / 2.;
875 if (td->SOG >= g_SOGminCOG_kts)
884 float sin_theta = sinf(theta), cos_theta = cosf(theta);
887 dash_long[0] = (int)(1.0 * gFrame->GetPrimaryCanvas()
890 (int)(0.5 * gFrame->GetPrimaryCanvas()->GetPixPerMM());
892 int targetscale = 100;
895 idxCC = cp->m_canvasIndex;
897 if (idxCC > AIS_TARGETDATA_MAX_CANVAS - 1)
900 if (cp->GetAttenAIS()) {
901 if (td->NavStatus <= 15) {
905 if (td->importance < AISImportanceSwitchPoint)
906 targetscale = td->last_scale[idxCC] - 2;
908 if (td->importance > AISImportanceSwitchPoint)
909 targetscale = td->last_scale[idxCC] + 5;
910 if (targetscale > 100) targetscale = 100;
911 if (targetscale < 50) targetscale = 50;
912 td->last_scale[idxCC] = targetscale;
918 wxPoint ais_real_size[6];
919 bool bcan_draw_size =
true;
920 if (g_bDrawAISSize) {
921 if (td->DimA + td->DimB == 0 || td->DimC + td->DimD == 0) {
922 bcan_draw_size =
false;
924 double ref_lat, ref_lon;
925 ll_gc_ll(td->Lat, td->Lon, 0, 100. / 1852., &ref_lat, &ref_lon);
926 wxPoint2DDouble b_point = vp.GetDoublePixFromLL(td->Lat, td->Lon);
927 wxPoint2DDouble r_point = vp.GetDoublePixFromLL(ref_lat, ref_lon);
928 double ppm = r_point.GetDistance(b_point) / 100.;
929 double offwid = (td->DimC + td->DimD) * ppm * 0.25;
930 double offlen = (td->DimA + td->DimB) * ppm * 0.15;
931 ais_real_size[0].x = -td->DimD * ppm;
932 ais_real_size[0].y = -td->DimB * ppm;
933 ais_real_size[1].x = -td->DimD * ppm;
934 ais_real_size[1].y = td->DimA * ppm - offlen;
935 ais_real_size[2].x = -td->DimD * ppm + offwid;
936 ais_real_size[2].y = td->DimA * ppm;
937 ais_real_size[3].x = td->DimC * ppm - offwid;
938 ais_real_size[3].y = td->DimA * ppm;
939 ais_real_size[4].x = td->DimC * ppm;
940 ais_real_size[4].y = td->DimA * ppm - offlen;
941 ais_real_size[5].x = td->DimC * ppm;
942 ais_real_size[5].y = -td->DimB * ppm;
944 if (ais_real_size[4].x - ais_real_size[0].x < 16 ||
945 ais_real_size[2].y - ais_real_size[0].y < 30)
946 bcan_draw_size =
false;
948 bcan_draw_size =
true;
949 transrot_pts(6, ais_real_size, sin_theta, cos_theta);
956 wxPoint ais_quad_icon[4] = {wxPoint(-8, -6), wxPoint(0, 24), wxPoint(8, -6),
958 wxPoint ais_octo_icon[8] = {wxPoint(4, 8), wxPoint(8, 4), wxPoint(8, -4),
959 wxPoint(4, -8), wxPoint(-4, -8), wxPoint(-8, -4),
960 wxPoint(-8, 4), wxPoint(-4, 8)};
962 if (!g_bInlandEcdis) {
964 if (targetscale == 50) {
965 ais_quad_icon[0] = wxPoint(-4, -3);
966 ais_quad_icon[1] = wxPoint(0, 12);
967 ais_quad_icon[2] = wxPoint(4, -3);
968 ais_quad_icon[3] = wxPoint(0, -3);
969 }
else if (targetscale != 100) {
971 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
972 ais_quad_icon[1] = wxPoint(0, (
int)24 * targetscale / 100);
974 wxPoint((
int)8 * targetscale / 100, (
int)-6 * targetscale / 100);
975 ais_quad_icon[3] = wxPoint(0, (
int)-6 * targetscale / 100);
979 if (td->Class == AIS_CLASS_B) ais_quad_icon[3].y = 0;
981 if ((td->Class == AIS_GPSG_BUDDY) || (td->b_isFollower)) {
982 ais_quad_icon[0] = wxPoint(-5, -12);
983 ais_quad_icon[1] = wxPoint(-3, 12);
984 ais_quad_icon[2] = wxPoint(3, 12);
985 ais_quad_icon[3] = wxPoint(5, -12);
986 }
else if (td->Class == AIS_DSC) {
987 ais_quad_icon[0].y = 0;
988 ais_quad_icon[1].y = 8;
989 ais_quad_icon[2].y = 0;
990 ais_quad_icon[3].y = -8;
991 }
else if (td->Class == AIS_APRS) {
992 ais_quad_icon[0] = wxPoint(-8, -8);
993 ais_quad_icon[1] = wxPoint(-8, 8);
994 ais_quad_icon[2] = wxPoint(8, 8);
995 ais_quad_icon[3] = wxPoint(8, -8);
998 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1001 iconPoints = ais_quad_icon;
1005 transrot_pts(4, ais_quad_icon, sin_theta, cos_theta);
1007 iconPoints = ais_quad_icon;
1010 iconPoints = ais_octo_icon;
1014 wxColour UBLCK = GetGlobalColor(_T (
"UBLCK" ));
1015 dc.SetPen(wxPen(UBLCK));
1018 wxColour UINFG = GetGlobalColor(_T (
"UINFG" ));
1019 wxBrush target_brush = wxBrush(UINFG);
1022 if (td->b_isEuroInland && !g_bInlandEcdis)
1023 target_brush = wxBrush(GetGlobalColor(_T (
"TEAL1" )));
1026 if (td->b_nameFromCache)
1027 target_brush = wxBrush(GetGlobalColor(_T (
"GREEN5" )));
1030 wxColour URED = GetGlobalColor(_T (
"URED" ));
1031 if (!td->b_nameValid) target_brush = wxBrush(GetGlobalColor(_T (
"CHYLW" )));
1033 if ((td->Class == AIS_DSC) &&
1034 ((td->ShipType == 12) || (td->ShipType == 16)))
1035 target_brush = wxBrush(URED);
1037 if (td->b_SarAircraftPosnReport) target_brush = wxBrush(UINFG);
1039 if ((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid))
1040 target_brush = wxBrush(URED);
1042 if ((td->n_alert_state == AIS_ALERT_NO_DIALOG_SET) && (td->bCPA_Valid) &&
1043 (!td->b_isFollower))
1044 target_brush = wxBrush(URED);
1046 if (td->b_positionDoubtful)
1047 target_brush = wxBrush(GetGlobalColor(_T (
"UINFF" )));
1049 wxPen target_outline_pen(UBLCK, AIS_width_target_outline);
1052 if (((td->n_alert_state == AIS_ALERT_SET) && (td->bCPA_Valid)) ||
1053 (td->b_show_AIS_CPA && (td->bCPA_Valid))) {
1055 double tcpa_lat, tcpa_lon;
1056 ll_gc_ll(td->Lat, td->Lon, td->COG, target_sog * td->TCPA / 60., &tcpa_lat,
1059 wxPoint TPoint = TargetPoint;
1060 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1063 ClipResult res = cohen_sutherland_line_clip_i(
1064 &TPoint.x, &TPoint.y, &tCPAPoint.x, &tCPAPoint.y, 0, vp.pix_width, 0,
1067 if (res != Invisible) {
1068 wxPen ppPen2(URED, AIS_width_cogpredictor_line, wxPENSTYLE_USER_DASH);
1069 ppPen2.SetDashes(2, dash_long);
1072 dc.StrokeLine(TPoint.x, TPoint.y, tCPAPoint.x, tCPAPoint.y);
1076 double ocpa_lat, ocpa_lon;
1079 if (std::isnan(gCog) || std::isnan(gSog)) {
1083 ll_gc_ll(gLat, gLon, gCog, gSog * td->TCPA / 60., &ocpa_lat, &ocpa_lon);
1088 GetCanvasPointPix(vp, cp, ocpa_lat, ocpa_lon, &oCPAPoint);
1089 GetCanvasPointPix(vp, cp, tcpa_lat, tcpa_lon, &tCPAPoint);
1092 wxPoint oCPAPoint_unclipped = oCPAPoint;
1093 wxPoint tCPAPoint_unclipped = tCPAPoint;
1096 ClipResult ores = cohen_sutherland_line_clip_i(
1097 &tCPAPoint.x, &tCPAPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0, vp.pix_width,
1100 if (ores != Invisible) {
1101 wxColour yellow = GetGlobalColor(_T (
"YELO1" ));
1102 dc.SetPen(wxPen(yellow, AIS_width_interceptbar_base));
1103 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1105 wxPen ppPen2(URED, AIS_width_interceptbar_top, wxPENSTYLE_USER_DASH);
1106 ppPen2.SetDashes(2, dash_long);
1108 dc.StrokeLine(tCPAPoint.x, tCPAPoint.y, oCPAPoint.x, oCPAPoint.y);
1111 wxBrush br(GetGlobalColor(_T (
"BLUE3" )));
1113 dc.SetPen(wxPen(UBLCK, AIS_width_target_outline));
1117 tCPAPoint_unclipped.x, tCPAPoint_unclipped.y,
1118 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1120 oCPAPoint_unclipped.x, oCPAPoint_unclipped.y,
1121 AIS_intercept_bar_circle_diameter * AIS_user_scale_factor);
1126 GetCanvasPointPix(vp, cp, gLat, gLon, &oShipPoint);
1127 oCPAPoint = oCPAPoint_unclipped;
1129 ClipResult ownres = cohen_sutherland_line_clip_i(
1130 &oShipPoint.x, &oShipPoint.y, &oCPAPoint.x, &oCPAPoint.y, 0,
1131 vp.pix_width, 0, vp.pix_height);
1133 if (ownres != Invisible) {
1134 wxPen ppPen2(URED, AIS_width_interceptline, wxPENSTYLE_USER_DASH);
1135 ppPen2.SetDashes(2, dash_long);
1138 dc.StrokeLine(oShipPoint.x, oShipPoint.y, oCPAPoint.x, oCPAPoint.y);
1141 dc.SetPen(wxPen(UBLCK));
1142 dc.SetBrush(wxBrush(URED));
1148 auto alert_dlg_active =
1150 if (alert_dlg_active && alert_dlg_active->IsShown() && cp) {
1151 if (alert_dlg_active->Get_Dialog_MMSI() == td->MMSI)
1152 cp->JaggyCircle(dc, wxPen(URED, 2), TargetPoint.x, TargetPoint.y, 100);
1157 if (g_pais_query_dialog_active && g_pais_query_dialog_active->IsShown()) {
1158 if (g_pais_query_dialog_active->GetMMSI() == td->MMSI)
1159 TargetFrame(dc, wxPen(UBLCK, 2), TargetPoint.x, TargetPoint.y, 25);
1164 if ((g_bShowCOG) && (target_sog > g_SOGminCOG_kts) && td->b_active) {
1165 int pixx = TargetPoint.x;
1166 int pixy = TargetPoint.y;
1167 int pixx1 = PredPoint.x;
1168 int pixy1 = PredPoint.y;
1172 float l = sqrtf(powf((
float)(PredPoint.x - TargetPoint.x), 2) +
1173 powf((
float)(PredPoint.y - TargetPoint.y), 2));
1176 ClipResult res = cohen_sutherland_line_clip_i(
1177 &pixx, &pixy, &pixx1, &pixy1, 0, vp.pix_width, 0, vp.pix_height);
1179 if (res != Invisible) {
1181 if (targetscale >= 75) {
1182 wxPen wide_pen(target_brush.GetColour(), AIS_width_cogpredictor_base);
1183 dc.SetPen(wide_pen);
1184 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1187 if (AIS_width_cogpredictor_base > 1) {
1189 wxPen narrow_pen(UBLCK, AIS_width_cogpredictor_line);
1190 if (targetscale < 75) {
1191 narrow_pen.SetWidth(1);
1192 narrow_pen.SetStyle(wxPENSTYLE_USER_DASH);
1196 narrow_pen.SetDashes(2, dash_dot);
1198 dc.SetPen(narrow_pen);
1199 dc.StrokeLine(pixx, pixy, pixx1, pixy1);
1203 dc.SetBrush(target_brush);
1204 dc.StrokeCircle(PredPoint.x, PredPoint.y, 5 * targetscale / 100);
1209 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1212 glTranslated(PredPoint.x, PredPoint.y, 0);
1213 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1215 float points[] = {0.0f, 5.0f, 2.5f, 4.330127f, 4.330127f,
1216 2.5f, 5.0f, 0, 4.330127f, -2.5f,
1217 2.5f, -4.330127f, 0, -5.1f, -2.5f,
1218 -4.330127f, -4.330127f, -2.5f, -5.0f, 0,
1219 -4.330127f, 2.5f, -2.5f, 4.330127f, 0,
1221 if (targetscale <= 75) {
1222 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1224 points[i] = points[i] / 2;
1227 wxColour c = target_brush.GetColour();
1228 glColor3ub(c.Red(), c.Green(), c.Blue());
1230 glBegin(GL_TRIANGLE_FAN);
1231 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1233 glVertex2i(points[i], points[i + 1]);
1236 glColor3ub(0, 0, 0);
1237 glLineWidth(AIS_width_target_outline);
1238 glBegin(GL_LINE_LOOP);
1239 for (
unsigned int i = 0; i < (
sizeof points) / (
sizeof *points);
1241 glVertex2i(points[i], points[i + 1]);
1246 dc.SetBrush(target_brush);
1247 dc.StrokeCircle(PredPoint.x, PredPoint.y,
1248 AIS_intercept_bar_circle_diameter *
1249 AIS_user_scale_factor * targetscale / 100);
1256 if ((td->ROTAIS != 0) && (td->ROTAIS != -128) && (!g_bShowScaled)) {
1257 float cog_angle = td->COG * PI / 180.;
1259 float theta2 = theta;
1260 if (td->SOG >= g_SOGminCOG_kts)
1261 theta2 = cog_angle - (PI / 2);
1265 theta2 += (float)PI / 2;
1267 theta2 -= (float)PI / 2;
1269 int xrot = (int)round(pixx1 + (nv * cosf(theta2)));
1270 int yrot = (int)round(pixy1 + (nv * sinf(theta2)));
1271 dc.StrokeLine(pixx1, pixy1, xrot, yrot);
1277 if (td->Class == AIS_ARPA) {
1278 wxPen target_pen(UBLCK, 2);
1280 dc.SetPen(target_pen);
1281 dc.SetBrush(target_brush);
1282 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1.8 * AIS_icon_diameter);
1284 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1);
1286 if (!td->b_active) {
1287 dc.SetPen(wxPen(UBLCK, 2));
1288 dc.StrokeLine(TargetPoint.x - 14, TargetPoint.y, TargetPoint.x + 14,
1290 dc.SetPen(wxPen(UBLCK, 1));
1293 }
else if (td->Class == AIS_METEO) {
1294 wxPen met(UBLCK, (wxMax(target_outline_pen.GetWidth(), 2.5)));
1296 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1297 double met_radius = 1.8 * AIS_icon_diameter;
1298 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, met_radius);
1301 dc.SetPen(wxPen(wxMax(target_outline_pen.GetWidth(), 1)));
1303 dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4,
1304 TargetPoint.x - met_radius / 3,
1305 TargetPoint.y + met_radius / 2);
1307 TargetPoint.x - met_radius / 3, TargetPoint.y + met_radius / 2,
1308 TargetPoint.x - met_radius / 2, TargetPoint.y - met_radius / 2);
1310 dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4,
1311 TargetPoint.x + met_radius / 3,
1312 TargetPoint.y + met_radius / 2);
1314 TargetPoint.x + met_radius / 3, TargetPoint.y + met_radius / 2,
1315 TargetPoint.x + met_radius / 2, TargetPoint.y - met_radius / 2);
1317 }
else if (td->Class == AIS_ATON) {
1318 AtoN_Diamond(dc, wxPen(UBLCK, AIS_width_target_outline), TargetPoint.x, TargetPoint.y, AIS_icon_diameter * 1.5, td);
1319 }
else if (td->Class == AIS_BASE) {
1320 Base_Square(dc, wxPen(UBLCK, AIS_width_target_outline), TargetPoint.x, TargetPoint.y, AIS_icon_diameter);
1321 }
else if (td->Class == AIS_SART) {
1322 if (td->NavStatus == 14)
1323 SART_Render(dc, wxPen(URED, AIS_width_target_outline), TargetPoint.x, TargetPoint.y, AIS_icon_diameter);
1325 SART_Render(dc, wxPen(GetGlobalColor(_T (
"UGREN" )), AIS_width_target_outline), TargetPoint.x,
1326 TargetPoint.y, AIS_icon_diameter);
1328 }
else if (td->b_SarAircraftPosnReport) {
1329 int airtype = (td->MMSI % 1000) / 100;
1330 int ar = airtype == 5 ? 15 : 9;
1331 wxPoint SarIcon[15];
1333 double scaleplus = 1.4;
1335 SarIcon[0] = wxPoint(0, 9) * AIS_scale_factor * scaleplus;
1336 SarIcon[1] = wxPoint(1, 1) * AIS_scale_factor * scaleplus;
1337 SarIcon[2] = wxPoint(2, 1) * AIS_scale_factor * scaleplus;
1338 SarIcon[3] = wxPoint(9, 8) * AIS_scale_factor * scaleplus;
1339 SarIcon[4] = wxPoint(9, 7) * AIS_scale_factor * scaleplus;
1340 SarIcon[5] = wxPoint(3, 0) * AIS_scale_factor * scaleplus;
1341 SarIcon[6] = wxPoint(3, -5) * AIS_scale_factor * scaleplus;
1342 SarIcon[7] = wxPoint(9, -12) * AIS_scale_factor * scaleplus;
1343 SarIcon[8] = wxPoint(9, -13) * AIS_scale_factor * scaleplus;
1344 SarIcon[9] = wxPoint(2, -5) * AIS_scale_factor * scaleplus;
1345 SarIcon[10] = wxPoint(1, -15) * AIS_scale_factor * scaleplus;
1346 SarIcon[11] = wxPoint(3, -16) * AIS_scale_factor * scaleplus;
1347 SarIcon[12] = wxPoint(4, -18) * AIS_scale_factor * scaleplus;
1348 SarIcon[13] = wxPoint(1, -18) * AIS_scale_factor * scaleplus;
1349 SarIcon[14] = wxPoint(0, -19) * AIS_scale_factor * scaleplus;
1351 SarIcon[0] = wxPoint(0, 12) * AIS_scale_factor;
1352 SarIcon[1] = wxPoint(4, 2) * AIS_scale_factor;
1353 SarIcon[2] = wxPoint(16, -2) * AIS_scale_factor;
1354 SarIcon[3] = wxPoint(16, -8) * AIS_scale_factor;
1355 SarIcon[4] = wxPoint(4, -8) * AIS_scale_factor;
1356 SarIcon[5] = wxPoint(3, -16) * AIS_scale_factor;
1357 SarIcon[6] = wxPoint(10, -18) * AIS_scale_factor;
1358 SarIcon[7] = wxPoint(10, -22) * AIS_scale_factor;
1359 SarIcon[8] = wxPoint(0, -22) * AIS_scale_factor;
1366 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1367 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1369 wxPen tri_pen(target_brush.GetColour(), 1);
1371 dc.SetBrush(target_brush);
1374 int mappings[14][3] = {
1375 {0, 1, 10}, {0, 10, 14}, {1, 2, 9}, {1, 9, 10}, {10, 13, 14},
1376 {10, 11, 13}, {11, 12, 13}, {1, 14, 10}, {2, 5, 9}, {5, 6, 9},
1377 {2, 3, 5}, {3, 4, 5}, {6, 7, 8}, {6, 9, 8}};
1380 for (
int i = 0; i < nmap; i++) {
1381 wxPoint ais_tri_icon[3];
1382 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1383 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1386 dc.SetPen(target_outline_pen);
1387 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1388 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1392 for (
int i = 0; i < ar; i++)
1394 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1396 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1399 dc.SetBrush(target_brush);
1401 for (
int i = 0; i < nmap; i++) {
1402 wxPoint ais_tri_icon[3];
1403 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1404 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1407 dc.SetPen(target_outline_pen);
1408 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1409 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1414 for (
int i = 0; i < ar; i++) SarRot[i] = SarIcon[i];
1415 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1417 wxPen tri_pen(target_brush.GetColour(), 1);
1419 dc.SetBrush(target_brush);
1422 int mappings[7][3] = {{0, 1, 4}, {1, 2, 3}, {1, 3, 4}, {0, 4, 5},
1423 {0, 5, 8}, {5, 6, 7}, {5, 7, 8}};
1424 for (
int i = 0; i < 7; i++) {
1425 wxPoint ais_tri_icon[3];
1426 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1427 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1430 dc.SetPen(target_outline_pen);
1431 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1432 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1436 for (
int i = 0; i < ar; i++)
1438 wxPoint(-SarIcon[i].x, SarIcon[i].y);
1440 transrot_pts(ar, SarRot, sin_theta, cos_theta);
1443 dc.SetBrush(target_brush);
1445 for (
int i = 0; i < 7; i++) {
1446 wxPoint ais_tri_icon[3];
1447 for (
int j = 0; j < 3; j++) ais_tri_icon[j] = SarRot[mappings[i][j]];
1448 dc.StrokePolygon(3, ais_tri_icon, TargetPoint.x, TargetPoint.y);
1451 dc.SetPen(target_outline_pen);
1452 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1453 dc.StrokePolygon(ar, SarRot, TargetPoint.x, TargetPoint.y);
1457 if (!td->b_active) {
1458 dc.SetPen(wxPen(UBLCK, 3));
1459 dc.StrokeLine(TargetPoint.x - 16, TargetPoint.y, TargetPoint.x + 16,
1464 wxPen target_pen(UBLCK, 1);
1465 dc.SetPen(target_pen);
1467 wxPoint Point = TargetPoint;
1468 if (g_bDrawAISRealtime &&
1469 (td->Class == AIS_CLASS_A || td->Class == AIS_CLASS_B) &&
1470 td->SOG > g_AIS_RealtPred_Kts && td->SOG < 102.2) {
1471 wxDateTime now = wxDateTime::Now();
1473 int target_age = now.GetTicks() - td->PositionReportTicks;
1476 spherical_ll_gc_ll(td->Lat, td->Lon, td->COG,
1477 td->SOG * target_age / 3600.0, &lat, &lon);
1479 GetCanvasPointPix(vp, cp, lat, lon, &Point);
1481 wxBrush realtime_brush = wxBrush(GetGlobalColor(
"GREY1"));
1482 dc.SetBrush(realtime_brush);
1483 dc.StrokePolygon(nPoints, iconPoints, Point.x, Point.y, AIS_scale_factor);
1485 dc.SetBrush(target_brush);
1488 dc.StrokePolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1493 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1495 wxColour c = target_brush.GetColour();
1496 glColor3ub(c.Red(), c.Green(), c.Blue());
1499 glTranslated(TargetPoint.x, TargetPoint.y, 0);
1500 glScalef(AIS_scale_factor, AIS_scale_factor, AIS_scale_factor);
1502 glBegin(GL_TRIANGLE_FAN);
1505 glVertex2i(ais_quad_icon[3].x, ais_quad_icon[3].y);
1506 glVertex2i(ais_quad_icon[0].x, ais_quad_icon[0].y);
1507 glVertex2i(ais_quad_icon[1].x, ais_quad_icon[1].y);
1508 glVertex2i(ais_quad_icon[2].x, ais_quad_icon[2].y);
1510 for (
int i = 0; i < 8; i++) {
1511 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1516 glLineWidth(AIS_width_target_outline);
1518 glColor3ub(UBLCK.Red(), UBLCK.Green(), UBLCK.Blue());
1520 glBegin(GL_LINE_LOOP);
1521 for (
int i = 0; i < nPoints; i++)
1522 glVertex2i(iconPoints[i].x, iconPoints[i].y);
1527 dc.SetPen(target_outline_pen);
1528 dc.DrawPolygon(nPoints, iconPoints, TargetPoint.x, TargetPoint.y,
1534 if (td->b_isFollower) {
1535 wxPoint ais_follow_stroke[3];
1536 ais_follow_stroke[0] = wxPoint(-3, -20) * AIS_scale_factor;
1537 ais_follow_stroke[1] = wxPoint(0, 0) * AIS_scale_factor;
1538 ais_follow_stroke[2] = wxPoint(3, -20) * AIS_scale_factor;
1540 transrot_pts(3, ais_follow_stroke, sin_theta, cos_theta);
1542 int penWidth = wxMax(target_outline_pen.GetWidth(), 2);
1543 dc.SetPen(wxPen(UBLCK, penWidth));
1544 dc.StrokeLine(ais_follow_stroke[0].x + TargetPoint.x,
1545 ais_follow_stroke[0].y + TargetPoint.y,
1546 ais_follow_stroke[1].x + TargetPoint.x,
1547 ais_follow_stroke[1].y + TargetPoint.y);
1548 dc.StrokeLine(ais_follow_stroke[1].x + TargetPoint.x,
1549 ais_follow_stroke[1].y + TargetPoint.y,
1550 ais_follow_stroke[2].x + TargetPoint.x,
1551 ais_follow_stroke[2].y + TargetPoint.y);
1554 if (g_bDrawAISSize && bcan_draw_size) {
1555 dc.SetPen(target_outline_pen);
1556 dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT));
1557 if (!g_bInlandEcdis) {
1558 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1561 dc.StrokePolygon(6, ais_real_size, TargetPoint.x, TargetPoint.y, 1.0);
1566 dc.SetBrush(wxBrush(GetGlobalColor(_T (
"SHIPS" ))));
1567 int navstatus = td->NavStatus;
1571 if (((td->ShipType >= 40) && (td->ShipType < 50)) &&
1572 (navstatus == UNDERWAY_USING_ENGINE || td->Class == AIS_CLASS_B))
1575 if (targetscale > 90) {
1576 switch (navstatus) {
1579 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1582 case RESTRICTED_MANOEUVRABILITY: {
1584 diamond[0] = wxPoint(4, 0) * AIS_scale_factor;
1585 diamond[1] = wxPoint(0, -6) * AIS_scale_factor;
1586 diamond[2] = wxPoint(-4, 0) * AIS_scale_factor;
1587 diamond[3] = wxPoint(0, 6) * AIS_scale_factor;
1588 dc.StrokePolygon(4, diamond, TargetPoint.x,
1589 TargetPoint.y - (11 * AIS_scale_factor));
1590 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1591 dc.StrokeCircle(TargetPoint.x,
1592 TargetPoint.y - (22 * AIS_scale_factor),
1593 4 * AIS_scale_factor);
1597 case CONSTRAINED_BY_DRAFT: {
1598 wxPoint can[4] = {wxPoint(-3, 0) * AIS_scale_factor,
1599 wxPoint(3, 0) * AIS_scale_factor,
1600 wxPoint(3, -16) * AIS_scale_factor,
1601 wxPoint(-3, -16) * AIS_scale_factor};
1602 dc.StrokePolygon(4, can, TargetPoint.x, TargetPoint.y);
1605 case NOT_UNDER_COMMAND: {
1606 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1607 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1608 4 * AIS_scale_factor);
1613 tri[0] = wxPoint(-4, 0) * AIS_scale_factor;
1614 tri[1] = wxPoint(4, 0) * AIS_scale_factor;
1615 tri[2] = wxPoint(0, -9) * AIS_scale_factor;
1616 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1617 tri[0] = wxPoint(0, -9) * AIS_scale_factor;
1618 tri[1] = wxPoint(4, -18) * AIS_scale_factor;
1619 tri[2] = wxPoint(-4, -18) * AIS_scale_factor;
1620 dc.StrokePolygon(3, tri, TargetPoint.x, TargetPoint.y);
1624 dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 4 * AIS_scale_factor);
1625 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 9,
1626 4 * AIS_scale_factor);
1627 dc.StrokeCircle(TargetPoint.x, TargetPoint.y - 18,
1628 4 * AIS_scale_factor);
1633 dc.SetBrush(target_brush);
1635 wxPoint arrow1[3] = {wxPoint(-4, 20) * AIS_scale_factor,
1636 wxPoint(0, 27) * AIS_scale_factor,
1637 wxPoint(4, 20) * AIS_scale_factor};
1638 transrot_pts(3, arrow1, sin_theta, cos_theta, TargetPoint);
1639 dc.StrokePolygon(3, arrow1);
1641 wxPoint arrow2[3] = {wxPoint(-4, 27) * AIS_scale_factor,
1642 wxPoint(0, 34) * AIS_scale_factor,
1643 wxPoint(4, 27) * AIS_scale_factor};
1644 transrot_pts(3, arrow2, sin_theta, cos_theta, TargetPoint);
1645 dc.StrokePolygon(3, arrow2);
1652 if (!td->b_active) {
1653 wxPoint p1 = transrot(wxPoint((
int)-14 * targetscale / 100, 0), sin_theta,
1654 cos_theta, TargetPoint);
1655 wxPoint p2 = transrot(wxPoint((
int)14 * targetscale / 100, 0), sin_theta,
1656 cos_theta, TargetPoint);
1658 dc.SetPen(wxPen(UBLCK, 2));
1659 dc.StrokeLine(p1.x, p1.y, p2.x, p2.y);
1666 if (td->blue_paddle && td->blue_paddle < 3) {
1667 wxPoint ais_flag_icon[4];
1670 if (g_bInlandEcdis) {
1672 ais_flag_icon[0] = wxPoint(-4, 4);
1673 ais_flag_icon[1] = wxPoint(-4, 11);
1674 ais_flag_icon[2] = wxPoint(-11, 11);
1675 ais_flag_icon[3] = wxPoint(-11, 4);
1676 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1678 ais_flag_icon[0] = wxPoint(TargetPoint.x - 4, TargetPoint.y + 4);
1679 ais_flag_icon[1] = wxPoint(TargetPoint.x - 4, TargetPoint.y - 3);
1680 ais_flag_icon[2] = wxPoint(TargetPoint.x + 3, TargetPoint.y - 3);
1681 ais_flag_icon[3] = wxPoint(TargetPoint.x + 3, TargetPoint.y + 4);
1684 dc.SetPen(wxPen(GetGlobalColor(_T (
"CHWHT" )), penWidth));
1688 wxPoint((
int)-8 * targetscale / 100, (
int)-6 * targetscale / 100);
1690 wxPoint((
int)-2 * targetscale / 100, (
int)18 * targetscale / 100);
1691 ais_flag_icon[2] = wxPoint((
int)-2 * targetscale / 100, 0);
1693 wxPoint((
int)-2 * targetscale / 100, (
int)-6 * targetscale / 100);
1694 transrot_pts(4, ais_flag_icon, sin_theta, cos_theta, TargetPoint);
1696 if (targetscale < 100) penWidth = 1;
1697 dc.SetPen(wxPen(GetGlobalColor(_T (
"CHWHT" )), penWidth));
1699 if (td->blue_paddle == 1) {
1700 ais_flag_icon[1] = ais_flag_icon[0];
1701 ais_flag_icon[2] = ais_flag_icon[3];
1704 dc.SetBrush(wxBrush(GetGlobalColor(_T (
"UINFB" ))));
1705 dc.StrokePolygon(4, ais_flag_icon);
1709 if ((g_bShowAISName) && (targetscale > 75)) {
1710 int true_scale_display = (int)(floor(vp.chart_scale / 100.) * 100);
1711 if (true_scale_display <
1712 g_Show_Target_Name_Scale) {
1714 wxString tgt_name = td->GetFullName();
1715 tgt_name = tgt_name.substr(0, tgt_name.find(_T (
"Unknown" ), 0));
1717 if (tgt_name != wxEmptyString) {
1718 dc.SetFont(*AIS_NameFont);
1719 dc.SetTextForeground(FontMgr::Get().GetFontColor(_(
"AIS Target Name")));
1722 dc.GetTextExtent(_T(
"W"), &w, &h);
1723 h *= g_Platform->GetDisplayDIPMult(gFrame);
1724 w *= g_Platform->GetDisplayDIPMult(gFrame);
1726 if ((td->COG > 90) && (td->COG < 180))
1727 dc.DrawText(tgt_name, TargetPoint.x + w, TargetPoint.y - h);
1729 dc.DrawText(tgt_name, TargetPoint.x + w,
1738 bool b_noshow =
false;
1739 bool b_forceshow =
false;
1740 for (
unsigned int i = 0; i < g_MMSI_Props_Array.GetCount(); i++) {
1741 if (td->MMSI == g_MMSI_Props_Array[i]->MMSI) {
1743 if (TRACKTYPE_NEVER == props->TrackType) {
1746 }
else if (TRACKTYPE_ALWAYS == props->TrackType) {
1754 int TrackLength = td->m_ptrack.size();
1755 if (((!b_noshow && td->b_show_track) || b_forceshow) && (TrackLength > 1)) {
1757 int TrackPointCount;
1758 wxPoint *TrackPoints = 0;
1759 TrackPoints =
new wxPoint[TrackLength];
1760 auto it = td->m_ptrack.begin();
1761 for (TrackPointCount = 0;
1762 it != td->m_ptrack.end() && (TrackPointCount < TrackLength);
1763 TrackPointCount++, ++it) {
1765 GetCanvasPointPix(vp, cp, ptrack_point.m_lat, ptrack_point.m_lon,
1766 &TrackPoints[TrackPointCount]);
1769 wxColour c = GetGlobalColor(_T (
"CHMGD" ));
1770 dc.SetPen(wxPen(c, 1.5 * AIS_nominal_line_width_pix));
1774 std::map<int, Track *>::iterator itt;
1775 itt = g_pAIS->m_persistent_tracks.find(td->MMSI);
1776 if (itt != g_pAIS->m_persistent_tracks.end()) {
1777 auto *ptrack = itt->second;
1778 if (ptrack->m_Colour == wxEmptyString) {
1779 c = GetGlobalColor(_T (
"TEAL1" ));
1780 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1782 for (
unsigned int i = 0;
1783 i <
sizeof(::GpxxColorNames) /
sizeof(wxString); i++) {
1784 if (ptrack->m_Colour == ::GpxxColorNames[i]) {
1785 c = ::GpxxColors[i];
1786 dc.SetPen(wxPen(c, 2.0 * AIS_nominal_line_width_pix));
1794 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1798 glColor3ub(c.Red(), c.Green(), c.Blue());
1799 glBegin(GL_LINE_STRIP);
1801 for (TrackPointCount = 0; TrackPointCount < TrackLength;
1803 glVertex2i(TrackPoints[TrackPointCount].x,
1804 TrackPoints[TrackPointCount].y);
1808 dc.DrawLines(TrackPointCount, TrackPoints);
1811 dc.DrawLines(TrackPointCount, TrackPoints);
1815 if (dc.GetDC()) dc.StrokeLines(TrackPointCount, TrackPoints);
1819 delete[] TrackPoints;
1825 if (!g_pAIS)
return;
1829 if (!cp->GetShowAIS())
return;
1834 const auto ¤t_targets = g_pAIS->GetTargetList();
1842 }
else if (cp->m_canvasIndex == 0) {
1847 for (
const auto &it : current_targets) {
1849 auto td = it.second;
1850 double So, Cpa, Rang, Siz = 0.0;
1851 So = g_ScaledNumWeightSOG / 12 *
1853 if (So > g_ScaledNumWeightSOG) So = g_ScaledNumWeightSOG;
1855 if (td->bCPA_Valid) {
1856 Cpa = g_ScaledNumWeightCPA - g_ScaledNumWeightCPA / 4 * td->CPA;
1859 if (td->TCPA > .0) Cpa = Cpa + Cpa * g_ScaledNumWeightTCPA / 100;
1860 if (Cpa < .0) Cpa = .0;
1864 Rang = g_ScaledNumWeightRange / 10 * td->Range_NM;
1865 if (Rang > g_ScaledNumWeightRange) Rang = g_ScaledNumWeightRange;
1866 Rang = g_ScaledNumWeightRange - Rang;
1868 Siz = g_ScaledNumWeightSizeOfT / 30 * (td->DimA + td->DimB);
1869 if (Siz > g_ScaledNumWeightSizeOfT) Siz = g_ScaledNumWeightSizeOfT;
1870 td->importance = (float)So + Cpa + Rang + Siz;
1876 AISImportanceSwitchPoint = 0.0;
1878 float *Array =
new float[g_ShowScaled_Num];
1879 for (
int i = 0; i < g_ShowScaled_Num; i++) Array[i] = 0.0;
1883 if (cp->GetAttenAIS()) {
1884 for (
const auto &it : current_targets) {
1885 auto td = it.second;
1886 if (vp.GetBBox().Contains(td->Lat, td->Lon)) {
1887 if (td->importance > AISImportanceSwitchPoint) {
1888 Array[LowestInd] = td->importance;
1890 AISImportanceSwitchPoint = Array[0];
1892 for (
int i = 1; i < g_ShowScaled_Num; i++) {
1893 if (Array[i] < AISImportanceSwitchPoint) {
1894 AISImportanceSwitchPoint = Array[i];
1907 for (
const auto &it : current_targets) {
1908 auto td = it.second;
1909 if ((td->SOG < g_SOGminCOG_kts) &&
1910 !((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))) {
1911 AISDrawTarget(td.get(), dc, vp, cp);
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);
1920 if (td->importance > 0) AISDrawTarget(td.get(), dc, vp, cp);
1924 for (
const auto &it : current_targets) {
1925 auto td = it.second;
1926 if ((td->Class == AIS_GPSG_BUDDY) || (td->Class == AIS_DSC))
1927 AISDrawTarget(td.get(), dc, vp, cp);
1932 if (!g_pAIS)
return false;
1934 if (!cc->GetShowAIS())
return false;
1937 for (
const auto &it : g_pAIS->GetTargetList()) {
1938 auto td = it.second;
1939 if (vp.GetBBox().Contains(td->Lat, td->Lon))
return true;
Global state for AIS decoder.