35#include <wx/graphics.h>
36#include <wx/clipbrd.h>
37#include <wx/aui/aui.h>
41#include "o_sound/o_sound.h"
48#include "model/geodesic.h"
54#include "model/nav_object_database.h"
116#include "tide_time.h"
123#include "user_colors.h"
125#include "s57_ocpn_utils.h"
128#include "androidUTIL.h"
138#include <wx/msw/msvcrt.h>
147#define printf printf2
149int __cdecl printf2(
const char *format, ...) {
153 va_start(argptr, format);
154 int ret = vsnprintf(str,
sizeof(str), format, argptr);
156 OutputDebugStringA(str);
161#if defined(__MSVC__) && (_MSC_VER < 1700)
162#define trunc(d) ((d > 0) ? floor(d) : ceil(d))
168#define OCPN_ALT_MENUBAR 1
175static bool g_bSmoothRecenter =
true;
176static bool bDrawCurrentValues;
197static bool mouse_leftisdown;
198static bool g_brouteCreating;
199static int r_gamma_mult;
200static int g_gamma_mult;
201static int b_gamma_mult;
202static int gamma_state;
203static bool g_brightness_init;
204static int last_brightness;
205static wxGLContext *g_pGLcontext;
208static wxDialog *g_pcurtain;
210static wxString g_lastS52PLIBPluginMessage;
213#define MAX_BRIGHT 100
220EVT_ACTIVATE(ChartCanvas::OnActivate)
221EVT_SIZE(ChartCanvas::OnSize)
222#ifndef HAVE_WX_GESTURE_EVENTS
223EVT_MOUSE_EVENTS(ChartCanvas::MouseEvent)
225EVT_TIMER(DBLCLICK_TIMER, ChartCanvas::MouseTimedEvent)
226EVT_TIMER(PAN_TIMER, ChartCanvas::PanTimerEvent)
227EVT_TIMER(MOVEMENT_TIMER, ChartCanvas::MovementTimerEvent)
228EVT_TIMER(MOVEMENT_STOP_TIMER, ChartCanvas::MovementStopTimerEvent)
229EVT_TIMER(CURTRACK_TIMER, ChartCanvas::OnCursorTrackTimerEvent)
230EVT_TIMER(ROT_TIMER, ChartCanvas::RotateTimerEvent)
231EVT_TIMER(ROPOPUP_TIMER, ChartCanvas::OnRolloverPopupTimerEvent)
232EVT_TIMER(ROUTEFINISH_TIMER, ChartCanvas::OnRouteFinishTimerEvent)
233EVT_KEY_DOWN(ChartCanvas::OnKeyDown)
234EVT_KEY_UP(ChartCanvas::OnKeyUp)
235EVT_CHAR(ChartCanvas::OnKeyChar)
236EVT_MOUSE_CAPTURE_LOST(ChartCanvas::LostMouseCapture)
237EVT_KILL_FOCUS(ChartCanvas::OnKillFocus)
238EVT_SET_FOCUS(ChartCanvas::OnSetFocus)
239EVT_MENU(-1, ChartCanvas::OnToolLeftClick)
240EVT_TIMER(DEFERRED_FOCUS_TIMER, ChartCanvas::OnDeferredFocusTimerEvent)
241EVT_TIMER(MOVEMENT_VP_TIMER, ChartCanvas::MovementVPTimerEvent)
242EVT_TIMER(DRAG_INERTIA_TIMER, ChartCanvas::OnChartDragInertiaTimer)
243EVT_TIMER(JUMP_EASE_TIMER, ChartCanvas::OnJumpEaseTimer)
244EVT_TIMER(MENU_TIMER, ChartCanvas::OnMenuTimer)
245EVT_TIMER(TAP_TIMER, ChartCanvas::OnTapTimer)
252 m_nmea_log(nmea_log) {
253 parent_frame = frame;
254 m_canvasIndex = canvasIndex;
258 SetBackgroundColour(wxColour(0, 0, 0));
259 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
263 m_bDrawingRoute =
false;
264 m_bRouteEditing =
false;
265 m_bMarkEditing =
false;
266 m_bRoutePoinDragging =
false;
267 m_bIsInRadius =
false;
268 m_bMayToggleMenuBar =
true;
271 m_bShowNavobjects =
true;
273 m_bAppendingRoute =
false;
274 pThumbDIBShow = NULL;
275 m_bShowCurrent =
false;
277 bShowingCurrent =
false;
281 m_b_paint_enable =
true;
284 pss_overlay_bmp = NULL;
285 pss_overlay_mask = NULL;
286 m_bChartDragging =
false;
287 m_bMeasure_Active =
false;
288 m_bMeasure_DistCircle =
false;
289 m_pMeasureRoute = NULL;
290 m_pTrackRolloverWin = NULL;
291 m_pRouteRolloverWin = NULL;
292 m_pAISRolloverWin = NULL;
294 m_disable_edge_pan =
false;
295 m_dragoffsetSet =
false;
299 m_singleChart = NULL;
300 m_upMode = NORTH_UP_MODE;
302 m_bShowAISScaled =
false;
303 m_timed_move_vp_active =
false;
305 m_disable_adjust_on_zoom =
false;
312 m_pSelectedRoute = NULL;
313 m_pSelectedTrack = NULL;
314 m_pRoutePointEditTarget = NULL;
315 m_pFoundPoint = NULL;
316 m_pMouseRoute = NULL;
317 m_prev_pMousePoint = NULL;
318 m_pEditRouteArray = NULL;
319 m_pFoundRoutePoint = NULL;
320 m_FinishRouteOnKillFocus =
true;
322 m_pRolloverRouteSeg = NULL;
323 m_pRolloverTrackSeg = NULL;
324 m_bsectors_shown =
false;
326 m_bbrightdir =
false;
331 m_pos_image_user_day = NULL;
332 m_pos_image_user_dusk = NULL;
333 m_pos_image_user_night = NULL;
334 m_pos_image_user_grey_day = NULL;
335 m_pos_image_user_grey_dusk = NULL;
336 m_pos_image_user_grey_night = NULL;
339 m_rotation_speed = 0;
345 m_pos_image_user_yellow_day = NULL;
346 m_pos_image_user_yellow_dusk = NULL;
347 m_pos_image_user_yellow_night = NULL;
349 SetOwnShipState(SHIP_INVALID);
351 undo =
new Undo(
this);
357 m_focus_indicator_pix = 1;
359 m_pCurrentStack = NULL;
360 m_bpersistent_quilt =
false;
361 m_piano_ctx_menu = NULL;
363 m_NotificationsList = NULL;
364 m_notification_button = NULL;
366 g_ChartNotRenderScaleFactor = 2.0;
367 m_bShowScaleInStatusBar =
true;
370 m_bShowScaleInStatusBar =
false;
371 m_show_focus_bar =
true;
373 m_bShowOutlines =
false;
374 m_bDisplayGrid =
false;
375 m_bShowDepthUnits =
true;
376 m_encDisplayCategory = (int)STANDARD;
378 m_encShowLights =
true;
379 m_encShowAnchor =
true;
380 m_encShowDataQual =
false;
382 m_pQuilt =
new Quilt(
this);
387 g_PrintingInProgress =
false;
389#ifdef HAVE_WX_GESTURE_EVENTS
390 m_oldVPSScale = -1.0;
391 m_popupWanted =
false;
394 m_inLongPress =
false;
397 m_sw_left_down.Start();
398 m_sw_left_up.Start();
402 singleClickEventIsValid =
false;
411 pCursorPencil = NULL;
416 SetCursor(*pCursorArrow);
418 pPanTimer =
new wxTimer(
this, m_MouseDragging);
421 pMovementTimer =
new wxTimer(
this, MOVEMENT_TIMER);
422 pMovementTimer->Stop();
424 pMovementStopTimer =
new wxTimer(
this, MOVEMENT_STOP_TIMER);
425 pMovementStopTimer->Stop();
427 pRotDefTimer =
new wxTimer(
this, ROT_TIMER);
428 pRotDefTimer->Stop();
430 m_DoubleClickTimer =
new wxTimer(
this, DBLCLICK_TIMER);
431 m_DoubleClickTimer->Stop();
433 m_VPMovementTimer.SetOwner(
this, MOVEMENT_VP_TIMER);
434 m_chart_drag_inertia_timer.SetOwner(
this, DRAG_INERTIA_TIMER);
435 m_chart_drag_inertia_active =
false;
437 m_easeTimer.SetOwner(
this, JUMP_EASE_TIMER);
438 m_animationActive =
false;
439 m_menuTimer.SetOwner(
this, MENU_TIMER);
440 m_tap_timer.SetOwner(
this, TAP_TIMER);
444 m_panx_target_final = m_pany_target_final = 0;
445 m_panx_target_now = m_pany_target_now = 0;
448 pCurTrackTimer =
new wxTimer(
this, CURTRACK_TIMER);
449 pCurTrackTimer->Stop();
450 m_curtrack_timer_msec = 10;
452 m_wheelzoom_stop_oneshot = 0;
453 m_last_wheel_dir = 0;
455 m_RolloverPopupTimer.SetOwner(
this, ROPOPUP_TIMER);
457 m_deferredFocusTimer.SetOwner(
this, DEFERRED_FOCUS_TIMER);
459 m_rollover_popup_timer_msec = 20;
461 m_routeFinishTimer.SetOwner(
this, ROUTEFINISH_TIMER);
463 m_b_rot_hidef =
true;
468 m_upMode = NORTH_UP_MODE;
469 m_bLookAhead =
false;
473 m_cs = GLOBAL_COLOR_SCHEME_DAY;
476 VPoint.view_scale_ppm = 1;
480 m_canvas_scale_factor = 1.;
482 m_canvas_width = 1000;
484 m_overzoomTextWidth = 0;
485 m_overzoomTextHeight = 0;
494 m_pEM_Fathoms = NULL;
496 CreateDepthUnitEmbossMaps(GLOBAL_COLOR_SCHEME_DAY);
498 m_pEM_OverZoom = NULL;
500 CreateOZEmbossMapData(GLOBAL_COLOR_SCHEME_DAY);
508 m_bmTideDusk = CreateDimBitmap(m_bmTideDay, .50);
511 m_bmTideNight = CreateDimBitmap(m_bmTideDay, .20);
514 double factor_dusk = 0.5;
515 double factor_night = 0.25;
518 m_os_image_red_day = style->GetIcon(
"ship-red").ConvertToImage();
520 int rimg_width = m_os_image_red_day.GetWidth();
521 int rimg_height = m_os_image_red_day.GetHeight();
523 m_os_image_red_dusk = m_os_image_red_day.Copy();
524 m_os_image_red_night = m_os_image_red_day.Copy();
526 for (
int iy = 0; iy < rimg_height; iy++) {
527 for (
int ix = 0; ix < rimg_width; ix++) {
528 if (!m_os_image_red_day.IsTransparent(ix, iy)) {
529 wxImage::RGBValue rgb(m_os_image_red_day.GetRed(ix, iy),
530 m_os_image_red_day.GetGreen(ix, iy),
531 m_os_image_red_day.GetBlue(ix, iy));
532 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
533 hsv.value = hsv.value * factor_dusk;
534 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
535 m_os_image_red_dusk.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
537 hsv = wxImage::RGBtoHSV(rgb);
538 hsv.value = hsv.value * factor_night;
539 nrgb = wxImage::HSVtoRGB(hsv);
540 m_os_image_red_night.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
546 m_os_image_grey_day =
547 style->GetIcon(
"ship-red").ConvertToImage().ConvertToGreyscale();
549 int gimg_width = m_os_image_grey_day.GetWidth();
550 int gimg_height = m_os_image_grey_day.GetHeight();
552 m_os_image_grey_dusk = m_os_image_grey_day.Copy();
553 m_os_image_grey_night = m_os_image_grey_day.Copy();
555 for (
int iy = 0; iy < gimg_height; iy++) {
556 for (
int ix = 0; ix < gimg_width; ix++) {
557 if (!m_os_image_grey_day.IsTransparent(ix, iy)) {
558 wxImage::RGBValue rgb(m_os_image_grey_day.GetRed(ix, iy),
559 m_os_image_grey_day.GetGreen(ix, iy),
560 m_os_image_grey_day.GetBlue(ix, iy));
561 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
562 hsv.value = hsv.value * factor_dusk;
563 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
564 m_os_image_grey_dusk.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
566 hsv = wxImage::RGBtoHSV(rgb);
567 hsv.value = hsv.value * factor_night;
568 nrgb = wxImage::HSVtoRGB(hsv);
569 m_os_image_grey_night.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
575 m_os_image_yellow_day = m_os_image_red_day.Copy();
577 gimg_width = m_os_image_yellow_day.GetWidth();
578 gimg_height = m_os_image_yellow_day.GetHeight();
580 m_os_image_yellow_dusk = m_os_image_red_day.Copy();
581 m_os_image_yellow_night = m_os_image_red_day.Copy();
583 for (
int iy = 0; iy < gimg_height; iy++) {
584 for (
int ix = 0; ix < gimg_width; ix++) {
585 if (!m_os_image_yellow_day.IsTransparent(ix, iy)) {
586 wxImage::RGBValue rgb(m_os_image_yellow_day.GetRed(ix, iy),
587 m_os_image_yellow_day.GetGreen(ix, iy),
588 m_os_image_yellow_day.GetBlue(ix, iy));
589 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
590 hsv.hue += 60. / 360.;
591 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
592 m_os_image_yellow_day.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
594 hsv = wxImage::RGBtoHSV(rgb);
595 hsv.value = hsv.value * factor_dusk;
596 hsv.hue += 60. / 360.;
597 nrgb = wxImage::HSVtoRGB(hsv);
598 m_os_image_yellow_dusk.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
600 hsv = wxImage::RGBtoHSV(rgb);
601 hsv.hue += 60. / 360.;
602 hsv.value = hsv.value * factor_night;
603 nrgb = wxImage::HSVtoRGB(hsv);
604 m_os_image_yellow_night.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
610 m_pos_image_red = &m_os_image_red_day;
611 m_pos_image_yellow = &m_os_image_yellow_day;
612 m_pos_image_grey = &m_os_image_grey_day;
616 m_pBrightPopup = NULL;
619 if (!g_bdisable_opengl) m_pQuilt->EnableHighDefinitionZoom(
true);
624 m_Piano =
new Piano(
this);
626 m_bShowCompassWin =
false;
628 m_Compass->SetScaleFactor(g_compass_scalefactor);
629 m_Compass->Show(
false);
631 if (IsPrimaryCanvas() && !g_disableNotifications) {
633 m_notification_button->SetScaleFactor(g_compass_scalefactor);
634 m_notification_button->Show(
true);
637 m_pianoFrozen =
false;
639 SetMinSize(wxSize(200, 200));
641 m_displayScale = 1.0;
642#if defined(__WXOSX__) || defined(__WXGTK3__)
644 m_displayScale = GetContentScaleFactor();
646 VPoint.SetPixelScale(m_displayScale);
648#ifdef HAVE_WX_GESTURE_EVENTS
651 if (!EnableTouchEvents(wxTOUCH_ZOOM_GESTURE | wxTOUCH_PRESS_GESTURES)) {
652 wxLogError(
"Failed to enable touch events");
657 Bind(wxEVT_LONG_PRESS, &ChartCanvas::OnLongPress,
this);
658 Bind(wxEVT_PRESS_AND_TAP, &ChartCanvas::OnPressAndTap,
this);
660 Bind(wxEVT_RIGHT_UP, &ChartCanvas::OnRightUp,
this);
661 Bind(wxEVT_RIGHT_DOWN, &ChartCanvas::OnRightDown,
this);
663 Bind(wxEVT_LEFT_UP, &ChartCanvas::OnLeftUp,
this);
664 Bind(wxEVT_LEFT_DOWN, &ChartCanvas::OnLeftDown,
this);
666 Bind(wxEVT_MOUSEWHEEL, &ChartCanvas::OnWheel,
this);
667 Bind(wxEVT_MOTION, &ChartCanvas::OnMotion,
this);
672 auto ¬eman = NotificationManager::GetInstance();
674 wxDEFINE_EVENT(EVT_NOTIFICATIONLIST_CHANGE, wxCommandEvent);
675 evt_notificationlist_change_listener.Listen(
676 noteman.evt_notificationlist_change,
this, EVT_NOTIFICATIONLIST_CHANGE);
677 Bind(EVT_NOTIFICATIONLIST_CHANGE, [&](wxCommandEvent &) {
678 if (m_NotificationsList && m_NotificationsList->IsShown()) {
679 m_NotificationsList->ReloadNotificationList();
685ChartCanvas::~ChartCanvas() {
686 delete pThumbDIBShow;
694 delete pCursorPencil;
698 delete pMovementTimer;
699 delete pMovementStopTimer;
700 delete pCurTrackTimer;
702 delete m_DoubleClickTimer;
704 delete m_pTrackRolloverWin;
705 delete m_pRouteRolloverWin;
706 delete m_pAISRolloverWin;
707 delete m_pBrightPopup;
713 m_dc_route.SelectObject(wxNullBitmap);
716 delete pWorldBackgroundChart;
717 delete pss_overlay_bmp;
721 delete m_pEM_Fathoms;
723 delete m_pEM_OverZoom;
728 delete m_pos_image_user_day;
729 delete m_pos_image_user_dusk;
730 delete m_pos_image_user_night;
731 delete m_pos_image_user_grey_day;
732 delete m_pos_image_user_grey_dusk;
733 delete m_pos_image_user_grey_night;
734 delete m_pos_image_user_yellow_day;
735 delete m_pos_image_user_yellow_dusk;
736 delete m_pos_image_user_yellow_night;
740 if (!g_bdisable_opengl) {
743#if wxCHECK_VERSION(2, 9, 0)
744 if (IsPrimaryCanvas() && g_bopengl)
delete g_pGLcontext;
751 MUIBar *muiBar = m_muiBar;
755 delete m_pCurrentStack;
758 delete m_notification_button;
761void ChartCanvas::SetupGridFont() {
762 wxFont *dFont = FontMgr::Get().
GetFont(_(
"GridText"), 0);
764 int gridFontSize = wxMax(10, dFont->GetPointSize() * dpi_factor);
766 gridFontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL,
767 FALSE, wxString(
"Arial"));
770void ChartCanvas::RebuildCursors() {
776 delete pCursorPencil;
780 double cursorScale = exp(g_GUIScaleFactor * (0.693 / 5.0));
785 wxImage ICursorLeft = style->GetIcon(
"left").ConvertToImage();
786 wxImage ICursorRight = style->GetIcon(
"right").ConvertToImage();
787 wxImage ICursorUp = style->GetIcon(
"up").ConvertToImage();
788 wxImage ICursorDown = style->GetIcon(
"down").ConvertToImage();
789 wxImage ICursorPencil =
790 style->GetIconScaled(
"pencil", pencilScale).ConvertToImage();
791 wxImage ICursorCross = style->GetIcon(
"cross").ConvertToImage();
793#if !defined(__WXMSW__) && !defined(__WXQT__)
794 ICursorLeft.ConvertAlphaToMask(128);
795 ICursorRight.ConvertAlphaToMask(128);
796 ICursorUp.ConvertAlphaToMask(128);
797 ICursorDown.ConvertAlphaToMask(128);
798 ICursorPencil.ConvertAlphaToMask(10);
799 ICursorCross.ConvertAlphaToMask(10);
802 if (ICursorLeft.Ok()) {
803 ICursorLeft.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, 0);
804 ICursorLeft.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 15);
805 pCursorLeft =
new wxCursor(ICursorLeft);
807 pCursorLeft =
new wxCursor(wxCURSOR_ARROW);
809 if (ICursorRight.Ok()) {
810 ICursorRight.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, 31);
811 ICursorRight.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 15);
812 pCursorRight =
new wxCursor(ICursorRight);
814 pCursorRight =
new wxCursor(wxCURSOR_ARROW);
816 if (ICursorUp.Ok()) {
817 ICursorUp.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, 15);
818 ICursorUp.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 0);
819 pCursorUp =
new wxCursor(ICursorUp);
821 pCursorUp =
new wxCursor(wxCURSOR_ARROW);
823 if (ICursorDown.Ok()) {
824 ICursorDown.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, 15);
825 ICursorDown.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 31);
826 pCursorDown =
new wxCursor(ICursorDown);
828 pCursorDown =
new wxCursor(wxCURSOR_ARROW);
830 if (ICursorPencil.Ok()) {
831 ICursorPencil.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, 0 * pencilScale);
832 ICursorPencil.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 16 * pencilScale);
833 pCursorPencil =
new wxCursor(ICursorPencil);
835 pCursorPencil =
new wxCursor(wxCURSOR_ARROW);
837 if (ICursorCross.Ok()) {
838 ICursorCross.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, 13);
839 ICursorCross.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 12);
840 pCursorCross =
new wxCursor(ICursorCross);
842 pCursorCross =
new wxCursor(wxCURSOR_ARROW);
844 pCursorArrow =
new wxCursor(wxCURSOR_ARROW);
845 pPlugIn_Cursor = NULL;
848void ChartCanvas::CanvasApplyLocale() {
849 CreateDepthUnitEmbossMaps(m_cs);
850 CreateOZEmbossMapData(m_cs);
853void ChartCanvas::SetupGlCanvas() {
856 if (!g_bdisable_opengl) {
858 wxLogMessage(
"Creating glChartCanvas");
863 if (IsPrimaryCanvas()) {
870 wxGLContext *pctx =
new wxGLContext(m_glcc);
871 m_glcc->SetContext(pctx);
875 m_glcc->SetContext(
new wxGLContext(m_glcc, g_pGLcontext));
877 m_glcc->SetContext(g_pGLcontext);
887 if (!g_bdisable_opengl) {
890 wxLogMessage(
"Creating glChartCanvas");
894 if (IsPrimaryCanvas()) {
895 qDebug() <<
"Creating Primary glChartCanvas";
903 wxGLContext *pctx =
new wxGLContext(m_glcc);
904 m_glcc->SetContext(pctx);
906 m_glcc->m_pParentCanvas =
this;
909 qDebug() <<
"Creating Secondary glChartCanvas";
916 top_frame::Get()->GetWxGlCanvas());
919 wxGLContext *pwxctx =
new wxGLContext(m_glcc);
920 m_glcc->SetContext(pwxctx);
921 m_glcc->m_pParentCanvas =
this;
929void ChartCanvas::OnKillFocus(wxFocusEvent &WXUNUSED(event)) {
930 RefreshRect(wxRect(0, 0, GetClientSize().x, m_focus_indicator_pix),
false);
945 if (m_routeState && m_FinishRouteOnKillFocus)
946 m_routeFinishTimer.Start(20, wxTIMER_ONE_SHOT);
948 if (m_routeState && m_FinishRouteOnKillFocus) FinishRoute();
952void ChartCanvas::OnSetFocus(wxFocusEvent &WXUNUSED(event)) {
953 m_routeFinishTimer.Stop();
957 top_frame::Get()->UpdateGlobalMenuItems(
this);
959 RefreshRect(wxRect(0, 0, GetClientSize().x, m_focus_indicator_pix),
false);
962void ChartCanvas::OnRouteFinishTimerEvent(wxTimerEvent &event) {
963 if (m_routeState && m_FinishRouteOnKillFocus) FinishRoute();
966#ifdef HAVE_WX_GESTURE_EVENTS
967void ChartCanvas::OnLongPress(wxLongPressEvent &event) {
974 m_popupWanted =
true;
976 m_inLongPress = !g_bhide_context_menus;
979 m_menuPos =
event.GetPosition();
980 wxMouseEvent ev(wxEVT_LEFT_UP);
981 ev.m_x = m_menuPos.x;
982 ev.m_y = m_menuPos.y;
983 wxPostEvent(
this, ev);
987 wxMouseEvent ev_right_click(wxEVT_RIGHT_DOWN);
988 ev_right_click.m_x = m_menuPos.x;
989 ev_right_click.m_y = m_menuPos.y;
990 MouseEvent(ev_right_click);
995void ChartCanvas::OnPressAndTap(wxPressAndTapEvent &event) {
999void ChartCanvas::OnRightUp(wxMouseEvent &event) { MouseEvent(event); }
1001void ChartCanvas::OnRightDown(wxMouseEvent &event) { MouseEvent(event); }
1003void ChartCanvas::OnLeftUp(wxMouseEvent &event) {
1005 long dt = m_sw_left_up.Time() - m_sw_up_time;
1006 m_sw_up_time = m_sw_left_up.Time();
1017 wxPoint pos =
event.GetPosition();
1021 if (!m_popupWanted) {
1022 wxMouseEvent ev(wxEVT_LEFT_UP);
1029 m_popupWanted =
false;
1031 wxMouseEvent ev(wxEVT_RIGHT_DOWN);
1038void ChartCanvas::OnLeftDown(wxMouseEvent &event) {
1043 long dt = m_sw_left_down.Time() - m_sw_down_time;
1044 m_sw_down_time = m_sw_left_down.Time();
1068 int max_double_click_distance = wxSystemSettings::GetMetric(wxSYS_DCLICK_X) *
1070 wxRect tap_area(m_lastTapPos.x - max_double_click_distance,
1071 m_lastTapPos.y - max_double_click_distance,
1072 max_double_click_distance * 2, max_double_click_distance * 2);
1075 if (m_tap_timer.IsRunning() && tap_area.Contains(event.GetPosition())) {
1081 m_lastTapPos =
event.GetPosition();
1082 m_tap_timer.StartOnce(
1086 if (m_tap_count == 2) {
1090 wxMouseEvent ev(wxEVT_LEFT_DCLICK);
1103void ChartCanvas::OnMotion(wxMouseEvent &event) {
1108 event.m_leftDown = m_leftdown;
1112void ChartCanvas::OnZoom(wxZoomGestureEvent &event) {
1114 if (event.IsGestureEnd())
return;
1116 double factor =
event.GetZoomFactor();
1118 if (event.IsGestureStart() || m_oldVPSScale < 0) {
1123 double wanted_factor = m_oldVPSScale / current_vps * factor;
1128 if (event.IsGestureStart()) {
1129 m_zoomStartPoint =
event.GetPosition();
1131 wxPoint delta =
event.GetPosition() - m_zoomStartPoint;
1133 m_zoomStartPoint =
event.GetPosition();
1137void ChartCanvas::OnWheel(wxMouseEvent &event) { MouseEvent(event); }
1139void ChartCanvas::OnDoubleLeftClick(wxMouseEvent &event) {
1140 DoRotateCanvas(0.0);
1144void ChartCanvas::OnTapTimer(wxTimerEvent &event) {
1149void ChartCanvas::OnMenuTimer(wxTimerEvent &event) {
1150 m_FinishRouteOnKillFocus =
false;
1151 CallPopupMenu(m_menuPos.x, m_menuPos.y);
1152 m_FinishRouteOnKillFocus =
true;
1155void ChartCanvas::ApplyCanvasConfig(
canvasConfig *pcc) {
1160 m_restore_dbindex = pcc->DBindex;
1162 if (pcc->GroupID < 0) pcc->GroupID = 0;
1167 m_groupIndex = pcc->GroupID;
1169 if (pcc->
bQuilt != GetQuiltMode()) ToggleCanvasQuiltMode();
1181 SetbEnableBasemapTile((pcc->bEnableBasemapTile));
1185 m_encDisplayCategory = pcc->nENCDisplayCategory;
1186 m_encShowDepth = pcc->bShowENCDepths;
1187 m_encShowLightDesc = pcc->bShowENCLightDescriptions;
1188 m_encShowBuoyLabels = pcc->bShowENCBuoyLabels;
1189 m_encShowLights = pcc->bShowENCLights;
1190 m_bShowVisibleSectors = pcc->bShowENCVisibleSectorLights;
1191 m_encShowAnchor = pcc->bShowENCAnchorInfo;
1192 m_encShowDataQual = pcc->bShowENCDataQuality;
1196 m_upMode = NORTH_UP_MODE;
1198 m_upMode = COURSE_UP_MODE;
1200 m_upMode = HEAD_UP_MODE;
1204 m_singleChart = NULL;
1207void ChartCanvas::ApplyGlobalSettings() {
1210 m_Compass->Show(m_bShowCompassWin && g_bShowCompassWin);
1211 if (m_bShowCompassWin && g_bShowCompassWin) m_Compass->UpdateStatus();
1213 if (m_notification_button) m_notification_button->UpdateStatus();
1216void ChartCanvas::CheckGroupValid(
bool showMessage,
bool switchGroup0) {
1217 bool groupOK = CheckGroup(m_groupIndex);
1220 SetGroupIndex(m_groupIndex,
true);
1224void ChartCanvas::SetShowGPS(
bool bshow) {
1225 if (m_bShowGPS != bshow) {
1228 m_Compass->SetScaleFactor(g_compass_scalefactor);
1229 m_Compass->Show(m_bShowCompassWin && g_bShowCompassWin);
1234void ChartCanvas::SetShowGPSCompassWindow(
bool bshow) {
1235 m_bShowCompassWin = bshow;
1237 m_Compass->Show(m_bShowCompassWin && g_bShowCompassWin);
1238 if (m_bShowCompassWin && g_bShowCompassWin) m_Compass->UpdateStatus();
1242int ChartCanvas::GetPianoHeight() {
1244 if (g_bShowChartBar && GetPiano()) height = m_Piano->GetHeight();
1249void ChartCanvas::ConfigureChartBar() {
1252 m_Piano->SetVizIcon(
new wxBitmap(style->GetIcon(
"viz")));
1253 m_Piano->SetInVizIcon(
new wxBitmap(style->GetIcon(
"redX")));
1255 if (GetQuiltMode()) {
1256 m_Piano->SetRoundedRectangles(
true);
1258 m_Piano->SetTMercIcon(
new wxBitmap(style->GetIcon(
"tmercprj")));
1259 m_Piano->SetPolyIcon(
new wxBitmap(style->GetIcon(
"polyprj")));
1260 m_Piano->SetSkewIcon(
new wxBitmap(style->GetIcon(
"skewprj")));
1263void ChartCanvas::ShowTides(
bool bShow) {
1264 top_frame::Get()->LoadHarmonics();
1267 SetbShowTide(bShow);
1269 top_frame::Get()->SetMenubarItemState(ID_MENU_SHOW_TIDES, bShow);
1271 wxLogMessage(
"Chart1::Event...TCMgr Not Available");
1272 SetbShowTide(
false);
1273 top_frame::Get()->SetMenubarItemState(ID_MENU_SHOW_TIDES,
false);
1276 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
1277 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
1288void ChartCanvas::ShowCurrents(
bool bShow) {
1289 top_frame::Get()->LoadHarmonics();
1292 SetbShowCurrent(bShow);
1293 top_frame::Get()->SetMenubarItemState(ID_MENU_SHOW_CURRENTS, bShow);
1295 wxLogMessage(
"Chart1::Event...TCMgr Not Available");
1296 SetbShowCurrent(
false);
1297 top_frame::Get()->SetMenubarItemState(ID_MENU_SHOW_CURRENTS,
false);
1300 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
1301 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
1318void ChartCanvas::canvasRefreshGroupIndex() { SetGroupIndex(m_groupIndex); }
1320void ChartCanvas::SetGroupIndex(
int index,
bool autoSwitch) {
1323 int new_index = index;
1326 bool bgroup_override =
false;
1327 int old_group_index = new_index;
1329 if (!CheckGroup(new_index)) {
1331 bgroup_override =
true;
1334 if (!autoSwitch && (index <= (
int)
g_pGroupArray->GetCount()))
1338 int current_chart_native_scale = GetCanvasChartNativeScale();
1341 m_groupIndex = new_index;
1347 if (m_muiBar) m_muiBar->SetCanvasENCAvailable(m_bENCGroup);
1351 ChartData->CheckExclusiveTileGroup(m_canvasIndex);
1355 g_sticky_chart = -1;
1359 UpdateCanvasOnGroupChange();
1362 if (GetQuiltMode()) dbi_now = GetQuiltReferenceChartIndex();
1364 int dbi_hint = FindClosestCanvasChartdbIndex(current_chart_native_scale);
1367 if ((dbi_now != dbi_hint) || !GetQuiltMode()) {
1368 double best_scale = GetBestStartScale(dbi_hint, vp);
1372 if (GetQuiltMode()) dbi_hint = GetQuiltReferenceChartIndex();
1376 canvasChartsRefresh(dbi_hint);
1378 UpdateCanvasControlBar();
1380 if (!autoSwitch && bgroup_override) {
1382 wxString msg(_(
"Group \""));
1385 msg += pGroup->m_group_name;
1387 msg += _(
"\" is empty.");
1389 OCPNMessageBox(
this, msg, _(
"OpenCPN Group Notice"), wxICON_INFORMATION, 2);
1396 if (bgroup_override) {
1397 wxString msg(_(
"Group \""));
1400 msg += pGroup->m_group_name;
1402 msg += _(
"\" is empty, switching to \"All Active Charts\" group.");
1404 OCPNMessageBox(
this, msg, _(
"OpenCPN Group Notice"), wxOK, 5);
1408bool ChartCanvas::CheckGroup(
int igroup) {
1411 if (igroup == 0)
return true;
1418 if (pGroup->m_element_array.empty())
1422 for (
const auto &elem : pGroup->m_element_array) {
1423 for (
unsigned int ic = 0;
1424 ic < (
unsigned int)
ChartData->GetChartTableEntries(); ic++) {
1425 auto &cte =
ChartData->GetChartTableEntry(ic);
1426 wxString chart_full_path(cte.GetpFullPath(), wxConvUTF8);
1428 if (chart_full_path.StartsWith(elem.m_element_name))
return true;
1433 for (
const auto &elem : pGroup->m_element_array) {
1434 const wxString &element_root = elem.m_element_name;
1435 wxString test_string =
"GSHH";
1436 if (element_root.Upper().Contains(test_string))
return true;
1442void ChartCanvas::canvasChartsRefresh(
int dbi_hint) {
1445 AbstractPlatform::ShowBusySpinner();
1449 SetQuiltRefChart(-1);
1451 m_singleChart = NULL;
1457 if (!m_pCurrentStack) {
1459 ChartData->BuildChartStack(m_pCurrentStack, m_vLat, m_vLon, m_groupIndex);
1462 if (-1 != dbi_hint) {
1463 if (GetQuiltMode()) {
1464 GetpCurrentStack()->SetCurrentEntryFromdbIndex(dbi_hint);
1465 SetQuiltRefChart(dbi_hint);
1469 pTentative_Chart =
ChartData->OpenChartFromDB(dbi_hint, FULL_INIT);
1471 if (pTentative_Chart) {
1474 if (m_singleChart) m_singleChart->Deactivate();
1476 m_singleChart = pTentative_Chart;
1477 m_singleChart->Activate();
1479 GetpCurrentStack()->CurrentStackEntry =
ChartData->GetStackEntry(
1480 GetpCurrentStack(), m_singleChart->GetFullPath());
1488 GetpCurrentStack()->CurrentStackEntry = GetpCurrentStack()->nEntry - 1;
1489 int selected_index = GetpCurrentStack()->GetCurrentEntrydbIndex();
1490 SetQuiltRefChart(selected_index);
1494 SetupCanvasQuiltMode();
1495 if (!GetQuiltMode() && m_singleChart == 0) {
1497 if (NULL == pDummyChart) pDummyChart =
new ChartDummy;
1498 m_singleChart = pDummyChart;
1504 UpdateCanvasControlBar();
1505 UpdateGPSCompassStatusBox(
true);
1507 SetCursor(wxCURSOR_ARROW);
1509 AbstractPlatform::HideBusySpinner();
1512bool ChartCanvas::DoCanvasUpdate() {
1514 double vpLat, vpLon;
1515 bool blong_jump =
false;
1516 meters_to_shift = 0;
1519 bool bNewChart =
false;
1520 bool bNewView =
false;
1521 bool bCanvasChartAutoOpen =
true;
1523 bool bNewPiano =
false;
1524 bool bOpenSpecified;
1530 if (!GetVP().IsValid())
return false;
1531 if (bDBUpdateInProgress)
return false;
1536 if (m_chart_drag_inertia_active)
return false;
1537 if (m_animationActive)
return false;
1563 double dx = m_OSoffsetx;
1564 double dy = m_OSoffsety;
1568 if (GetUpMode() == NORTH_UP_MODE) {
1569 fromSM(d_east, d_north,
gLat,
gLon, &vpLat, &vpLon);
1571 double offset_angle = atan2(d_north, d_east);
1572 double offset_distance = sqrt((d_north * d_north) + (d_east * d_east));
1573 double chart_angle = GetVPRotation();
1574 double target_angle = chart_angle + offset_angle;
1575 double d_east_mod = offset_distance * cos(target_angle);
1576 double d_north_mod = offset_distance * sin(target_angle);
1577 fromSM(d_east_mod, d_north_mod,
gLat,
gLon, &vpLat, &vpLon);
1581 if (m_bLookAhead &&
bGPSValid && !m_MouseDragging) {
1582 double cog_to_use =
gCog;
1584 (fabs(
gCog - gCog_gt) > 20)) {
1585 cog_to_use = gCog_gt;
1588 if (!g_btenhertz) cog_to_use =
g_COGAvg;
1590 double angle = cog_to_use + (GetVPRotation() * 180. / PI);
1592 double pixel_deltay = (cos(angle * PI / 180.)) * GetCanvasHeight() / 4;
1593 double pixel_deltax = (sin(angle * PI / 180.)) * GetCanvasWidth() / 4;
1595 double pixel_delta_tent =
1596 sqrt((pixel_deltay * pixel_deltay) + (pixel_deltax * pixel_deltax));
1598 double pixel_delta = 0;
1603 if (!std::isnan(
gSog)) {
1607 pixel_delta = pixel_delta_tent;
1610 meters_to_shift = 0;
1612 if (!std::isnan(
gCog)) {
1613 meters_to_shift = cos(
gLat * PI / 180.) * pixel_delta /
GetVPScale();
1614 dir_to_shift = cog_to_use;
1615 ll_gc_ll(
gLat,
gLon, dir_to_shift, meters_to_shift / 1852., &vpLat,
1621 }
else if (m_bLookAhead && (!
bGPSValid || m_MouseDragging)) {
1635 if (GetQuiltMode()) {
1636 int current_db_index = -1;
1637 if (m_pCurrentStack)
1640 ->GetCurrentEntrydbIndex();
1648 if (m_bautofind && (-1 == GetQuiltReferenceChartIndex()) &&
1650 if (m_pCurrentStack->nEntry) {
1651 int new_dbIndex = m_pCurrentStack->GetDBIndex(m_pCurrentStack->nEntry -
1653 SelectQuiltRefdbChart(new_dbIndex,
true);
1654 m_bautofind =
false;
1658 ChartData->BuildChartStack(m_pCurrentStack, tLat, tLon, m_groupIndex);
1659 m_pCurrentStack->SetCurrentEntryFromdbIndex(current_db_index);
1664 ChartData->CheckExclusiveTileGroup(m_canvasIndex);
1670 double proposed_scale_onscreen =
1673 int initial_db_index = m_restore_dbindex;
1674 if (initial_db_index < 0) {
1675 if (m_pCurrentStack->nEntry) {
1677 m_pCurrentStack->GetDBIndex(m_pCurrentStack->nEntry - 1);
1682 if (m_pCurrentStack->nEntry) {
1683 int initial_type =
ChartData->GetDBChartType(initial_db_index);
1688 if (!IsChartQuiltableRef(initial_db_index)) {
1692 int stack_index = 0;
1694 if (stack_index >= 0) {
1695 while ((stack_index < m_pCurrentStack->nEntry - 1)) {
1696 int test_db_index = m_pCurrentStack->GetDBIndex(stack_index);
1697 if (IsChartQuiltableRef(test_db_index) &&
1699 ChartData->GetDBChartType(initial_db_index))) {
1700 initial_db_index = test_db_index;
1710 SetQuiltRefChart(initial_db_index);
1711 m_pCurrentStack->SetCurrentEntryFromdbIndex(initial_db_index);
1719 0, GetVPRotation());
1724 bool super_jump =
false;
1726 double pixlt = fabs(vpLat - m_vLat) * 1852 * 60 *
GetVPScale();
1727 double pixlg = fabs(vpLon - m_vLon) * 1852 * 60 *
GetVPScale();
1728 if (wxMax(pixlt, pixlg) > GetCanvasWidth()) super_jump =
true;
1735 pLast_Ch = m_singleChart;
1736 ChartTypeEnum new_open_type;
1737 ChartFamilyEnum new_open_family;
1739 new_open_type = pLast_Ch->GetChartType();
1740 new_open_family = pLast_Ch->GetChartFamily();
1742 new_open_type = CHART_TYPE_KAP;
1743 new_open_family = CHART_FAMILY_RASTER;
1746 bOpenSpecified = m_bFirstAuto;
1749 if (NULL == m_pCurrentStack) m_pCurrentStack =
new ChartStack;
1752 if (0 ==
ChartData->BuildChartStack(&WorkStack, tLat, tLon, g_sticky_chart,
1754 if (NULL == pDummyChart) {
1760 if (m_singleChart->GetChartType() != CHART_TYPE_DUMMY) bNewChart =
true;
1762 m_singleChart = pDummyChart;
1767 if (!GetVP().IsValid()) set_scale = 1. / 20000.;
1769 bNewView |=
SetViewPoint(tLat, tLon, set_scale, 0, GetVPRotation());
1772 if (WorkStack.nEntry && m_pCurrentStack->nEntry) {
1773 if (!
ChartData->EqualStacks(&WorkStack, m_pCurrentStack)) {
1780 ChartData->CopyStack(m_pCurrentStack, &WorkStack);
1786 if (!
ChartData->EqualStacks(&WorkStack, m_pCurrentStack)) {
1791 ChartData->CopyStack(&LastStack, m_pCurrentStack);
1794 ChartData->CopyStack(m_pCurrentStack, &WorkStack);
1799 if (NULL != m_singleChart)
1800 tEntry =
ChartData->GetStackEntry(m_pCurrentStack,
1801 m_singleChart->GetFullPath());
1804 m_pCurrentStack->CurrentStackEntry = tEntry;
1814 if (bCanvasChartAutoOpen) {
1815 bool search_direction =
1817 int start_index = 0;
1821 if ((LastStack.CurrentStackEntry == LastStack.nEntry - 1) ||
1822 (LastStack.nEntry == 0)) {
1823 search_direction =
true;
1824 start_index = m_pCurrentStack->nEntry - 1;
1828 if (bOpenSpecified) {
1829 if (m_restore_dbindex >= 0) {
1831 ChartData->OpenChartFromDB(m_restore_dbindex, FULL_INIT);
1832 std::vector<int> one_array;
1833 one_array.push_back(m_restore_dbindex);
1834 m_Piano->SetActiveKeyArray(one_array);
1835 if (m_pCurrentStack) m_pCurrentStack->b_valid =
false;
1836 m_restore_dbindex = -1;
1840 search_direction =
false;
1841 start_index = m_restore_dbindex;
1842 if ((start_index < 0) | (start_index >= m_pCurrentStack->nEntry))
1845 new_open_type = CHART_TYPE_DONTCARE;
1850 pProposed =
ChartData->OpenStackChartConditional(
1851 m_pCurrentStack, start_index, search_direction, new_open_type,
1855 if (NULL == pProposed)
1856 pProposed =
ChartData->OpenStackChartConditional(
1857 m_pCurrentStack, start_index, search_direction,
1858 CHART_TYPE_CM93COMP, CHART_FAMILY_VECTOR);
1860 if (NULL == pProposed)
1861 pProposed =
ChartData->OpenStackChartConditional(
1862 m_pCurrentStack, start_index, search_direction,
1863 CHART_TYPE_CM93COMP, CHART_FAMILY_RASTER);
1874 if (NULL == pProposed) {
1875 if (NULL == pDummyChart) {
1881 if (pLast_Ch->GetChartType() != CHART_TYPE_DUMMY) bNewChart =
true;
1883 pProposed = pDummyChart;
1887 if (m_singleChart) m_singleChart->Deactivate();
1888 m_singleChart = pProposed;
1890 if (m_singleChart) {
1891 m_singleChart->Activate();
1892 m_pCurrentStack->CurrentStackEntry =
ChartData->GetStackEntry(
1893 m_pCurrentStack, m_singleChart->GetFullPath());
1898 if (NULL != m_singleChart) {
1902 double proposed_scale_onscreen;
1905 double new_scale_ppm =
1906 m_singleChart->GetNearestPreferredScalePPM(
GetVPScale());
1914 if (bNewChart && !g_bPreserveScaleOnX && !bOpenSpecified) {
1915 proposed_scale_onscreen = m_singleChart->GetNativeScale() / 2;
1916 double equivalent_vp_scale =
1918 double new_scale_ppm =
1919 m_singleChart->GetNearestPreferredScalePPM(equivalent_vp_scale);
1924 proposed_scale_onscreen =
1925 wxMin(proposed_scale_onscreen,
1928 proposed_scale_onscreen =
1929 wxMax(proposed_scale_onscreen,
1937 m_singleChart->GetChartSkew() * PI / 180.,
1945 if ((m_bFollow) && m_singleChart)
1947 m_singleChart->GetChartSkew() * PI / 180.,
1956 m_bFirstAuto =
false;
1960 if (bNewChart && !bNewView) Refresh(
false);
1965 if (m_glcc && g_bopengl && bNewChart) GetglCanvas()->Invalidate();
1968 return bNewChart | bNewView;
1971void ChartCanvas::SelectQuiltRefdbChart(
int db_index,
bool b_autoscale) {
1972 if (m_pCurrentStack) m_pCurrentStack->SetCurrentEntryFromdbIndex(db_index);
1974 SetQuiltRefChart(db_index);
1979 double best_scale_ppm = GetBestVPScale(pc);
1983 SetQuiltRefChart(-1);
1985 SetQuiltRefChart(-1);
1988void ChartCanvas::SelectQuiltRefChart(
int selected_index) {
1989 std::vector<int> piano_chart_index_array =
1990 GetQuiltExtendedStackdbIndexArray();
1991 int current_db_index = piano_chart_index_array[selected_index];
1993 SelectQuiltRefdbChart(current_db_index);
1996double ChartCanvas::GetBestVPScale(
ChartBase *pchart) {
2000 if ((g_bPreserveScaleOnX) ||
2001 (CHART_TYPE_CM93COMP == pchart->GetChartType())) {
2007 proposed_scale_onscreen = pchart->GetNativeScale() / 2;
2008 double equivalent_vp_scale =
2010 double new_scale_ppm =
2011 pchart->GetNearestPreferredScalePPM(equivalent_vp_scale);
2018 double max_underzoom_multiplier = 2.0;
2019 if (GetVP().b_quilt) {
2020 double scale_max = m_pQuilt->GetNomScaleMin(pchart->GetNativeScale(),
2021 pchart->GetChartType(),
2022 pchart->GetChartFamily());
2023 max_underzoom_multiplier = scale_max / pchart->GetNativeScale();
2026 proposed_scale_onscreen = wxMin(
2027 proposed_scale_onscreen,
2029 max_underzoom_multiplier);
2032 proposed_scale_onscreen =
2033 wxMax(proposed_scale_onscreen,
2041void ChartCanvas::SetupCanvasQuiltMode() {
2046 m_Piano->SetNoshowIndexArray(m_quilt_noshow_index_array);
2050 m_Piano->SetVizIcon(
new wxBitmap(style->GetIcon(
"viz")));
2051 m_Piano->SetInVizIcon(
new wxBitmap(style->GetIcon(
"redX")));
2052 m_Piano->SetTMercIcon(
new wxBitmap(style->GetIcon(
"tmercprj")));
2053 m_Piano->SetSkewIcon(
new wxBitmap(style->GetIcon(
"skewprj")));
2055 m_Piano->SetRoundedRectangles(
true);
2058 int target_new_dbindex = -1;
2059 if (m_pCurrentStack) {
2060 target_new_dbindex =
2061 GetQuiltReferenceChartIndex();
2063 if (-1 != target_new_dbindex) {
2064 if (!IsChartQuiltableRef(target_new_dbindex)) {
2065 int proj =
ChartData->GetDBChartProj(target_new_dbindex);
2066 int type =
ChartData->GetDBChartType(target_new_dbindex);
2069 int stack_index = m_pCurrentStack->CurrentStackEntry;
2071 while ((stack_index < m_pCurrentStack->nEntry - 1) &&
2072 (stack_index >= 0)) {
2073 int proj_tent =
ChartData->GetDBChartProj(
2074 m_pCurrentStack->GetDBIndex(stack_index));
2075 int type_tent =
ChartData->GetDBChartType(
2076 m_pCurrentStack->GetDBIndex(stack_index));
2078 if (IsChartQuiltableRef(m_pCurrentStack->GetDBIndex(stack_index))) {
2079 if ((proj == proj_tent) && (type_tent == type)) {
2080 target_new_dbindex = m_pCurrentStack->GetDBIndex(stack_index);
2090 if (IsChartQuiltableRef(target_new_dbindex))
2091 SelectQuiltRefdbChart(target_new_dbindex,
2094 int stack_index = m_pCurrentStack->CurrentStackEntry;
2095 SelectQuiltRefdbChart(m_pCurrentStack->GetDBIndex(stack_index),
false);
2098 m_singleChart = NULL;
2101 AdjustQuiltRefChart();
2109 GetVP().SetProjectionType(PROJECTION_UNKNOWN);
2113 std::vector<int> empty_array;
2114 m_Piano->SetActiveKeyArray(empty_array);
2115 m_Piano->SetNoshowIndexArray(empty_array);
2116 m_Piano->SetEclipsedIndexArray(empty_array);
2119 m_Piano->SetVizIcon(
new wxBitmap(style->GetIcon(
"viz")));
2120 m_Piano->SetInVizIcon(
new wxBitmap(style->GetIcon(
"redX")));
2121 m_Piano->SetTMercIcon(
new wxBitmap(style->GetIcon(
"tmercprj")));
2122 m_Piano->SetSkewIcon(
new wxBitmap(style->GetIcon(
"skewprj")));
2124 m_Piano->SetRoundedRectangles(
false);
2130 if (!GetQuiltMode()) {
2135 if (m_bFollow ==
true) {
2143 if (!m_singleChart) {
2145 if (GetQuiltReferenceChartIndex() >= 0) {
2146 m_singleChart =
ChartData->OpenChartFromDB(
2147 GetQuiltReferenceChartIndex(), FULL_INIT);
2150 else if (m_restore_dbindex >= 0) {
2152 ChartData->OpenChartFromDB(m_restore_dbindex, FULL_INIT);
2158 ChartData->BuildChartStack(&TempStack, tLat, tLon, g_sticky_chart,
2166 int cur_max_scale = (int)1e8;
2168 ChartBase *pChart = GetFirstQuiltChart();
2172 ChartData->GetStackEntry(&TempStack, pChart->GetFullPath());
2174 if (pChart->GetNativeScale() < cur_max_scale) {
2175 Candidate_Chart = pChart;
2176 cur_max_scale = pChart->GetNativeScale();
2179 pChart = GetNextQuiltChart();
2182 m_singleChart = Candidate_Chart;
2186 if (NULL == m_singleChart) {
2187 m_singleChart =
ChartData->OpenStackChartConditional(
2188 &TempStack, TempStack.nEntry - 1,
true, CHART_TYPE_DONTCARE,
2189 CHART_FAMILY_DONTCARE);
2195 InvalidateAllQuiltPatchs();
2197 if (m_singleChart) {
2198 int dbi =
ChartData->FinddbIndex(m_singleChart->GetFullPath());
2199 std::vector<int> one_array;
2200 one_array.push_back(dbi);
2201 m_Piano->SetActiveKeyArray(one_array);
2204 if (m_singleChart) {
2205 GetVP().SetProjectionType(m_singleChart->GetChartProjectionType());
2210 if (m_pCurrentStack) m_pCurrentStack->b_valid =
false;
2214bool ChartCanvas::IsTempMenuBarEnabled() {
2217 wxGetOsVersion(&major);
2225double ChartCanvas::GetCanvasRangeMeters() {
2227 GetSize(&width, &height);
2228 int minDimension = wxMin(width, height);
2231 range *= cos(GetVP().clat * PI / 180.);
2235void ChartCanvas::SetCanvasRangeMeters(
double range) {
2237 GetSize(&width, &height);
2238 int minDimension = wxMin(width, height);
2240 double scale_ppm = minDimension / (range / cos(GetVP().clat * PI / 180.));
2244bool ChartCanvas::SetUserOwnship() {
2248 if (pWayPointMan && pWayPointMan->DoesIconExist(
"ownship")) {
2249 double factor_dusk = 0.5;
2250 double factor_night = 0.25;
2252 wxBitmap *pbmp = pWayPointMan->GetIconBitmap(
"ownship");
2253 m_pos_image_user_day =
new wxImage;
2254 *m_pos_image_user_day = pbmp->ConvertToImage();
2255 if (!m_pos_image_user_day->HasAlpha()) m_pos_image_user_day->InitAlpha();
2257 int gimg_width = m_pos_image_user_day->GetWidth();
2258 int gimg_height = m_pos_image_user_day->GetHeight();
2261 m_pos_image_user_dusk =
new wxImage;
2262 m_pos_image_user_night =
new wxImage;
2264 *m_pos_image_user_dusk = m_pos_image_user_day->Copy();
2265 *m_pos_image_user_night = m_pos_image_user_day->Copy();
2267 for (
int iy = 0; iy < gimg_height; iy++) {
2268 for (
int ix = 0; ix < gimg_width; ix++) {
2269 if (!m_pos_image_user_day->IsTransparent(ix, iy)) {
2270 wxImage::RGBValue rgb(m_pos_image_user_day->GetRed(ix, iy),
2271 m_pos_image_user_day->GetGreen(ix, iy),
2272 m_pos_image_user_day->GetBlue(ix, iy));
2273 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
2274 hsv.value = hsv.value * factor_dusk;
2275 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
2276 m_pos_image_user_dusk->SetRGB(ix, iy, nrgb.red, nrgb.green,
2279 hsv = wxImage::RGBtoHSV(rgb);
2280 hsv.value = hsv.value * factor_night;
2281 nrgb = wxImage::HSVtoRGB(hsv);
2282 m_pos_image_user_night->SetRGB(ix, iy, nrgb.red, nrgb.green,
2289 m_pos_image_user_grey_day =
new wxImage;
2290 *m_pos_image_user_grey_day = m_pos_image_user_day->ConvertToGreyscale();
2292 m_pos_image_user_grey_dusk =
new wxImage;
2293 m_pos_image_user_grey_night =
new wxImage;
2295 *m_pos_image_user_grey_dusk = m_pos_image_user_grey_day->Copy();
2296 *m_pos_image_user_grey_night = m_pos_image_user_grey_day->Copy();
2298 for (
int iy = 0; iy < gimg_height; iy++) {
2299 for (
int ix = 0; ix < gimg_width; ix++) {
2300 if (!m_pos_image_user_grey_day->IsTransparent(ix, iy)) {
2301 wxImage::RGBValue rgb(m_pos_image_user_grey_day->GetRed(ix, iy),
2302 m_pos_image_user_grey_day->GetGreen(ix, iy),
2303 m_pos_image_user_grey_day->GetBlue(ix, iy));
2304 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
2305 hsv.value = hsv.value * factor_dusk;
2306 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
2307 m_pos_image_user_grey_dusk->SetRGB(ix, iy, nrgb.red, nrgb.green,
2310 hsv = wxImage::RGBtoHSV(rgb);
2311 hsv.value = hsv.value * factor_night;
2312 nrgb = wxImage::HSVtoRGB(hsv);
2313 m_pos_image_user_grey_night->SetRGB(ix, iy, nrgb.red, nrgb.green,
2320 m_pos_image_user_yellow_day =
new wxImage;
2321 m_pos_image_user_yellow_dusk =
new wxImage;
2322 m_pos_image_user_yellow_night =
new wxImage;
2324 *m_pos_image_user_yellow_day = m_pos_image_user_grey_day->Copy();
2325 *m_pos_image_user_yellow_dusk = m_pos_image_user_grey_day->Copy();
2326 *m_pos_image_user_yellow_night = m_pos_image_user_grey_day->Copy();
2328 for (
int iy = 0; iy < gimg_height; iy++) {
2329 for (
int ix = 0; ix < gimg_width; ix++) {
2330 if (!m_pos_image_user_grey_day->IsTransparent(ix, iy)) {
2331 wxImage::RGBValue rgb(m_pos_image_user_grey_day->GetRed(ix, iy),
2332 m_pos_image_user_grey_day->GetGreen(ix, iy),
2333 m_pos_image_user_grey_day->GetBlue(ix, iy));
2337 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
2338 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
2339 m_pos_image_user_yellow_day->SetRGB(ix, iy, nrgb.red, nrgb.green, 0);
2341 hsv = wxImage::RGBtoHSV(rgb);
2342 hsv.value = hsv.value * factor_dusk;
2343 nrgb = wxImage::HSVtoRGB(hsv);
2344 m_pos_image_user_yellow_dusk->SetRGB(ix, iy, nrgb.red, nrgb.green, 0);
2346 hsv = wxImage::RGBtoHSV(rgb);
2347 hsv.value = hsv.value * factor_night;
2348 nrgb = wxImage::HSVtoRGB(hsv);
2349 m_pos_image_user_yellow_night->SetRGB(ix, iy, nrgb.red, nrgb.green,
2361 m_display_size_mm = size;
2368 double horizontal = sd.x;
2372 m_pix_per_mm = (horizontal) / ((
double)m_display_size_mm);
2373 m_canvas_scale_factor = (horizontal) / (m_display_size_mm / 1000.);
2377 ps52plib->SetPPMM(m_pix_per_mm);
2382 "Metrics: m_display_size_mm: %g g_Platform->getDisplaySize(): "
2384 m_display_size_mm, sd.x, sd.y);
2390 msg.Printf(
"monitor size: %d %d", ssx, ssy);
2393 m_focus_indicator_pix = wxRound(1 *
GetPixPerMM());
2396void ChartCanvas::OnEvtCompressProgress( OCPN_CompressProgressEvent & event )
2398 wxString msg(event.m_string.c_str(), wxConvUTF8);
2400 if(pprog_threads > 0 && compress_msg_array.GetCount() > (
unsigned int)pprog_threads) {
2401 compress_msg_array.RemoveAt(pprog_threads, compress_msg_array.GetCount() - pprog_threads);
2404 if(compress_msg_array.GetCount() > (
unsigned int)event.thread )
2406 compress_msg_array.RemoveAt(event.thread);
2407 compress_msg_array.Insert( msg, event.thread);
2410 compress_msg_array.Add(msg);
2413 wxString combined_msg;
2414 for(
unsigned int i=0 ; i < compress_msg_array.GetCount() ; i++) {
2415 combined_msg += compress_msg_array[i];
2416 combined_msg +=
"\n";
2420 pprog->Update(pprog_count, combined_msg, &skip );
2421 pprog->SetSize(pprog_size);
2426void ChartCanvas::InvalidateGL() {
2427 if (!m_glcc)
return;
2429 if (g_bopengl) m_glcc->Invalidate();
2431 if (m_Compass) m_Compass->UpdateStatus(
true);
2434int ChartCanvas::GetCanvasChartNativeScale() {
2436 if (!VPoint.b_quilt) {
2437 if (m_singleChart) ret = m_singleChart->GetNativeScale();
2439 ret = (int)m_pQuilt->GetRefNativeScale();
2444ChartBase *ChartCanvas::GetChartAtCursor() {
2446 if (m_singleChart && (m_singleChart->GetChartFamily() == CHART_FAMILY_VECTOR))
2447 target_chart = m_singleChart;
2448 else if (VPoint.b_quilt)
2449 target_chart = m_pQuilt->GetChartAtPix(VPoint, wxPoint(mouse_x, mouse_y));
2451 target_chart = NULL;
2452 return target_chart;
2455ChartBase *ChartCanvas::GetOverlayChartAtCursor() {
2459 m_pQuilt->GetOverlayChartAtPix(VPoint, wxPoint(mouse_x, mouse_y));
2461 target_chart = NULL;
2462 return target_chart;
2465int ChartCanvas::FindClosestCanvasChartdbIndex(
int scale) {
2466 int new_dbIndex = -1;
2467 if (!VPoint.b_quilt) {
2468 if (m_pCurrentStack) {
2469 for (
int i = 0; i < m_pCurrentStack->nEntry; i++) {
2470 int sc =
ChartData->GetStackChartScale(m_pCurrentStack, i, NULL, 0);
2472 new_dbIndex = m_pCurrentStack->GetDBIndex(i);
2483 unsigned int im = m_pQuilt->GetExtendedStackIndexArray().size();
2486 for (
unsigned int is = 0; is < im; is++) {
2488 m_pQuilt->GetExtendedStackIndexArray()[is]);
2489 if ((m.Scale_ge(
scale)) &&
2490 (m_pQuilt->GetRefFamily() == m.GetChartFamily())) {
2491 new_dbIndex = m_pQuilt->GetExtendedStackIndexArray()[is];
2498 if (new_dbIndex < 0) {
2499 for (
unsigned int is = 0; is < im; is++) {
2501 m_pQuilt->GetExtendedStackIndexArray()[is]);
2502 if (m.Scale_ge(
scale)) {
2503 new_dbIndex = m_pQuilt->GetExtendedStackIndexArray()[is];
2514void ChartCanvas::EnablePaint(
bool b_enable) {
2515 m_b_paint_enable = b_enable;
2517 if (m_glcc) m_glcc->EnablePaint(b_enable);
2521bool ChartCanvas::IsQuiltDelta() {
return m_pQuilt->IsQuiltDelta(VPoint); }
2523void ChartCanvas::UnlockQuilt() { m_pQuilt->UnlockQuilt(); }
2525std::vector<int> ChartCanvas::GetQuiltIndexArray() {
2526 return m_pQuilt->GetQuiltIndexArray();
2530void ChartCanvas::SetQuiltMode(
bool b_quilt) {
2531 VPoint.b_quilt = b_quilt;
2532 VPoint.b_FullScreenQuilt = g_bFullScreenQuilt;
2535bool ChartCanvas::GetQuiltMode() {
return VPoint.b_quilt; }
2537int ChartCanvas::GetQuiltReferenceChartIndex() {
2538 return m_pQuilt->GetRefChartdbIndex();
2541void ChartCanvas::InvalidateAllQuiltPatchs() {
2542 m_pQuilt->InvalidateAllQuiltPatchs();
2545ChartBase *ChartCanvas::GetLargestScaleQuiltChart() {
2546 return m_pQuilt->GetLargestScaleChart();
2549ChartBase *ChartCanvas::GetFirstQuiltChart() {
2550 return m_pQuilt->GetFirstChart();
2553ChartBase *ChartCanvas::GetNextQuiltChart() {
return m_pQuilt->GetNextChart(); }
2555int ChartCanvas::GetQuiltChartCount() {
return m_pQuilt->GetnCharts(); }
2557void ChartCanvas::SetQuiltChartHiLiteIndex(
int dbIndex) {
2558 m_pQuilt->SetHiliteIndex(dbIndex);
2561void ChartCanvas::SetQuiltChartHiLiteIndexArray(std::vector<int> hilite_array) {
2562 m_pQuilt->SetHiliteIndexArray(hilite_array);
2565void ChartCanvas::ClearQuiltChartHiLiteIndexArray() {
2566 m_pQuilt->ClearHiliteIndexArray();
2569std::vector<int> ChartCanvas::GetQuiltCandidatedbIndexArray(
bool flag1,
2571 return m_pQuilt->GetCandidatedbIndexArray(flag1, flag2);
2574int ChartCanvas::GetQuiltRefChartdbIndex() {
2575 return m_pQuilt->GetRefChartdbIndex();
2578std::vector<int> &ChartCanvas::GetQuiltExtendedStackdbIndexArray() {
2579 return m_pQuilt->GetExtendedStackIndexArray();
2582std::vector<int> &ChartCanvas::GetQuiltFullScreendbIndexArray() {
2583 return m_pQuilt->GetFullscreenIndexArray();
2586std::vector<int> ChartCanvas::GetQuiltEclipsedStackdbIndexArray() {
2587 return m_pQuilt->GetEclipsedStackIndexArray();
2590void ChartCanvas::InvalidateQuilt() {
return m_pQuilt->Invalidate(); }
2592double ChartCanvas::GetQuiltMaxErrorFactor() {
2593 return m_pQuilt->GetMaxErrorFactor();
2596bool ChartCanvas::IsChartQuiltableRef(
int db_index) {
2597 return m_pQuilt->IsChartQuiltableRef(db_index);
2601 double chartMaxScale =
2603 return (chartMaxScale * g_ChartNotRenderScaleFactor > vp.
chart_scale);
2606void ChartCanvas::StartMeasureRoute() {
2607 if (!m_routeState) {
2608 if (m_bMeasure_Active) {
2610 m_pMeasureRoute = NULL;
2613 m_bMeasure_Active =
true;
2614 m_nMeasureState = 1;
2615 m_bDrawingRoute =
false;
2617 SetCursor(*pCursorPencil);
2622void ChartCanvas::CancelMeasureRoute() {
2623 m_bMeasure_Active =
false;
2624 m_nMeasureState = 0;
2625 m_bDrawingRoute =
false;
2627 if (m_pMeasureRoute &&
g_pRouteMan->IsRouteInList(m_pMeasureRoute)) {
2629 }
else if (m_pMeasureRoute) {
2630 wxLogMessage(
"CancelMeasureRoute: stale route %p", m_pMeasureRoute);
2633 m_pMeasureRoute = NULL;
2635 SetCursor(*pCursorArrow);
2638ViewPort &ChartCanvas::GetVP() {
return VPoint; }
2640void ChartCanvas::SetVP(
ViewPort &vp) {
2651void ChartCanvas::TriggerDeferredFocus() {
2654 m_deferredFocusTimer.Start(20,
true);
2656#if defined(__WXGTK__) || defined(__WXOSX__)
2657 top_frame::Get()->Raise();
2667void ChartCanvas::OnDeferredFocusTimerEvent(wxTimerEvent &event) {
2672void ChartCanvas::OnKeyChar(wxKeyEvent &event) {
2673 if (SendKeyEventToPlugins(event))
2677 int key_char =
event.GetKeyCode();
2680 HotkeysDlg(wxWindow::FindWindowByName(
"MainWindow")).ShowModal();
2686 ZoomCanvas(1.0 / g_plus_minus_zoom_factor,
false);
2691 if (g_benable_rotate) {
2712void ChartCanvas::OnKeyDown(wxKeyEvent &event) {
2713 if (SendKeyEventToPlugins(event))
2717 bool b_handled =
false;
2719 m_modkeys =
event.GetModifiers();
2721 int panspeed = m_modkeys == wxMOD_ALT ? 1 : 100;
2723#ifdef OCPN_ALT_MENUBAR
2729 if (IsTempMenuBarEnabled() && event.AltDown() && !g_bShowMenuBar) {
2731 if (event.GetKeyCode() >=
'A' && event.GetKeyCode() <=
'Z') {
2732 if (!g_bTempShowMenuBar) {
2733 g_bTempShowMenuBar =
true;
2734 top_frame::Get()->ApplyGlobalSettings(
false);
2736 m_bMayToggleMenuBar =
false;
2742 if (event.GetKeyCode() != WXK_ALT) {
2743 m_bMayToggleMenuBar =
false;
2750 switch (event.GetKeyCode()) {
2757 event.GetPosition(&x, &y);
2758 m_FinishRouteOnKillFocus =
false;
2759 CallPopupMenu(x, y);
2760 m_FinishRouteOnKillFocus =
true;
2764 m_modkeys |= wxMOD_ALT;
2768 m_modkeys |= wxMOD_CONTROL;
2773 case WXK_RAW_CONTROL:
2774 m_modkeys |= wxMOD_RAW_CONTROL;
2779 if (m_modkeys == wxMOD_CONTROL)
2780 top_frame::Get()->DoStackDown(
this);
2782 StartTimedMovement();
2792 StartTimedMovement();
2800 if (m_modkeys == wxMOD_CONTROL)
2801 top_frame::Get()->DoStackUp(
this);
2803 StartTimedMovement();
2813 StartTimedMovement();
2821 if (event.ShiftDown()) {
2823 ChartFamilyEnum target_family = CHART_FAMILY_RASTER;
2825 std::shared_ptr<HostApi> host_api;
2827 auto api_121 = std::dynamic_pointer_cast<HostApi121>(host_api);
2830 api_121->SelectChartFamily(m_canvasIndex,
2837 if (event.ShiftDown()) {
2839 ChartFamilyEnum target_family = CHART_FAMILY_VECTOR;
2841 std::shared_ptr<HostApi> host_api;
2843 auto api_121 = std::dynamic_pointer_cast<HostApi121>(host_api);
2846 api_121->SelectChartFamily(m_canvasIndex,
2849 SetShowENCText(!GetShowENCText());
2856 if (!m_bMeasure_Active) {
2857 if (event.ShiftDown())
2858 m_bMeasure_DistCircle =
true;
2860 m_bMeasure_DistCircle =
false;
2862 StartMeasureRoute();
2864 CancelMeasureRoute();
2866 SetCursor(*pCursorArrow);
2876 top_frame::Get()->ToggleColorScheme();
2877 top_frame::Get()->Raise();
2878 TriggerDeferredFocus();
2882 int mod = m_modkeys & wxMOD_SHIFT;
2883 if (mod != m_brightmod) {
2885 m_bbrightdir = !m_bbrightdir;
2888 if (!m_bbrightdir) {
2889 g_nbrightness -= 10;
2890 if (g_nbrightness <= MIN_BRIGHT) {
2891 g_nbrightness = MIN_BRIGHT;
2892 m_bbrightdir =
true;
2895 g_nbrightness += 10;
2896 if (g_nbrightness >= MAX_BRIGHT) {
2897 g_nbrightness = MAX_BRIGHT;
2898 m_bbrightdir =
false;
2902 SetScreenBrightness(g_nbrightness);
2903 ShowBrightnessLevelTimedPopup(g_nbrightness / 10, 1, 10);
2906 top_frame::Get()->Raise();
2912 top_frame::Get()->DoStackDown(
this);
2916 top_frame::Get()->DoStackUp(
this);
2921 ToggleCanvasQuiltMode();
2927 top_frame::Get()->ToggleFullScreen();
2932 if (m_modkeys == wxMOD_ALT) {
2935 ToggleChartOutlines();
2941 top_frame::Get()->ActivateMOB();
2945 case WXK_NUMPAD_ADD:
2950 case WXK_NUMPAD_SUBTRACT:
2951 case WXK_PAGEDOWN: {
2952 ZoomCanvas(1.0 / g_plus_minus_zoom_factor,
false);
2957 if (m_bMeasure_Active) {
2958 if (m_nMeasureState > 2) {
2959 m_pMeasureRoute->DeletePoint(m_pMeasureRoute->GetLastPoint());
2961 m_pMeasureRoute->GetnPoints();
2963 top_frame::Get()->RefreshAllCanvas();
2965 CancelMeasureRoute();
2966 StartMeasureRoute();
2974 if (event.GetKeyCode() < 128)
2976 int key_char =
event.GetKeyCode();
2980 if (!g_b_assume_azerty) {
2982 if (g_benable_rotate) {
3014 ZoomCanvas(1.0 / g_plus_minus_zoom_factor,
false);
3021 if (key_char ==
'F' && m_modkeys & wxMOD_CONTROL &&
3022 m_modkeys & wxMOD_RAW_CONTROL) {
3023 top_frame::Get()->ToggleFullScreen();
3028 if (event.ControlDown()) key_char -= 64;
3030 if (key_char >=
'0' && key_char <=
'9')
3031 SetGroupIndex(key_char -
'0');
3036 SetShowENCAnchor(!GetShowENCAnchor());
3042 top_frame::Get()->ToggleColorScheme();
3047 event.GetPosition(&x, &y);
3048 ChartTypeEnum ChartType = CHART_TYPE_UNKNOWN;
3049 ChartFamilyEnum ChartFam = CHART_FAMILY_UNKNOWN;
3052 if (VPoint.b_quilt) {
3054 if (m_pQuilt->GetChartAtPix(
3059 ChartType = m_pQuilt->GetChartAtPix(VPoint, wxPoint(x, y))
3061 ChartFam = m_pQuilt->GetChartAtPix(VPoint, wxPoint(x, y))
3066 if (m_singleChart) {
3067 ChartType = m_singleChart->GetChartType();
3068 ChartFam = m_singleChart->GetChartFamily();
3072 if ((ChartType != CHART_TYPE_UNKNOWN) ||
3073 (ChartFam != CHART_FAMILY_UNKNOWN)) {
3075 this, -1, ChartType, ChartFam,
3076 wxPoint(g_detailslider_dialog_x, g_detailslider_dialog_y),
3077 wxDefaultSize, wxSIMPLE_BORDER,
"");
3090 m_nmea_log->Raise();
3094 SetShowENCLights(!GetShowENCLights());
3100 if (event.ShiftDown())
3101 m_bMeasure_DistCircle =
true;
3103 m_bMeasure_DistCircle =
false;
3105 StartMeasureRoute();
3109 if (g_bInlandEcdis && ps52plib) {
3110 SetENCDisplayCategory((_DisCat)STANDARD);
3115 ToggleChartOutlines();
3119 ToggleCanvasQuiltMode();
3123 top_frame::Get()->ToggleTestPause();
3126 g_bNavAidRadarRingsShown = !g_bNavAidRadarRingsShown;
3127 if (g_bNavAidRadarRingsShown && g_iNavAidRadarRingsNumberVisible == 0)
3128 g_iNavAidRadarRingsNumberVisible = 1;
3129 else if (!g_bNavAidRadarRingsShown &&
3130 g_iNavAidRadarRingsNumberVisible == 1)
3131 g_iNavAidRadarRingsNumberVisible = 0;
3134 SetShowENCDepth(!m_encShowDepth);
3139 SetShowENCText(!GetShowENCText());
3144 SetShowENCDataQual(!GetShowENCDataQual());
3149 m_bShowNavobjects = !m_bShowNavobjects;
3164 if (g_bShowMenuBar ==
false) top_frame::Get()->ToggleChartBar(
this);
3169 if (event.ControlDown()) top_frame::Get()->DropMarker(
false);
3176 int indexActive = r->GetIndexOf(r->m_pRouteActivePoint);
3177 if ((indexActive + 1) <= r->GetnPoints()) {
3188 if (!g_bShowMenuBar) top_frame::Get()->DropMarker(
true);
3194 if (g_bSpaceDropMark) top_frame::Get()->DropMarker(
true);
3200 if (m_modkeys == wxMOD_CONTROL) top_frame::Get()->ActivateMOB();
3207 top_frame::Get()->DoSettings();
3211 parent_frame->Close();
3227 if (undo->AnythingToRedo()) {
3228 undo->RedoNextAction();
3235 if (event.ShiftDown()) {
3236 if (undo->AnythingToRedo()) {
3237 undo->RedoNextAction();
3242 if (undo->AnythingToUndo()) {
3243 undo->UndoLastAction();
3252 if (m_bMeasure_Active) {
3253 CancelMeasureRoute();
3255 SetCursor(*pCursorArrow);
3258 top_frame::Get()->RefreshAllCanvas();
3272 switch (gamma_state) {
3292 SetScreenBrightness(g_nbrightness);
3297 if (event.ControlDown()) {
3298 m_bShowCompassWin = !m_bShowCompassWin;
3299 SetShowGPSCompassWindow(m_bShowCompassWin);
3316void ChartCanvas::OnKeyUp(wxKeyEvent &event) {
3317 if (SendKeyEventToPlugins(event))
3321 switch (event.GetKeyCode()) {
3323 top_frame::Get()->SwitchKBFocus(
this);
3329 if (!m_pany) m_panspeed = 0;
3335 if (!m_panx) m_panspeed = 0;
3338 case WXK_NUMPAD_ADD:
3339 case WXK_NUMPAD_SUBTRACT:
3348 m_modkeys &= ~wxMOD_ALT;
3349#ifdef OCPN_ALT_MENUBAR
3354 if (IsTempMenuBarEnabled() && !g_bShowMenuBar && m_bMayToggleMenuBar) {
3355 g_bTempShowMenuBar = !g_bTempShowMenuBar;
3356 top_frame::Get()->ApplyGlobalSettings(
false);
3358 m_bMayToggleMenuBar =
true;
3364 m_modkeys &= ~wxMOD_CONTROL;
3368 if (event.GetKeyCode() < 128)
3370 int key_char =
event.GetKeyCode();
3374 if (!g_b_assume_azerty) {
3389 m_rotation_speed = 0;
3407void ChartCanvas::ToggleChartOutlines() {
3408 m_bShowOutlines = !m_bShowOutlines;
3414 if (g_bopengl) InvalidateGL();
3418void ChartCanvas::ToggleLookahead() {
3419 m_bLookAhead = !m_bLookAhead;
3424void ChartCanvas::SetUpMode(
int mode) {
3427 if (mode != NORTH_UP_MODE) {
3430 if (!std::isnan(
gCog)) stuff =
gCog;
3433 auto cog_table = top_frame::Get()->GetCOGTable();
3434 for (
int i = 0; i <
g_COGAvgSec; i++) cog_table[i] = stuff;
3437 top_frame::Get()->StartCogTimer();
3439 if (!g_bskew_comp && (fabs(GetVPSkew()) > 0.0001))
3440 SetVPRotation(GetVPSkew());
3445 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
3446 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
3448 UpdateGPSCompassStatusBox(
true);
3449 top_frame::Get()->DoChartUpdate();
3452bool ChartCanvas::DoCanvasCOGSet() {
3453 if (GetUpMode() == NORTH_UP_MODE)
return false;
3455 if (g_btenhertz) cog_use =
gCog;
3457 double rotation = 0;
3458 if ((GetUpMode() == HEAD_UP_MODE) && !std::isnan(
gHdt)) {
3459 rotation = -
gHdt * PI / 180.;
3460 }
else if ((GetUpMode() == COURSE_UP_MODE) && !std::isnan(cog_use))
3461 rotation = -cog_use * PI / 180.;
3463 SetVPRotation(rotation);
3467double easeOutCubic(
double t) {
3469 return 1.0 - pow(1.0 - t, 3.0);
3472void ChartCanvas::StartChartDragInertia() {
3473 m_bChartDragging =
false;
3476 m_chart_drag_inertia_time = 750;
3477 m_chart_drag_inertia_start_time = wxGetLocalTimeMillis();
3482 n_vel = wxMin(n_vel, m_drag_vec_t.size());
3486 size_t length = m_drag_vec_t.size();
3487 for (
size_t i = 0; i < n_vel; i++) {
3488 xacc += m_drag_vec_x.at(length - 1 - i);
3489 yacc += m_drag_vec_y.at(length - 1 - i);
3490 tacc += m_drag_vec_t.at(length - 1 - i);
3493 if (tacc == 0)
return;
3495 double drag_velocity_x = xacc / tacc;
3496 double drag_velocity_y = yacc / tacc;
3502 if ((fabs(drag_velocity_x) < 200) && (fabs(drag_velocity_y) < 200))
return;
3504 m_chart_drag_velocity_x = drag_velocity_x;
3505 m_chart_drag_velocity_y = drag_velocity_y;
3507 m_chart_drag_inertia_active =
true;
3509 m_chart_drag_inertia_timer.Start(1, wxTIMER_ONE_SHOT);
3512void ChartCanvas::OnChartDragInertiaTimer(wxTimerEvent &event) {
3513 if (!m_chart_drag_inertia_active)
return;
3515 wxLongLong now = wxGetLocalTimeMillis();
3516 double elapsed = (now - m_chart_drag_inertia_start_time).ToDouble();
3517 double t = elapsed / m_chart_drag_inertia_time.ToDouble();
3518 if (t > 1.0) t = 1.0;
3519 double e = 1.0 - easeOutCubic(t);
3522 m_chart_drag_velocity_x * ((elapsed - m_last_elapsed) / 1000.) * e;
3524 m_chart_drag_velocity_y * ((elapsed - m_last_elapsed) / 1000.) * e;
3526 m_last_elapsed = elapsed;
3530 double destination_x = (GetCanvasWidth() / 2) + wxRound(dx);
3531 double destination_y = (GetCanvasHeight() / 2) + wxRound(dy);
3532 double inertia_lat, inertia_lon;
3534 double prev_clat = VPoint.
clat;
3537 if (fabs(VPoint.
clat - prev_clat) < 1e-9 && fabs(dy) > 1) {
3538 m_chart_drag_inertia_timer.Stop();
3539 m_chart_drag_inertia_active =
false;
3544 if (!IsOwnshipOnScreen()) {
3546 top_frame::Get()->SetMenubarItemState(ID_MENU_NAV_FOLLOW,
false);
3547 UpdateFollowButtonState();
3558 if ((t >= 1) || (fabs(dx) < 1) || (fabs(dy) < 1)) {
3559 m_chart_drag_inertia_timer.Stop();
3562 m_target_lat = GetVP().
clat;
3563 m_target_lon = GetVP().
clon;
3564 m_pan_drag.x = m_pan_drag.y = 0;
3565 m_panx = m_pany = 0;
3566 m_chart_drag_inertia_active =
false;
3570 int target_redraw_interval = 40;
3571 m_chart_drag_inertia_timer.Start(target_redraw_interval, wxTIMER_ONE_SHOT);
3575void ChartCanvas::StopMovement() {
3576 m_panx = m_pany = 0;
3579 m_rotation_speed = 0;
3582#if !defined(__WXGTK__) && !defined(__WXQT__)
3584 top_frame::Get()->Raise();
3593bool ChartCanvas::StartTimedMovement(
bool stoptimer) {
3595 if (stoptimer) pMovementStopTimer->Start(800, wxTIMER_ONE_SHOT);
3597 if (!pMovementTimer->IsRunning()) {
3598 pMovementTimer->Start(1, wxTIMER_ONE_SHOT);
3601 if (m_panx || m_pany || m_zoom_factor != 1 || m_rotation_speed) {
3606 m_last_movement_time = wxDateTime::UNow();
3610void ChartCanvas::StartTimedMovementVP(
double target_lat,
double target_lon,
3613 m_target_lat = target_lat;
3614 m_target_lon = target_lon;
3617 m_start_lat = GetVP().
clat;
3618 m_start_lon = GetVP().
clon;
3620 m_VPMovementTimer.Start(1,
true);
3621 m_timed_move_vp_active =
true;
3623 m_timedVP_step = nstep;
3626void ChartCanvas::DoTimedMovementVP() {
3627 if (!m_timed_move_vp_active)
return;
3628 if (m_stvpc++ > m_timedVP_step * 2) {
3635 pow(m_run_lat - m_target_lat, 2) + pow(m_run_lon - m_target_lon, 2);
3650 double new_lat = GetVP().
clat + (m_target_lat - m_start_lat) / m_timedVP_step;
3651 double new_lon = GetVP().
clon + (m_target_lon - m_start_lon) / m_timedVP_step;
3653 m_run_lat = new_lat;
3654 m_run_lon = new_lon;
3659void ChartCanvas::StopMovementVP() { m_timed_move_vp_active =
false; }
3661void ChartCanvas::MovementVPTimerEvent(wxTimerEvent &) { DoTimedMovementVP(); }
3663void ChartCanvas::StartTimedMovementTarget() {}
3665void ChartCanvas::DoTimedMovementTarget() {}
3667void ChartCanvas::StopMovementTarget() {}
3670void ChartCanvas::DoTimedMovement() {
3671 if (m_pan_drag == wxPoint(0, 0) && !m_panx && !m_pany && m_zoom_factor == 1 &&
3675 wxDateTime now = wxDateTime::UNow();
3677 if (m_last_movement_time.IsValid())
3678 dt = (now - m_last_movement_time).GetMilliseconds().ToLong();
3680 m_last_movement_time = now;
3690 if (dt == 0) dt = 1;
3693 if (m_mustmove < 0) m_mustmove = 0;
3696 if (m_pan_drag.x || m_pan_drag.y) {
3698 m_pan_drag.x = m_pan_drag.y = 0;
3701 if (m_panx || m_pany) {
3702 const double slowpan = .1, maxpan = 2;
3703 if (m_modkeys == wxMOD_ALT)
3704 m_panspeed = slowpan;
3706 m_panspeed += (double)dt / 500;
3707 m_panspeed = wxMin(maxpan, m_panspeed);
3709 PanCanvas(m_panspeed * m_panx * dt, m_panspeed * m_pany * dt);
3712 if (m_zoom_factor != 1) {
3713 double alpha = 400, beta = 1.5;
3714 double zoom_factor = (exp(dt / alpha) - 1) / beta + 1;
3716 if (m_modkeys == wxMOD_ALT) zoom_factor = pow(zoom_factor, .15);
3718 if (m_zoom_factor < 1) zoom_factor = 1 / zoom_factor;
3723 if (zoom_factor > 1) {
3724 if (VPoint.
chart_scale / zoom_factor <= m_zoom_target)
3728 else if (zoom_factor < 1) {
3729 if (VPoint.
chart_scale / zoom_factor >= m_zoom_target)
3733 if ((
GetVPScale()) < (m_absolute_min_scale_ppm * 1.00001))
3738 if (fabs(zoom_factor - 1) > 1e-4) {
3739 DoZoomCanvas(zoom_factor, m_bzooming_to_cursor);
3744 if (m_wheelzoom_stop_oneshot > 0) {
3745 if (m_wheelstopwatch.Time() > m_wheelzoom_stop_oneshot) {
3746 m_wheelzoom_stop_oneshot = 0;
3751 if (zoom_factor > 1) {
3753 m_wheelzoom_stop_oneshot = 0;
3756 }
else if (zoom_factor < 1) {
3758 m_wheelzoom_stop_oneshot = 0;
3765 if (m_rotation_speed) {
3766 double speed = m_rotation_speed;
3767 if (m_modkeys == wxMOD_ALT) speed /= 10;
3768 DoRotateCanvas(VPoint.
rotation + speed * PI / 180 * dt / 1000.0);
3772void ChartCanvas::SetColorScheme(ColorScheme cs) {
3777 case GLOBAL_COLOR_SCHEME_DAY:
3778 case GLOBAL_COLOR_SCHEME_DAY_HICON:
3779 m_pos_image_red = &m_os_image_red_day;
3780 m_pos_image_grey = &m_os_image_grey_day;
3781 m_pos_image_yellow = &m_os_image_yellow_day;
3782 m_pos_image_user = m_pos_image_user_day;
3783 m_pos_image_user_grey = m_pos_image_user_grey_day;
3784 m_pos_image_user_yellow = m_pos_image_user_yellow_day;
3785 m_cTideBitmap = m_bmTideDay;
3786 m_cCurrentBitmap = m_bmCurrentDay;
3789 case GLOBAL_COLOR_SCHEME_DUSK:
3790 case GLOBAL_COLOR_SCHEME_NIGHT_HICON:
3791 m_pos_image_red = &m_os_image_red_dusk;
3792 m_pos_image_grey = &m_os_image_grey_dusk;
3793 m_pos_image_yellow = &m_os_image_yellow_dusk;
3794 m_pos_image_user = m_pos_image_user_dusk;
3795 m_pos_image_user_grey = m_pos_image_user_grey_dusk;
3796 m_pos_image_user_yellow = m_pos_image_user_yellow_dusk;
3797 m_cTideBitmap = m_bmTideDusk;
3798 m_cCurrentBitmap = m_bmCurrentDusk;
3800 case GLOBAL_COLOR_SCHEME_NIGHT:
3801 m_pos_image_red = &m_os_image_red_night;
3802 m_pos_image_grey = &m_os_image_grey_night;
3803 m_pos_image_yellow = &m_os_image_yellow_night;
3804 m_pos_image_user = m_pos_image_user_night;
3805 m_pos_image_user_grey = m_pos_image_user_grey_night;
3806 m_pos_image_user_yellow = m_pos_image_user_yellow_night;
3807 m_cTideBitmap = m_bmTideNight;
3808 m_cCurrentBitmap = m_bmCurrentNight;
3811 m_pos_image_red = &m_os_image_red_day;
3812 m_pos_image_grey = &m_os_image_grey_day;
3813 m_pos_image_yellow = &m_os_image_yellow_day;
3814 m_pos_image_user = m_pos_image_user_day;
3815 m_pos_image_user_grey = m_pos_image_user_grey_day;
3816 m_pos_image_user_yellow = m_pos_image_user_yellow_day;
3817 m_cTideBitmap = m_bmTideDay;
3818 m_cCurrentBitmap = m_bmCurrentDay;
3822 CreateDepthUnitEmbossMaps(cs);
3823 CreateOZEmbossMapData(cs);
3826 m_fog_color = wxColor(
3830 case GLOBAL_COLOR_SCHEME_DUSK:
3831 case GLOBAL_COLOR_SCHEME_NIGHT_HICON:
3834 case GLOBAL_COLOR_SCHEME_NIGHT:
3840 m_fog_color.Set(m_fog_color.Red() * dim, m_fog_color.Green() * dim,
3841 m_fog_color.Blue() * dim);
3845 if( cs == GLOBAL_COLOR_SCHEME_DUSK || cs == GLOBAL_COLOR_SCHEME_NIGHT ) {
3846 SetBackgroundColour( wxColour(0,0,0) );
3848 SetWindowStyleFlag( (GetWindowStyleFlag() & ~wxSIMPLE_BORDER) | wxNO_BORDER);
3851 SetWindowStyleFlag( (GetWindowStyleFlag() & ~wxNO_BORDER) | wxSIMPLE_BORDER);
3853 SetBackgroundColour( wxNullColour );
3860 m_Piano->SetColorScheme(cs);
3862 m_Compass->SetColorScheme(cs);
3864 if (m_muiBar) m_muiBar->SetColorScheme(cs);
3866 if (pWorldBackgroundChart) pWorldBackgroundChart->SetColorScheme(cs);
3868 if (m_NotificationsList) m_NotificationsList->SetColorScheme();
3869 if (m_notification_button) {
3870 m_notification_button->SetColorScheme(cs);
3874 if (g_bopengl && m_glcc) {
3875 m_glcc->SetColorScheme(cs);
3881 m_brepaint_piano =
true;
3888wxBitmap ChartCanvas::CreateDimBitmap(wxBitmap &Bitmap,
double factor) {
3889 wxImage img = Bitmap.ConvertToImage();
3890 int sx = img.GetWidth();
3891 int sy = img.GetHeight();
3893 wxImage new_img(img);
3895 for (
int i = 0; i < sx; i++) {
3896 for (
int j = 0; j < sy; j++) {
3897 if (!img.IsTransparent(i, j)) {
3898 new_img.SetRGB(i, j, (
unsigned char)(img.GetRed(i, j) * factor),
3899 (
unsigned char)(img.GetGreen(i, j) * factor),
3900 (
unsigned char)(img.GetBlue(i, j) * factor));
3905 wxBitmap ret = wxBitmap(new_img);
3910void ChartCanvas::ShowBrightnessLevelTimedPopup(
int brightness,
int min,
3913 40, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
3915 if (!m_pBrightPopup) {
3918 GetTextExtent(
"MAX", &x, &y, NULL, NULL, pfont);
3922 m_pBrightPopup->SetSize(x, y);
3923 m_pBrightPopup->Move(120, 120);
3926 int bmpsx = m_pBrightPopup->GetSize().x;
3927 int bmpsy = m_pBrightPopup->GetSize().y;
3929 wxBitmap bmp(bmpsx, bmpsx);
3930 wxMemoryDC mdc(bmp);
3932 mdc.SetTextForeground(GetGlobalColor(
"GREEN4"));
3933 mdc.SetBackground(wxBrush(GetGlobalColor(
"UINFD")));
3934 mdc.SetPen(wxPen(wxColour(0, 0, 0)));
3935 mdc.SetBrush(wxBrush(GetGlobalColor(
"UINFD")));
3938 mdc.DrawRectangle(0, 0, bmpsx, bmpsy);
3940 mdc.SetFont(*pfont);
3943 if (brightness == max)
3945 else if (brightness == min)
3948 val.Printf(
"%3d", brightness);
3950 mdc.DrawText(val, 0, 0);
3952 mdc.SelectObject(wxNullBitmap);
3954 m_pBrightPopup->SetBitmap(bmp);
3955 m_pBrightPopup->Show();
3956 m_pBrightPopup->Refresh();
3959void ChartCanvas::RotateTimerEvent(wxTimerEvent &event) {
3960 m_b_rot_hidef =
true;
3964void ChartCanvas::OnRolloverPopupTimerEvent(wxTimerEvent &event) {
3967 bool b_need_refresh =
false;
3969 wxSize win_size = GetSize() * m_displayScale;
3973 bool showAISRollover =
false;
3975 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
3979 int FoundAIS_MMSI = (wxIntPtr)pFind->m_pData1;
3980 auto ptarget =
g_pAIS->Get_Target_Data_From_MMSI(FoundAIS_MMSI);
3983 showAISRollover =
true;
3985 if (NULL == m_pAISRolloverWin) {
3987 m_pAISRolloverWin->IsActive(
false);
3988 b_need_refresh =
true;
3989 }
else if (m_pAISRolloverWin->IsActive() && m_AISRollover_MMSI &&
3990 m_AISRollover_MMSI != FoundAIS_MMSI) {
3996 m_RolloverPopupTimer.Start(50, wxTIMER_ONE_SHOT);
3997 m_pAISRolloverWin->IsActive(
false);
3998 m_AISRollover_MMSI = 0;
4003 m_AISRollover_MMSI = FoundAIS_MMSI;
4005 if (!m_pAISRolloverWin->IsActive()) {
4006 wxString s = ptarget->GetRolloverString();
4007 m_pAISRolloverWin->SetString(s);
4009 m_pAISRolloverWin->SetBestPosition(mouse_x, mouse_y, 16, 16,
4010 AIS_ROLLOVER, win_size);
4011 m_pAISRolloverWin->SetBitmap(AIS_ROLLOVER);
4012 m_pAISRolloverWin->IsActive(
true);
4013 b_need_refresh =
true;
4017 m_AISRollover_MMSI = 0;
4018 showAISRollover =
false;
4023 if (m_pAISRolloverWin && m_pAISRolloverWin->IsActive() && !showAISRollover) {
4024 m_pAISRolloverWin->IsActive(
false);
4025 m_AISRollover_MMSI = 0;
4026 b_need_refresh =
true;
4031 bool showRouteRollover =
false;
4033 if (NULL == m_pRolloverRouteSeg) {
4037 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
4038 SelectableItemList SelList =
pSelect->FindSelectionList(
4040 auto node = SelList.begin();
4041 while (node != SelList.end()) {
4046 if (pr && pr->IsVisible()) {
4047 m_pRolloverRouteSeg = pFindSel;
4048 showRouteRollover =
true;
4050 if (NULL == m_pRouteRolloverWin) {
4052 m_pRouteRolloverWin->IsActive(
false);
4055 if (!m_pRouteRolloverWin->IsActive()) {
4063 int ia = pr->GetIndexOf(segShow_point_a);
4064 int ib = pr->GetIndexOf(segShow_point_b);
4065 if (ia < 0 || ib < 0) {
4066 wxLogMessage(
"Ignoring stale route segment selection.");
4071 DistanceBearingMercator(
4072 segShow_point_b->m_lat, segShow_point_b->m_lon,
4073 segShow_point_a->m_lat, segShow_point_a->m_lon, &brg, &dist);
4075 if (!pr->m_bIsInLayer)
4076 s.Append(_(
"Route") +
": ");
4078 s.Append(_(
"Layer Route: "));
4080 if (pr->m_RouteNameString.IsEmpty())
4081 s.Append(_(
"(unnamed)"));
4083 s.Append(pr->m_RouteNameString);
4088 << _(
"Leg: from ") << segShow_point_a->GetName() << _(
" to ")
4089 << segShow_point_b->GetName() <<
"\n";
4092 s << wxString::Format(wxString(
"%03d%c(T) ", wxConvUTF8),
4093 (
int)floor(brg + 0.5), 0x00B0);
4096 (segShow_point_b->m_lat + segShow_point_a->m_lat) / 2;
4098 (segShow_point_b->m_lon + segShow_point_a->m_lon) / 2;
4100 top_frame::Get()->GetMag(brg, latAverage, lonAverage);
4102 s << wxString::Format(wxString(
"%03d%c(M) ", wxConvUTF8),
4103 (
int)floor(varBrg + 0.5), 0x00B0);
4111 double shiptoEndLeg = 0.;
4112 bool validActive =
false;
4113 if (pr->IsActive() && (*pr->pRoutePointList->begin())->m_bIsActive)
4116 if (segShow_point_a != *pr->pRoutePointList->begin()) {
4117 auto node = pr->pRoutePointList->begin();
4119 float dist_to_endleg = 0;
4122 for (++node; node != pr->pRoutePointList->end(); ++node) {
4129 if (prp->IsSame(segShow_point_a))
break;
4137 << _(
"From Ship To") <<
" " << segShow_point_b->GetName() <<
"\n";
4140 ->GetCurrentRngToActivePoint();
4149 if (!std::isnan(
gCog) && !std::isnan(
gSog))
4154 float ttg_sec = (shiptoEndLeg /
gSog) * 3600.;
4155 wxTimeSpan ttg_span = wxTimeSpan::Seconds((
long)ttg_sec);
4157 << wxString(ttg_sec > SECONDS_PER_DAY
4158 ? ttg_span.Format(_(
"%Dd %H:%M"))
4159 : ttg_span.Format(_(
"%H:%M")));
4160 wxDateTime dtnow, eta;
4161 eta = dtnow.SetToCurrent().Add(ttg_span);
4162 s <<
" - " << eta.Format(
"%b").Mid(0, 4)
4163 << eta.Format(
" %d %H:%M");
4167 m_pRouteRolloverWin->SetString(s);
4169 m_pRouteRolloverWin->SetBestPosition(mouse_x, mouse_y, 16, 16,
4170 LEG_ROLLOVER, win_size);
4171 m_pRouteRolloverWin->SetBitmap(LEG_ROLLOVER);
4172 m_pRouteRolloverWin->IsActive(
true);
4173 b_need_refresh =
true;
4174 showRouteRollover =
true;
4183 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
4185 m_pRolloverRouteSeg))
4186 showRouteRollover =
false;
4187 else if (m_pRouteRolloverWin && !m_pRouteRolloverWin->IsActive())
4188 showRouteRollover =
false;
4190 showRouteRollover =
true;
4194 if (m_routeState) showRouteRollover =
false;
4197 if (m_pAISRolloverWin && m_pAISRolloverWin->IsActive())
4198 showRouteRollover =
false;
4200 if (m_pRouteRolloverWin &&
4201 !showRouteRollover) {
4202 m_pRouteRolloverWin->IsActive(
false);
4203 m_pRolloverRouteSeg = NULL;
4204 m_pRouteRolloverWin->Destroy();
4205 m_pRouteRolloverWin = NULL;
4206 b_need_refresh =
true;
4207 }
else if (m_pRouteRolloverWin && showRouteRollover) {
4208 m_pRouteRolloverWin->IsActive(
true);
4209 b_need_refresh =
true;
4214 bool showTrackRollover =
false;
4216 if (NULL == m_pRolloverTrackSeg) {
4220 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
4221 SelectableItemList SelList =
pSelect->FindSelectionList(
4224 auto node = SelList.begin();
4225 while (node != SelList.end()) {
4230 if (pt && pt->IsVisible()) {
4231 m_pRolloverTrackSeg = pFindSel;
4232 showTrackRollover =
true;
4234 if (NULL == m_pTrackRolloverWin) {
4236 m_pTrackRolloverWin->IsActive(
false);
4239 if (!m_pTrackRolloverWin->IsActive()) {
4247 DistanceBearingMercator(
4248 segShow_point_b->m_lat, segShow_point_b->m_lon,
4249 segShow_point_a->m_lat, segShow_point_a->m_lon, &brg, &dist);
4251 if (!pt->m_bIsInLayer)
4252 s.Append(_(
"Track") +
": ");
4254 s.Append(_(
"Layer Track: "));
4256 if (pt->GetName().IsEmpty())
4257 s.Append(_(
"(unnamed)"));
4259 s.Append(pt->GetName());
4260 double tlenght = pt->Length();
4262 if (pt->GetLastPoint()->GetTimeString() &&
4263 pt->GetPoint(0)->GetTimeString()) {
4264 wxDateTime lastPointTime = pt->GetLastPoint()->GetCreateTime();
4265 wxDateTime zeroPointTime = pt->GetPoint(0)->GetCreateTime();
4266 if (lastPointTime.IsValid() && zeroPointTime.IsValid()) {
4267 wxTimeSpan ttime = lastPointTime - zeroPointTime;
4268 double htime = ttime.GetSeconds().ToDouble() / 3600.;
4269 s << wxString::Format(
" %.1f ", (
float)(tlenght / htime))
4270 << getUsrSpeedUnit();
4271 s << wxString(htime > 24. ? ttime.Format(
" %Dd %H:%M")
4272 : ttime.Format(
" %H:%M"));
4276 if (g_bShowTrackPointTime &&
4277 strlen(segShow_point_b->GetTimeString())) {
4278 wxString stamp = segShow_point_b->GetTimeString();
4279 wxDateTime timestamp = segShow_point_b->GetCreateTime();
4280 if (timestamp.IsValid()) {
4284 stamp = ocpn::toUsrDateTimeFormat(timestamp.FromUTC(), opts);
4286 s <<
"\n" << _(
"Segment Created: ") << stamp;
4291 s << wxString::Format(wxString(
"%03d%c ", wxConvUTF8), (
int)brg,
4296 (segShow_point_b->m_lat + segShow_point_a->m_lat) / 2;
4298 (segShow_point_b->m_lon + segShow_point_a->m_lon) / 2;
4300 top_frame::Get()->GetMag(brg, latAverage, lonAverage);
4302 s << wxString::Format(wxString(
"%03d%c ", wxConvUTF8), (
int)varBrg,
4308 if (segShow_point_a->GetTimeString() &&
4309 segShow_point_b->GetTimeString()) {
4310 wxDateTime apoint = segShow_point_a->GetCreateTime();
4311 wxDateTime bpoint = segShow_point_b->GetCreateTime();
4312 if (apoint.IsValid() && bpoint.IsValid()) {
4313 double segmentSpeed = toUsrSpeed(
4314 dist / ((bpoint - apoint).GetSeconds().ToDouble() / 3600.));
4315 s << wxString::Format(
" %.1f ", (
float)segmentSpeed)
4316 << getUsrSpeedUnit();
4320 m_pTrackRolloverWin->SetString(s);
4322 m_pTrackRolloverWin->SetBestPosition(mouse_x, mouse_y, 16, 16,
4323 LEG_ROLLOVER, win_size);
4324 m_pTrackRolloverWin->SetBitmap(LEG_ROLLOVER);
4325 m_pTrackRolloverWin->IsActive(
true);
4326 b_need_refresh =
true;
4327 showTrackRollover =
true;
4336 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
4338 m_pRolloverTrackSeg))
4339 showTrackRollover =
false;
4340 else if (m_pTrackRolloverWin && !m_pTrackRolloverWin->IsActive())
4341 showTrackRollover =
false;
4343 showTrackRollover =
true;
4347 if (m_pAISRolloverWin && m_pAISRolloverWin->IsActive())
4348 showTrackRollover =
false;
4351 if (m_pRouteRolloverWin && m_pRouteRolloverWin->IsActive())
4352 showTrackRollover =
false;
4358 if (m_pTrackRolloverWin &&
4359 !showTrackRollover) {
4360 m_pTrackRolloverWin->IsActive(
false);
4361 m_pRolloverTrackSeg = NULL;
4362 m_pTrackRolloverWin->Destroy();
4363 m_pTrackRolloverWin = NULL;
4364 b_need_refresh =
true;
4365 }
else if (m_pTrackRolloverWin && showTrackRollover) {
4366 m_pTrackRolloverWin->IsActive(
true);
4367 b_need_refresh =
true;
4370 if (b_need_refresh) Refresh();
4373void ChartCanvas::OnCursorTrackTimerEvent(wxTimerEvent &event) {
4374 if ((GetShowENCLights() || m_bsectors_shown) &&
4375 s57_CheckExtendedLightSectors(
this, mouse_x, mouse_y, VPoint,
4376 extendedSectorLegs)) {
4377 if (!m_bsectors_shown) {
4379 m_bsectors_shown =
true;
4382 if (m_bsectors_shown) {
4384 m_bsectors_shown =
false;
4392#if defined(__WXGTK__) || defined(__WXQT__)
4397 double cursor_lat, cursor_lon;
4400 if ((fabs(cursor_lat) < 90.) && (fabs(cursor_lon) < 360.)) {
4401 while (cursor_lon < -180.) cursor_lon += 360.;
4403 while (cursor_lon > 180.) cursor_lon -= 360.;
4405 SetCursorStatus(cursor_lat, cursor_lon);
4411void ChartCanvas::SetCursorStatus(
double cursor_lat,
double cursor_lon) {
4412 if (!top_frame::Get()->GetFrameStatusBar())
return;
4416 s1 += toSDMM(1, cursor_lat);
4418 s1 += toSDMM(2, cursor_lon);
4420 if (STAT_FIELD_CURSOR_LL >= 0)
4421 top_frame::Get()->SetStatusText(s1, STAT_FIELD_CURSOR_LL);
4423 if (STAT_FIELD_CURSOR_BRGRNG < 0)
return;
4428 DistanceBearingMercator(cursor_lat, cursor_lon,
gLat,
gLon, &brg, &dist);
4429 if (g_bShowMag) sm.Printf(
"%03d%c(M) ", (
int)toMagnetic(brg), 0x00B0);
4430 if (g_bShowTrue) st.Printf(
"%03d%c(T) ", (
int)brg, 0x00B0);
4432 wxString s = st + sm;
4445 if (g_bShowLiveETA) {
4448 float boatSpeedDefault = g_defaultBoatSpeed;
4453 if (!std::isnan(
gSog)) {
4455 if (boatSpeed < 0.5) {
4458 realTimeETA = dist / boatSpeed * 60;
4467 s << minutesToHoursDays(realTimeETA);
4472 s << wxString::Format(
"%d", (
int)toUsrSpeed(boatSpeedDefault, -1));
4473 s << wxString::Format(
"%s", getUsrSpeedUnit(-1));
4475 s << minutesToHoursDays(dist / boatSpeedDefault * 60);
4480 top_frame::Get()->SetStatusText(s, STAT_FIELD_CURSOR_BRGRNG);
4488wxString minutesToHoursDays(
float timeInMinutes) {
4491 if (timeInMinutes == 0) {
4496 else if (timeInMinutes < 60 && timeInMinutes != 0) {
4497 s << wxString::Format(
"%d", (
int)timeInMinutes);
4502 else if (timeInMinutes >= 60 && timeInMinutes < 24 * 60) {
4505 hours = (int)timeInMinutes / 60;
4506 min = (int)timeInMinutes % 60;
4509 s << wxString::Format(
"%d", hours);
4512 s << wxString::Format(
"%d", hours);
4514 s << wxString::Format(
"%d", min);
4521 else if (timeInMinutes > 24 * 60) {
4524 days = (int)(timeInMinutes / 60) / 24;
4525 hours = (int)(timeInMinutes / 60) % 24;
4528 s << wxString::Format(
"%d", days);
4531 s << wxString::Format(
"%d", days);
4533 s << wxString::Format(
"%d", hours);
4545void ChartCanvas::GetCursorLatLon(
double *lat,
double *lon) {
4553 wxPoint2DDouble *r) {
4558 double rlon, wxPoint2DDouble *r) {
4569 if (!g_bopengl && m_singleChart &&
4570 (m_singleChart->GetChartFamily() == CHART_FAMILY_RASTER) &&
4571 (((fabs(vp.
rotation) < .0001) && (fabs(vp.
skew) < .0001)) ||
4572 ((m_singleChart->GetChartProjectionType() != PROJECTION_MERCATOR) &&
4573 (m_singleChart->GetChartProjectionType() !=
4574 PROJECTION_TRANSVERSE_MERCATOR) &&
4575 (m_singleChart->GetChartProjectionType() != PROJECTION_POLYCONIC))) &&
4576 (m_singleChart->GetChartProjectionType() == vp.m_projection_type) &&
4577 (m_singleChart->GetChartType() != CHART_TYPE_PLUGIN)) {
4591 Cur_BSB_Ch->SetVPRasterParms(vp);
4592 double rpixxd, rpixyd;
4593 if (0 == Cur_BSB_Ch->latlong_to_pix_vp(rlat, rlon, rpixxd, rpixyd, vp)) {
4619 if (std::isnan(p.m_x)) {
4620 *r = wxPoint(INVALID_COORD, INVALID_COORD);
4624 if ((abs(p.m_x) < 1e6) && (abs(p.m_y) < 1e6))
4625 *r = wxPoint(wxRound(p.m_x), wxRound(p.m_y));
4627 *r = wxPoint(INVALID_COORD, INVALID_COORD);
4646 if (!g_bopengl && m_singleChart &&
4647 (m_singleChart->GetChartFamily() == CHART_FAMILY_RASTER) &&
4648 (((fabs(GetVP().rotation) < .0001) && (fabs(GetVP().skew) < .0001)) ||
4649 ((m_singleChart->GetChartProjectionType() != PROJECTION_MERCATOR) &&
4650 (m_singleChart->GetChartProjectionType() !=
4651 PROJECTION_TRANSVERSE_MERCATOR) &&
4652 (m_singleChart->GetChartProjectionType() != PROJECTION_POLYCONIC))) &&
4653 (m_singleChart->GetChartProjectionType() == GetVP().m_projection_type) &&
4654 (m_singleChart->GetChartType() != CHART_TYPE_PLUGIN)) {
4665 Cur_BSB_Ch->SetVPRasterParms(GetVP());
4668 if (0 == Cur_BSB_Ch->vp_pix_to_latlong(GetVP(), x, y, &slat, &slon)) {
4673 else if (slon > 180.)
4684 GetVP().
GetLLFromPix(wxPoint2DDouble(x, y), &lat, &lon);
4690 DoZoomCanvas(factor,
false);
4691 extendedSectorLegs.clear();
4696 m_bzooming_to_cursor = can_zoom_to_cursor && g_bEnableZoomToCursor;
4699 if (StartTimedMovement(stoptimer)) {
4701 m_zoom_factor = factor;
4706 if (m_modkeys == wxMOD_ALT) factor = pow(factor, .15);
4708 DoZoomCanvas(factor, can_zoom_to_cursor);
4711 extendedSectorLegs.clear();
4714void ChartCanvas::DoZoomCanvas(
double factor,
bool can_zoom_to_cursor) {
4717 if (!m_pCurrentStack)
return;
4723 if (m_bzooming)
return;
4732 double proposed_scale_onscreen =
4735 bool b_do_zoom =
false;
4744 if (!VPoint.b_quilt) {
4747 if (!m_disable_adjust_on_zoom) {
4748 int new_db_index = m_pQuilt->AdjustRefOnZoomIn(proposed_scale_onscreen);
4749 if (new_db_index >= 0)
4750 pc =
ChartData->OpenChartFromDB(new_db_index, FULL_INIT);
4754 int current_ref_stack_index = -1;
4755 if (m_pCurrentStack->nEntry) {
4757 m_pCurrentStack->GetDBIndex(m_pCurrentStack->nEntry - 1);
4758 m_pQuilt->SetReferenceChart(trial_index);
4759 new_db_index = m_pQuilt->AdjustRefOnZoomIn(proposed_scale_onscreen);
4760 if (new_db_index >= 0)
4761 pc =
ChartData->OpenChartFromDB(new_db_index, FULL_INIT);
4765 if (m_pCurrentStack)
4766 m_pCurrentStack->SetCurrentEntryFromdbIndex(
4777 double min_allowed_scale =
4780 if (proposed_scale_onscreen < min_allowed_scale) {
4785 proposed_scale_onscreen = min_allowed_scale;
4789 proposed_scale_onscreen = wxMax(proposed_scale_onscreen, g_maxzoomin);
4792 }
else if (factor < 1) {
4797 bool b_smallest =
false;
4799 if (!VPoint.b_quilt) {
4804 LLBBox viewbox = VPoint.GetBBox();
4806 int current_index =
ChartData->FinddbIndex(pc->GetFullPath());
4807 double max_allowed_scale;
4821 if (proposed_scale_onscreen > max_allowed_scale) {
4823 proposed_scale_onscreen = max_allowed_scale;
4828 if (!m_disable_adjust_on_zoom) {
4830 m_pQuilt->AdjustRefOnZoomOut(proposed_scale_onscreen);
4831 if (new_db_index >= 0)
4832 pc =
ChartData->OpenChartFromDB(new_db_index, FULL_INIT);
4834 if (m_pCurrentStack)
4835 m_pCurrentStack->SetCurrentEntryFromdbIndex(
4838 b_smallest = m_pQuilt->IsChartSmallestScale(new_db_index);
4840 if (b_smallest || (0 == m_pQuilt->GetExtendedStackCount()))
4841 proposed_scale_onscreen =
4842 wxMin(proposed_scale_onscreen,
4848 m_absolute_min_scale_ppm)
4849 proposed_scale_onscreen =
4858 bool b_allow_ztc =
true;
4859 if (m_bFollow && m_bLookAhead) b_allow_ztc =
false;
4860 if (can_zoom_to_cursor && g_bEnableZoomToCursor && b_allow_ztc) {
4862 double brg, distance;
4863 ll_gc_ll_reverse(
gLat,
gLon, GetVP().clat, GetVP().clon, &brg,
4866 meters_to_shift = distance * 1852;
4874 PanCanvas(r.x - mouse_x, r.y - mouse_y);
4877 if (m_bFollow) DoCanvasUpdate();
4884void ChartCanvas::SetAbsoluteMinScale(
double min_scale) {
4886 m_absolute_min_scale_ppm = wxMax(m_absolute_min_scale_ppm, x_scale_ppm);
4890void ChartCanvas::RotateCanvas(
double dir) {
4894 if (StartTimedMovement()) {
4896 m_rotation_speed = dir * 60;
4899 double speed = dir * 10;
4900 if (m_modkeys == wxMOD_ALT) speed /= 20;
4901 DoRotateCanvas(VPoint.
rotation + PI / 180 * speed);
4905void ChartCanvas::DoRotateCanvas(
double rotation) {
4906 while (rotation < 0) rotation += 2 * PI;
4907 while (rotation > 2 * PI) rotation -= 2 * PI;
4909 if (rotation == VPoint.
rotation || std::isnan(rotation))
return;
4911 SetVPRotation(rotation);
4912 top_frame::Get()->UpdateRotationState(VPoint.
rotation);
4915void ChartCanvas::DoTiltCanvas(
double tilt) {
4916 while (tilt < 0) tilt = 0;
4917 while (tilt > .95) tilt = .95;
4919 if (tilt == VPoint.
tilt || std::isnan(tilt))
return;
4925void ChartCanvas::TogglebFollow() {
4932void ChartCanvas::ClearbFollow() {
4935 top_frame::Get()->SetMenubarItemState(ID_MENU_NAV_FOLLOW,
false);
4937 UpdateFollowButtonState();
4941 top_frame::Get()->SetChartUpdatePeriod();
4944void ChartCanvas::SetbFollow() {
4947 if ((fabs(m_OSoffsetx) > VPoint.
pix_width / 2) ||
4948 (fabs(m_OSoffsety) > VPoint.
pix_height / 2)) {
4956 p.m_x += m_OSoffsetx;
4957 p.m_y -= m_OSoffsety;
4966 top_frame::Get()->SetMenubarItemState(ID_MENU_NAV_FOLLOW,
true);
4967 UpdateFollowButtonState();
4969 if (!g_bSmoothRecenter) {
4973 top_frame::Get()->SetChartUpdatePeriod();
4976void ChartCanvas::UpdateFollowButtonState() {
4979 m_muiBar->SetFollowButtonState(0);
4982 m_muiBar->SetFollowButtonState(2);
4984 m_muiBar->SetFollowButtonState(1);
4990 androidSetFollowTool(0);
4993 androidSetFollowTool(2);
4995 androidSetFollowTool(1);
5002 for (
auto pic : *
PluginLoader::GetInstance()->GetPlugInArray()) {
5003 if (pic->m_enabled && pic->m_init_state) {
5004 switch (pic->m_api_version) {
5007 if (ppi) ppi->UpdateFollowState(m_canvasIndex, m_bFollow);
5018void ChartCanvas::JumpToPosition(
double lat,
double lon,
double scale_ppm) {
5019 if (g_bSmoothRecenter && !m_routeState) {
5020 if (StartSmoothJump(lat, lon, scale_ppm))
5024 double gcDist, gcBearingEnd;
5025 Geodesic::GreatCircleDistBear(m_vLon, m_vLat, lon, lat, &gcDist, NULL,
5027 gcBearingEnd += 180;
5028 double lat_offset = cos(gcBearingEnd * PI / 180.) * 0.5 *
5031 sin(gcBearingEnd * PI / 180.) * 0.5 * GetCanvasWidth() /
GetVPScale();
5032 double new_lat = lat + (lat_offset / (1852 * 60));
5033 double new_lon = lon + (lon_offset / (1852 * 60));
5036 StartSmoothJump(lat, lon, scale_ppm);
5041 if (lon > 180.0) lon -= 360.0;
5047 if (!GetQuiltMode()) {
5049 if (m_singleChart) skew = m_singleChart->GetChartSkew() * PI / 180.;
5050 SetViewPoint(lat, lon, scale_ppm, skew, GetVPRotation());
5054 VPoint.
chart_scale = m_canvas_scale_factor / (scale_ppm);
5055 AdjustQuiltRefChart();
5062 UpdateFollowButtonState();
5070bool ChartCanvas::StartSmoothJump(
double lat,
double lon,
double scale_ppm) {
5075 Geodesic::GreatCircleDistBear(m_vLon, m_vLat, lon, lat, &gcDist, NULL, NULL);
5076 double distance_pixels = gcDist *
GetVPScale();
5077 if (distance_pixels > 0.5 * GetCanvasWidth()) {
5083 m_startLat = m_vLat;
5084 m_startLon = m_vLon;
5089 m_endLon = (lon > 180.0) ? (lon - 360.0) : lon;
5090 m_endScale = scale_ppm;
5093 m_animationDuration = 600;
5094 m_animationStart = wxGetLocalTimeMillis();
5101 m_easeTimer.Start(16, wxTIMER_CONTINUOUS);
5102 m_animationActive =
true;
5107void ChartCanvas::OnJumpEaseTimer(wxTimerEvent &event) {
5109 wxLongLong now = wxGetLocalTimeMillis();
5110 double elapsed = (now - m_animationStart).ToDouble();
5111 double t = elapsed / m_animationDuration.ToDouble();
5112 if (t > 1.0) t = 1.0;
5115 double e = easeOutCubic(t);
5118 double curLat = m_startLat + (m_endLat - m_startLat) * e;
5119 double curLon = m_startLon + (m_endLon - m_startLon) * e;
5120 double curScale = m_startScale + (m_endScale - m_startScale) * e;
5125 SetViewPoint(curLat, curLon, curScale, 0.0, GetVPRotation());
5131 m_animationActive =
false;
5132 UpdateFollowButtonState();
5141 extendedSectorLegs.clear();
5150 if (iters++ > 5)
return false;
5151 if (!std::isnan(dlat))
break;
5154 if (fabs(dx) < 1 && fabs(dy) < 1)
return false;
5157 if (dlon > 360.) dlon -= 360.;
5158 if (dlon < -360.) dlon += 360.;
5173 int cur_ref_dbIndex = m_pQuilt->GetRefChartdbIndex();
5177 if (VPoint.b_quilt) {
5178 int new_ref_dbIndex = m_pQuilt->GetRefChartdbIndex();
5179 if ((new_ref_dbIndex != cur_ref_dbIndex) && (new_ref_dbIndex != -1)) {
5183 double tweak_scale_ppm =
5193 if (new_ref_dbIndex == -1) {
5194#pragma GCC diagnostic push
5195#pragma GCC diagnostic ignored "-Warray-bounds"
5202 int trial_index = -1;
5203 if (m_pCurrentStack->nEntry) {
5204 trial_index = m_pCurrentStack->GetDBIndex(m_pCurrentStack->nEntry - 1);
5207 if (trial_index < 0) {
5208 auto full_screen_array = GetQuiltFullScreendbIndexArray();
5209 if (full_screen_array.size())
5210 trial_index = full_screen_array[full_screen_array.size() - 1];
5213 if (trial_index >= 0) {
5214 m_pQuilt->SetReferenceChart(trial_index);
5219#pragma GCC diagnostic pop
5227 toSM(dlat, dlon,
gLat,
gLon, &offx, &offy);
5229 double offset_angle = atan2(offy, offx);
5230 double offset_distance = sqrt((offy * offy) + (offx * offx));
5231 double chart_angle = GetVPRotation();
5232 double target_angle = chart_angle - offset_angle;
5233 double d_east_mod = offset_distance * cos(target_angle);
5234 double d_north_mod = offset_distance * sin(target_angle);
5239 if (m_bFollow && ((fabs(m_OSoffsetx) > VPoint.
pix_width / 2) ||
5240 (fabs(m_OSoffsety) > VPoint.
pix_height / 2))) {
5242 UpdateFollowButtonState();
5248 pCurTrackTimer->Start(m_curtrack_timer_msec, wxTIMER_ONE_SHOT);
5253bool ChartCanvas::IsOwnshipOnScreen() {
5256 if (((r.x > 0) && r.x < GetCanvasWidth()) &&
5257 ((r.y > 0) && r.y < GetCanvasHeight()))
5263void ChartCanvas::ReloadVP(
bool b_adjust) {
5264 if (g_brightness_init) SetScreenBrightness(g_nbrightness);
5266 LoadVP(VPoint, b_adjust);
5269void ChartCanvas::LoadVP(
ViewPort &vp,
bool b_adjust) {
5271 if (g_bopengl && m_glcc) {
5272 m_glcc->Invalidate();
5273 if (m_glcc->GetSize() != GetSize()) {
5274 m_glcc->SetSize(GetSize());
5279 m_cache_vp.Invalidate();
5280 m_bm_cache_vp.Invalidate();
5283 VPoint.Invalidate();
5285 if (m_pQuilt) m_pQuilt->Invalidate();
5294 vp.m_projection_type, b_adjust);
5297void ChartCanvas::SetQuiltRefChart(
int dbIndex) {
5298 m_pQuilt->SetReferenceChart(dbIndex);
5299 VPoint.Invalidate();
5300 m_pQuilt->Invalidate();
5303double ChartCanvas::GetBestStartScale(
int dbi_hint,
const ViewPort &vp) {
5305 return m_pQuilt->GetBestStartScale(dbi_hint, vp);
5312int ChartCanvas::AdjustQuiltRefChart() {
5317 ChartData->OpenChartFromDB(m_pQuilt->GetRefChartdbIndex(), FULL_INIT);
5319 double min_ref_scale =
5320 pc->GetNormalScaleMin(m_canvas_scale_factor,
false);
5321 double max_ref_scale =
5322 pc->GetNormalScaleMax(m_canvas_scale_factor, m_canvas_width);
5325 ret = m_pQuilt->AdjustRefOnZoomIn(VPoint.
chart_scale);
5326 }
else if (VPoint.
chart_scale > max_ref_scale * 64) {
5327 ret = m_pQuilt->AdjustRefOnZoomOut(VPoint.
chart_scale);
5329 bool brender_ok = IsChartLargeEnoughToRender(pc, VPoint);
5332 int target_stack_index = wxNOT_FOUND;
5334 for (
auto index : m_pQuilt->GetExtendedStackIndexArray()) {
5335 if (index == m_pQuilt->GetRefChartdbIndex()) {
5336 target_stack_index = il;
5341 if (wxNOT_FOUND == target_stack_index)
5342 target_stack_index = 0;
5344 int ref_family = pc->GetChartFamily();
5345 int extended_array_count =
5346 m_pQuilt->GetExtendedStackIndexArray().size();
5347 while ((!brender_ok) &&
5348 ((
int)target_stack_index < (extended_array_count - 1))) {
5349 target_stack_index++;
5351 m_pQuilt->GetExtendedStackIndexArray()[target_stack_index];
5353 if ((ref_family ==
ChartData->GetDBChartFamily(test_db_index)) &&
5354 IsChartQuiltableRef(test_db_index)) {
5357 ChartData->OpenChartFromDB(test_db_index, FULL_INIT);
5359 brender_ok = IsChartLargeEnoughToRender(ptest_chart, VPoint);
5366 m_pQuilt->GetExtendedStackIndexArray()[target_stack_index];
5367 if ((ref_family ==
ChartData->GetDBChartFamily(new_db_index)) &&
5368 IsChartQuiltableRef(new_db_index)) {
5369 m_pQuilt->SetReferenceChart(new_db_index);
5372 ret = m_pQuilt->GetRefChartdbIndex();
5374 ret = m_pQuilt->GetRefChartdbIndex();
5377 ret = m_pQuilt->GetRefChartdbIndex();
5386void ChartCanvas::UpdateCanvasOnGroupChange() {
5387 delete m_pCurrentStack;
5399bool ChartCanvas::SetViewPointByCorners(
double latSW,
double lonSW,
5400 double latNE,
double lonNE) {
5402 double latc = (latSW + latNE) / 2.0;
5403 double lonc = (lonSW + lonNE) / 2.0;
5406 double ne_easting, ne_northing;
5407 toSM(latNE, lonNE, latc, lonc, &ne_easting, &ne_northing);
5409 double sw_easting, sw_northing;
5410 toSM(latSW, lonSW, latc, lonc, &sw_easting, &sw_northing);
5412 double scale_ppm = VPoint.
pix_height / fabs(ne_northing - sw_northing);
5419 VPoint.
rotation, VPoint.m_projection_type,
true, refresh);
5422bool ChartCanvas::SetVPProjection(
int projection) {
5428 double prev_true_scale_ppm = m_true_scale_ppm;
5433 m_absolute_min_scale_ppm));
5441bool ChartCanvas::SetVPRotation(
double angle) {
5443 VPoint.
skew, angle);
5446 double skew,
double rotation,
int projection,
5447 bool b_adjust,
bool b_refresh) {
5453 if (VPoint.IsValid()) {
5454 if ((fabs(VPoint.
view_scale_ppm - scale_ppm) / scale_ppm < 1e-5) &&
5455 (fabs(VPoint.
skew - skew) < 1e-9) &&
5456 (fabs(VPoint.
rotation - rotation) < 1e-9) &&
5457 (fabs(VPoint.
clat - lat) < 1e-9) && (fabs(VPoint.
clon - lon) < 1e-9) &&
5458 (VPoint.m_projection_type == projection ||
5459 projection == PROJECTION_UNKNOWN))
5462 if (VPoint.m_projection_type != projection)
5463 VPoint.InvalidateTransformCache();
5473 if (projection != PROJECTION_UNKNOWN)
5474 VPoint.SetProjectionType(projection);
5475 else if (VPoint.m_projection_type == PROJECTION_UNKNOWN)
5476 VPoint.SetProjectionType(PROJECTION_MERCATOR);
5481 static const double MAX_LAT = 85.0;
5483 if (VPoint.
pix_height > 0 && scale_ppm > 0) {
5484 double edge_lat, edge_lon;
5486 for (
int i = 0; i < 3; i++) {
5489 if (!std::isnan(edge_lat) && edge_lat > MAX_LAT) {
5490 VPoint.
clat -= (edge_lat - MAX_LAT);
5496 for (
int i = 0; i < 3; i++) {
5500 if (!std::isnan(edge_lat) && edge_lat < -MAX_LAT) {
5501 VPoint.
clat -= (edge_lat + MAX_LAT);
5509 if (VPoint.
clat > MAX_LAT)
5510 VPoint.
clat = MAX_LAT;
5511 else if (VPoint.
clat < -MAX_LAT)
5512 VPoint.
clat = -MAX_LAT;
5516 if (VPoint.m_projection_type == PROJECTION_POLYCONIC ||
5517 VPoint.m_projection_type == PROJECTION_TRANSVERSE_MERCATOR)
5529 bool bwasValid = VPoint.IsValid();
5534 m_cache_vp.Invalidate();
5539 VPoint.
chart_scale = m_canvas_scale_factor / (scale_ppm);
5543 if (top_frame::Get()->GetCanvasIndexUnderMouse() == m_canvasIndex) {
5544 int mouseX = mouse_x;
5545 int mouseY = mouse_y;
5546 if ((mouseX > 0) && (mouseX < VPoint.
pix_width) && (mouseY > 0) &&
5548 double lat_mouse, lon_mouse;
5556 if (!VPoint.b_quilt && m_singleChart) {
5561 if (b_adjust) m_singleChart->AdjustVP(last_vp, VPoint);
5565 if ((!m_cache_vp.IsValid()) ||
5570 wxPoint cp_last, cp_this;
5574 if (cp_last != cp_this) {
5580 if (m_pCurrentStack) {
5582 int current_db_index;
5584 m_pCurrentStack->GetCurrentEntrydbIndex();
5586 ChartData->BuildChartStack(m_pCurrentStack, lat, lon, current_db_index,
5588 m_pCurrentStack->SetCurrentEntryFromdbIndex(current_db_index);
5591 if (!g_bopengl) VPoint.b_MercatorProjectionOverride =
false;
5595 if (VPoint.b_quilt) {
5599 m_pQuilt->InvalidateAllQuiltPatchs();
5603 if (!m_pCurrentStack)
return false;
5605 int current_db_index;
5607 m_pCurrentStack->GetCurrentEntrydbIndex();
5609 ChartData->BuildChartStack(m_pCurrentStack, lat, lon, m_groupIndex);
5610 m_pCurrentStack->SetCurrentEntryFromdbIndex(current_db_index);
5613 int current_ref_stack_index = -1;
5614 for (
int i = 0; i < m_pCurrentStack->nEntry; i++) {
5615 if (m_pQuilt->GetRefChartdbIndex() == m_pCurrentStack->GetDBIndex(i))
5616 current_ref_stack_index = i;
5619 if (g_bFullScreenQuilt) {
5620 current_ref_stack_index = m_pQuilt->GetRefChartdbIndex();
5624 bool b_needNewRef =
false;
5627 if ((-1 == current_ref_stack_index) &&
5628 (m_pQuilt->GetRefChartdbIndex() >= 0))
5629 b_needNewRef =
true;
5636 bool renderable =
true;
5638 ChartData->OpenChartFromDB(m_pQuilt->GetRefChartdbIndex(), FULL_INIT);
5639 if (referenceChart) {
5640 double chartMaxScale = referenceChart->GetNormalScaleMax(
5642 renderable = chartMaxScale * 64 >= VPoint.
chart_scale;
5644 if (!renderable) b_needNewRef =
true;
5647 if (b_needNewRef && !m_disable_adjust_on_zoom) {
5649 ChartData->GetChartTableEntry(m_pQuilt->GetRefChartdbIndex());
5650 int target_scale = cte_ref.GetScale();
5651 int target_type = cte_ref.GetChartType();
5652 int candidate_stack_index;
5659 candidate_stack_index = 0;
5660 while (candidate_stack_index <= m_pCurrentStack->nEntry - 1) {
5662 m_pCurrentStack->GetDBIndex(candidate_stack_index));
5663 int candidate_scale = cte_candidate.GetScale();
5664 int candidate_type = cte_candidate.GetChartType();
5666 if ((candidate_scale >= target_scale) &&
5667 (candidate_type == target_type)) {
5668 bool renderable =
true;
5670 m_pCurrentStack->GetDBIndex(candidate_stack_index), FULL_INIT);
5671 if (tentative_referenceChart) {
5672 double chartMaxScale =
5673 tentative_referenceChart->GetNormalScaleMax(
5675 renderable = chartMaxScale * 1.5 > VPoint.
chart_scale;
5678 if (renderable)
break;
5681 candidate_stack_index++;
5686 if (candidate_stack_index >= m_pCurrentStack->nEntry) {
5687 candidate_stack_index = m_pCurrentStack->nEntry - 1;
5688 while (candidate_stack_index >= 0) {
5689 int idx = m_pCurrentStack->GetDBIndex(candidate_stack_index);
5693 int candidate_scale = cte_candidate.GetScale();
5694 int candidate_type = cte_candidate.GetChartType();
5696 if ((candidate_scale <= target_scale) &&
5697 (candidate_type == target_type))
5700 candidate_stack_index--;
5705 if ((candidate_stack_index >= m_pCurrentStack->nEntry) ||
5706 (candidate_stack_index < 0))
5707 candidate_stack_index = 0;
5709 int new_ref_index = m_pCurrentStack->GetDBIndex(candidate_stack_index);
5711 m_pQuilt->SetReferenceChart(new_ref_index);
5717 int ref_db_index = m_pQuilt->GetRefChartdbIndex(), proj;
5722 bool renderable =
true;
5724 ChartData->OpenChartFromDB(ref_db_index, FULL_INIT);
5725 if (referenceChart) {
5726 double chartMaxScale = referenceChart->GetNormalScaleMax(
5728 renderable = chartMaxScale * 1.5 > VPoint.
chart_scale;
5729 proj =
ChartData->GetDBChartProj(ref_db_index);
5731 proj = PROJECTION_MERCATOR;
5733 VPoint.b_MercatorProjectionOverride =
5734 (m_pQuilt->GetnCharts() == 0 || !renderable);
5736 if (VPoint.b_MercatorProjectionOverride) proj = PROJECTION_MERCATOR;
5738 VPoint.SetProjectionType(proj);
5743 if (m_pQuilt->IsQuiltDelta(VPoint)) {
5748 m_pQuilt->AdjustQuiltVP(last_vp, VPoint);
5768 m_pQuilt->Invalidate();
5785 if (b_refresh) Refresh(
false);
5792 }
else if (!g_bopengl) {
5793 OcpnProjType projection = PROJECTION_UNKNOWN;
5796 projection = m_singleChart->GetChartProjectionType();
5797 if (projection == PROJECTION_UNKNOWN) projection = PROJECTION_MERCATOR;
5798 VPoint.SetProjectionType(projection);
5802 if (last_vp.m_projection_type != VPoint.m_projection_type) {
5803 m_cache_vp.Invalidate();
5807 UpdateCanvasControlBar();
5811 if (VPoint.GetBBox().GetValid()) {
5814 VPoint.
ref_scale = m_singleChart->GetNativeScale();
5823 VPoint.
ref_scale = m_pQuilt->GetRefNativeScale();
5826 VPoint.
ref_scale = m_pQuilt->GetRefNativeScale();
5833 wxPoint2DDouble r, r1;
5835 double delta_check =
5839 double check_point = wxMin(89., VPoint.
clat);
5841 while ((delta_check + check_point) > 90.) delta_check /= 2.;
5844 DistanceBearingMercator(check_point, VPoint.
clon, check_point + delta_check,
5845 VPoint.
clon, 0, &rhumbDist);
5850 double delta_p = sqrt(((r1.m_y - r.m_y) * (r1.m_y - r.m_y)) +
5851 ((r1.m_x - r.m_x) * (r1.m_x - r.m_x)));
5853 m_true_scale_ppm = delta_p / (rhumbDist * 1852);
5857 if (0.0 == m_true_scale_ppm) m_true_scale_ppm = scale_ppm;
5863 if (scale_ppm < 1e-4) m_true_scale_ppm = scale_ppm;
5865 if (m_true_scale_ppm)
5866 VPoint.
chart_scale = m_canvas_scale_factor / (m_true_scale_ppm);
5871 double round_factor = 1000.;
5875 round_factor = 100.;
5877 round_factor = 1000.;
5880 double retina_coef = 1;
5884 retina_coef = GetContentScaleFactor();
5895 double true_scale_display =
5896 wxRound(VPoint.
chart_scale / round_factor) * round_factor * retina_coef;
5901 if (m_displayed_scale_factor > 10.0)
5902 text.Printf(
"%s %4.0f (%1.0fx)", _(
"Scale"), true_scale_display,
5903 m_displayed_scale_factor);
5904 else if (m_displayed_scale_factor > 1.0)
5905 text.Printf(
"%s %4.0f (%1.1fx)", _(
"Scale"), true_scale_display,
5906 m_displayed_scale_factor);
5907 else if (m_displayed_scale_factor > 0.1) {
5908 double sfr = wxRound(m_displayed_scale_factor * 10.) / 10.;
5909 text.Printf(
"%s %4.0f (%1.2fx)", _(
"Scale"), true_scale_display, sfr);
5910 }
else if (m_displayed_scale_factor > 0.01) {
5911 double sfr = wxRound(m_displayed_scale_factor * 100.) / 100.;
5912 text.Printf(
"%s %4.0f (%1.2fx)", _(
"Scale"), true_scale_display, sfr);
5915 "%s %4.0f (---)", _(
"Scale"),
5916 true_scale_display);
5919 m_scaleValue = true_scale_display;
5921 if (m_muiBar) m_muiBar->UpdateDynamicValues();
5923 if (m_bShowScaleInStatusBar && top_frame::Get()->GetStatusBar() &&
5924 (top_frame::Get()->GetStatusBar()->GetFieldsCount() >
5925 STAT_FIELD_SCALE)) {
5927 bool b_noshow =
false;
5931 wxClientDC dc(top_frame::Get()->GetStatusBar());
5933 wxFont *templateFont = FontMgr::Get().
GetFont(_(
"StatusBar"), 0);
5934 dc.SetFont(*templateFont);
5935 dc.GetTextExtent(text, &w, &h);
5940 top_frame::Get()->GetStatusBar()->GetFieldRect(STAT_FIELD_SCALE,
5942 if (w && w > rect.width) {
5943 text.Printf(
"%s (%1.1fx)", _(
"Scale"), m_displayed_scale_factor);
5947 dc.GetTextExtent(text, &w, &h);
5949 if (w && w > rect.width) {
5955 if (!b_noshow) top_frame::Get()->SetStatusText(text, STAT_FIELD_SCALE);
5960 m_vLat = VPoint.
clat;
5961 m_vLon = VPoint.
clon;
5975static int s_png_pred_icon[] = {-10, -10, -10, 10, 10, 10, 10, -10};
5979static int s_ownship_icon[] = {5, -42, 11, -28, 11, 42, -11, 42, -11, -28,
5980 -5, -42, -11, 0, 11, 0, 0, 42, 0, -42};
5982wxColour ChartCanvas::PredColor() {
5985 if (SHIP_NORMAL == m_ownship_state)
5986 return GetGlobalColor(
"URED");
5988 else if (SHIP_LOWACCURACY == m_ownship_state)
5989 return GetGlobalColor(
"YELO1");
5991 return GetGlobalColor(
"NODTA");
5994wxColour ChartCanvas::ShipColor() {
5998 if (SHIP_NORMAL != m_ownship_state)
return GetGlobalColor(
"GREY1");
6000 if (SHIP_LOWACCURACY == m_ownship_state)
return GetGlobalColor(
"YELO1");
6002 return GetGlobalColor(
"URED");
6005void ChartCanvas::ShipDrawLargeScale(
ocpnDC &dc,
6006 wxPoint2DDouble lShipMidPoint) {
6007 dc.SetPen(wxPen(PredColor(), 2));
6009 if (SHIP_NORMAL == m_ownship_state)
6010 dc.SetBrush(wxBrush(ShipColor(), wxBRUSHSTYLE_TRANSPARENT));
6012 dc.SetBrush(wxBrush(GetGlobalColor(
"YELO1")));
6014 dc.DrawEllipse(lShipMidPoint.m_x - 10, lShipMidPoint.m_y - 10, 20, 20);
6015 dc.DrawEllipse(lShipMidPoint.m_x - 6, lShipMidPoint.m_y - 6, 12, 12);
6017 dc.
DrawLine(lShipMidPoint.m_x - 12, lShipMidPoint.m_y, lShipMidPoint.m_x + 12,
6019 dc.
DrawLine(lShipMidPoint.m_x, lShipMidPoint.m_y - 12, lShipMidPoint.m_x,
6020 lShipMidPoint.m_y + 12);
6023void ChartCanvas::ShipIndicatorsDraw(
ocpnDC &dc,
int img_height,
6024 wxPoint GPSOffsetPixels,
6025 wxPoint2DDouble lGPSPoint) {
6030 float ref_dim = m_display_size_mm / 24;
6031 ref_dim = wxMin(ref_dim, 12);
6032 ref_dim = wxMax(ref_dim, 6);
6035 cPred.Set(g_cog_predictor_color);
6036 if (cPred == wxNullColour) cPred = PredColor();
6043 double nominal_line_width_pix = wxMax(
6045 floor(m_pix_per_mm / 2));
6049 if (nominal_line_width_pix > g_cog_predictor_width)
6050 g_cog_predictor_width = nominal_line_width_pix;
6053 wxPoint lPredPoint, lHeadPoint;
6055 float pCog = std::isnan(
gCog) ? 0 :
gCog;
6056 float pSog = std::isnan(
gSog) ? 0 :
gSog;
6058 double pred_lat, pred_lon;
6059 ll_gc_ll(
gLat,
gLon, pCog, pSog * g_ownship_predictor_minutes / 60.,
6060 &pred_lat, &pred_lon);
6071 float ndelta_pix = 10.;
6072 double hdg_pred_lat, hdg_pred_lon;
6073 bool b_render_hdt =
false;
6074 if (!std::isnan(
gHdt)) {
6076 ll_gc_ll(
gLat,
gLon,
gHdt, g_ownship_HDTpredictor_miles, &hdg_pred_lat,
6079 float dist = sqrtf(powf((
float)(lHeadPoint.x - lPredPoint.x), 2) +
6080 powf((
float)(lHeadPoint.y - lPredPoint.y), 2));
6081 if (dist > ndelta_pix ) {
6082 box.SetFromSegment(
gLat,
gLon, hdg_pred_lat, hdg_pred_lon);
6083 if (!GetVP().GetBBox().IntersectOut(box)) b_render_hdt =
true;
6088 wxPoint lShipMidPoint;
6089 lShipMidPoint.x = lGPSPoint.m_x + GPSOffsetPixels.x;
6090 lShipMidPoint.y = lGPSPoint.m_y + GPSOffsetPixels.y;
6091 float lpp = sqrtf(powf((
float)(lPredPoint.x - lShipMidPoint.x), 2) +
6092 powf((
float)(lPredPoint.y - lShipMidPoint.y), 2));
6094 if (lpp >= img_height / 2) {
6095 box.SetFromSegment(
gLat,
gLon, pred_lat, pred_lon);
6096 if (!GetVP().GetBBox().IntersectOut(box) && !std::isnan(
gCog) &&
6097 !std::isnan(
gSog)) {
6099 float dash_length = ref_dim;
6100 wxDash dash_long[2];
6102 (int)(floor(g_Platform->GetDisplayDPmm() * dash_length) /
6103 g_cog_predictor_width);
6104 dash_long[1] = dash_long[0] / 2.0;
6108 if (dash_length > 250.) {
6109 dash_long[0] = 250. / g_cog_predictor_width;
6110 dash_long[1] = dash_long[0] / 2;
6113 wxPen ppPen2(cPred, g_cog_predictor_width,
6114 (wxPenStyle)g_cog_predictor_style);
6115 if (g_cog_predictor_style == (wxPenStyle)wxUSER_DASH)
6116 ppPen2.SetDashes(2, dash_long);
6119 lGPSPoint.m_x + GPSOffsetPixels.x, lGPSPoint.m_y + GPSOffsetPixels.y,
6120 lPredPoint.x + GPSOffsetPixels.x, lPredPoint.y + GPSOffsetPixels.y);
6122 if (g_cog_predictor_width > 1) {
6123 float line_width = g_cog_predictor_width / 3.;
6125 wxDash dash_long3[2];
6126 dash_long3[0] = g_cog_predictor_width / line_width * dash_long[0];
6127 dash_long3[1] = g_cog_predictor_width / line_width * dash_long[1];
6129 wxPen ppPen3(GetGlobalColor(
"UBLCK"), wxMax(1, line_width),
6130 (wxPenStyle)g_cog_predictor_style);
6131 if (g_cog_predictor_style == (wxPenStyle)wxUSER_DASH)
6132 ppPen3.SetDashes(2, dash_long3);
6134 dc.StrokeLine(lGPSPoint.m_x + GPSOffsetPixels.x,
6135 lGPSPoint.m_y + GPSOffsetPixels.y,
6136 lPredPoint.x + GPSOffsetPixels.x,
6137 lPredPoint.y + GPSOffsetPixels.y);
6140 if (g_cog_predictor_endmarker) {
6142 double png_pred_icon_scale_factor = .4;
6143 if (g_ShipScaleFactorExp > 1.0)
6144 png_pred_icon_scale_factor *= (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
6145 if (g_scaler) png_pred_icon_scale_factor *= 1.0 / g_scaler;
6149 float cog_rad = atan2f((
float)(lPredPoint.y - lShipMidPoint.y),
6150 (
float)(lPredPoint.x - lShipMidPoint.x));
6151 cog_rad += (float)PI;
6153 for (
int i = 0; i < 4; i++) {
6155 double pxa = (double)(s_png_pred_icon[j]);
6156 double pya = (double)(s_png_pred_icon[j + 1]);
6158 pya *= png_pred_icon_scale_factor;
6159 pxa *= png_pred_icon_scale_factor;
6161 double px = (pxa * sin(cog_rad)) + (pya * cos(cog_rad));
6162 double py = (pya * sin(cog_rad)) - (pxa * cos(cog_rad));
6164 icon[i].x = (int)wxRound(px) + lPredPoint.x + GPSOffsetPixels.x;
6165 icon[i].y = (int)wxRound(py) + lPredPoint.y + GPSOffsetPixels.y;
6169 wxPen ppPen1(GetGlobalColor(
"UBLCK"), g_cog_predictor_width / 2,
6172 dc.SetBrush(wxBrush(cPred));
6174 dc.StrokePolygon(4, icon);
6181 float hdt_dash_length = ref_dim * 0.4;
6183 cPred.Set(g_ownship_HDTpredictor_color);
6184 if (cPred == wxNullColour) cPred = PredColor();
6186 (g_ownship_HDTpredictor_width > 0 ? g_ownship_HDTpredictor_width
6187 : g_cog_predictor_width * 0.8);
6188 wxDash dash_short[2];
6190 (int)(floor(g_Platform->GetDisplayDPmm() * hdt_dash_length) /
6193 (int)(floor(g_Platform->GetDisplayDPmm() * hdt_dash_length * 0.9) /
6196 wxPen ppPen2(cPred, hdt_width, (wxPenStyle)g_ownship_HDTpredictor_style);
6197 if (g_ownship_HDTpredictor_style == (wxPenStyle)wxUSER_DASH)
6198 ppPen2.SetDashes(2, dash_short);
6202 lGPSPoint.m_x + GPSOffsetPixels.x, lGPSPoint.m_y + GPSOffsetPixels.y,
6203 lHeadPoint.x + GPSOffsetPixels.x, lHeadPoint.y + GPSOffsetPixels.y);
6205 wxPen ppPen1(cPred, g_cog_predictor_width / 3, wxPENSTYLE_SOLID);
6207 dc.SetBrush(wxBrush(GetGlobalColor(
"GREY2")));
6209 if (g_ownship_HDTpredictor_endmarker) {
6210 double nominal_circle_size_pixels = wxMax(
6211 4.0, floor(m_pix_per_mm * (ref_dim / 5.0)));
6214 if (g_ShipScaleFactorExp > 1.0)
6215 nominal_circle_size_pixels *= (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
6217 dc.StrokeCircle(lHeadPoint.x + GPSOffsetPixels.x,
6218 lHeadPoint.y + GPSOffsetPixels.y,
6219 nominal_circle_size_pixels / 2);
6224 if (g_bNavAidRadarRingsShown && g_iNavAidRadarRingsNumberVisible > 0) {
6225 double factor = 1.00;
6226 if (g_pNavAidRadarRingsStepUnits == 1)
6228 else if (g_pNavAidRadarRingsStepUnits == 2) {
6229 if (std::isnan(
gSog))
6234 factor *= g_fNavAidRadarRingsStep;
6238 ll_gc_ll(
gLat,
gLon, 0, factor, &tlat, &tlon);
6241 double lpp = sqrt(pow((
double)(lGPSPoint.m_x - r.x), 2) +
6242 pow((
double)(lGPSPoint.m_y - r.y), 2));
6243 int pix_radius = (int)lpp;
6245 wxColor rangeringcolour =
6246 user_colors::GetDimColor(g_colourOwnshipRangeRingsColour);
6248 wxPen ppPen1(rangeringcolour, g_cog_predictor_width);
6251 dc.SetBrush(wxBrush(rangeringcolour, wxBRUSHSTYLE_TRANSPARENT));
6253 for (
int i = 1; i <= g_iNavAidRadarRingsNumberVisible; i++)
6254 dc.StrokeCircle(lGPSPoint.m_x, lGPSPoint.m_y, i * pix_radius);
6259void ChartCanvas::ResetGlGridFont() { GetglCanvas()->ResetGridFont(); }
6260bool ChartCanvas::CanAccelerateGlPanning() {
6261 return GetglCanvas()->CanAcceleratePanning();
6263void ChartCanvas::SetupGlCompression() { GetglCanvas()->SetupCompression(); }
6266void ChartCanvas::ResetGlGridFont() {}
6267bool ChartCanvas::CanAccelerateGlPanning() {
return false; }
6268void ChartCanvas::SetupGlCompression() {}
6271void ChartCanvas::ComputeShipScaleFactor(
6272 float icon_hdt,
int ownShipWidth,
int ownShipLength,
6273 wxPoint2DDouble &lShipMidPoint, wxPoint &GPSOffsetPixels,
6274 wxPoint2DDouble lGPSPoint,
float &scale_factor_x,
float &scale_factor_y) {
6275 float screenResolution = m_pix_per_mm;
6278 double ship_bow_lat, ship_bow_lon;
6279 ll_gc_ll(
gLat,
gLon, icon_hdt, g_n_ownship_length_meters / 1852.,
6280 &ship_bow_lat, &ship_bow_lon);
6281 wxPoint lShipBowPoint;
6282 wxPoint2DDouble b_point =
6286 float shipLength_px = sqrtf(powf((
float)(b_point.m_x - a_point.m_x), 2) +
6287 powf((
float)(b_point.m_y - a_point.m_y), 2));
6290 float shipLength_mm = shipLength_px / screenResolution;
6293 float ownship_min_mm = g_n_ownship_min_mm;
6294 ownship_min_mm = wxMax(ownship_min_mm, 1.0);
6297 float hdt_ant = icon_hdt + 180.;
6298 float dy = (g_n_ownship_length_meters / 2 - g_n_gps_antenna_offset_y) / 1852.;
6299 float dx = g_n_gps_antenna_offset_x / 1852.;
6300 if (g_n_gps_antenna_offset_y > g_n_ownship_length_meters / 2)
6308 if (shipLength_mm < ownship_min_mm) {
6309 dy /= shipLength_mm / ownship_min_mm;
6310 dx /= shipLength_mm / ownship_min_mm;
6313 double ship_mid_lat, ship_mid_lon, ship_mid_lat1, ship_mid_lon1;
6315 ll_gc_ll(
gLat,
gLon, hdt_ant, dy, &ship_mid_lat, &ship_mid_lon);
6316 ll_gc_ll(ship_mid_lat, ship_mid_lon, icon_hdt - 90., dx, &ship_mid_lat1,
6322 GPSOffsetPixels.x = lShipMidPoint.m_x - lGPSPoint.m_x;
6323 GPSOffsetPixels.y = lShipMidPoint.m_y - lGPSPoint.m_y;
6325 float scale_factor = shipLength_px / ownShipLength;
6328 float scale_factor_min = ownship_min_mm / (ownShipLength / screenResolution);
6331 scale_factor = wxMax(scale_factor, scale_factor_min);
6333 scale_factor_y = scale_factor;
6334 scale_factor_x = scale_factor_y * ((float)ownShipLength / ownShipWidth) /
6335 ((float)g_n_ownship_length_meters / g_n_ownship_beam_meters);
6338void ChartCanvas::ShipDraw(
ocpnDC &dc) {
6339 if (!GetVP().IsValid())
return;
6341 wxPoint GPSOffsetPixels(0, 0);
6342 wxPoint2DDouble lGPSPoint, lShipMidPoint;
6345 float pCog = std::isnan(
gCog) ? 0 :
gCog;
6346 float pSog = std::isnan(
gSog) ? 0 :
gSog;
6350 lShipMidPoint = lGPSPoint;
6354 float icon_hdt = pCog;
6355 if (!std::isnan(
gHdt)) icon_hdt =
gHdt;
6358 if (std::isnan(icon_hdt)) icon_hdt = 0.0;
6362 double osd_head_lat, osd_head_lon;
6363 wxPoint osd_head_point;
6365 ll_gc_ll(
gLat,
gLon, icon_hdt, pSog * 10. / 60., &osd_head_lat,
6370 float icon_rad = atan2f((
float)(osd_head_point.y - lShipMidPoint.m_y),
6371 (
float)(osd_head_point.x - lShipMidPoint.m_x));
6372 icon_rad += (float)PI;
6374 if (pSog < 0.2) icon_rad = ((icon_hdt + 90.) * PI / 180) + GetVP().
rotation;
6378 BoundingBox bb_screen(0, 0, GetVP().pix_width, GetVP().pix_height);
6382 if (bb_screen.PointInBox(lShipMidPoint, 20)) {
6383 if (GetVP().chart_scale >
6386 ShipDrawLargeScale(dc, lShipMidPoint);
6392 if (m_pos_image_user)
6393 pos_image = m_pos_image_user->Copy();
6394 else if (SHIP_NORMAL == m_ownship_state)
6395 pos_image = m_pos_image_red->Copy();
6396 if (SHIP_LOWACCURACY == m_ownship_state)
6397 pos_image = m_pos_image_yellow->Copy();
6398 else if (SHIP_NORMAL != m_ownship_state)
6399 pos_image = m_pos_image_grey->Copy();
6402 if (m_pos_image_user) {
6403 pos_image = m_pos_image_user->Copy();
6405 if (SHIP_LOWACCURACY == m_ownship_state)
6406 pos_image = m_pos_image_user_yellow->Copy();
6407 else if (SHIP_NORMAL != m_ownship_state)
6408 pos_image = m_pos_image_user_grey->Copy();
6411 img_height = pos_image.GetHeight();
6413 if (g_n_ownship_beam_meters > 0.0 && g_n_ownship_length_meters > 0.0 &&
6414 g_OwnShipIconType > 0)
6416 int ownShipWidth = 22;
6417 int ownShipLength = 84;
6418 if (g_OwnShipIconType == 1) {
6419 ownShipWidth = pos_image.GetWidth();
6420 ownShipLength = pos_image.GetHeight();
6423 float scale_factor_x, scale_factor_y;
6424 ComputeShipScaleFactor(icon_hdt, ownShipWidth, ownShipLength,
6425 lShipMidPoint, GPSOffsetPixels, lGPSPoint,
6426 scale_factor_x, scale_factor_y);
6428 if (g_OwnShipIconType == 1) {
6429 pos_image.Rescale(ownShipWidth * scale_factor_x,
6430 ownShipLength * scale_factor_y,
6431 wxIMAGE_QUALITY_HIGH);
6432 wxPoint rot_ctr(pos_image.GetWidth() / 2, pos_image.GetHeight() / 2);
6434 pos_image.Rotate(-(icon_rad - (PI / 2.)), rot_ctr,
true);
6437 for (
int ip = 0; ip < rot_image.GetWidth(); ip++)
6438 for (
int jp = 0; jp < rot_image.GetHeight(); jp++)
6439 if (rot_image.GetAlpha(ip, jp) > 64)
6440 rot_image.SetAlpha(ip, jp, 255);
6442 wxBitmap os_bm(rot_image);
6444 int w = os_bm.GetWidth();
6445 int h = os_bm.GetHeight();
6448 dc.DrawBitmap(os_bm, lShipMidPoint.m_x - w / 2,
6449 lShipMidPoint.m_y - h / 2,
true);
6452 dc.CalcBoundingBox(lShipMidPoint.m_x - w / 2,
6453 lShipMidPoint.m_y - h / 2);
6454 dc.CalcBoundingBox(lShipMidPoint.m_x - w / 2 + w,
6455 lShipMidPoint.m_y - h / 2 + h);
6458 else if (g_OwnShipIconType == 2) {
6459 wxPoint ownship_icon[10];
6461 for (
int i = 0; i < 10; i++) {
6463 float pxa = (float)(s_ownship_icon[j]);
6464 float pya = (float)(s_ownship_icon[j + 1]);
6465 pya *= scale_factor_y;
6466 pxa *= scale_factor_x;
6468 float px = (pxa * sinf(icon_rad)) + (pya * cosf(icon_rad));
6469 float py = (pya * sinf(icon_rad)) - (pxa * cosf(icon_rad));
6471 ownship_icon[i].x = (int)(px) + lShipMidPoint.m_x;
6472 ownship_icon[i].y = (int)(py) + lShipMidPoint.m_y;
6475 wxPen ppPen1(GetGlobalColor(
"UBLCK"), 1, wxPENSTYLE_SOLID);
6477 dc.SetBrush(wxBrush(ShipColor()));
6479 dc.StrokePolygon(6, &ownship_icon[0], 0, 0);
6482 dc.StrokeLine(ownship_icon[6].x, ownship_icon[6].y, ownship_icon[7].x,
6484 dc.StrokeLine(ownship_icon[8].x, ownship_icon[8].y, ownship_icon[9].x,
6488 img_height = ownShipLength * scale_factor_y;
6492 if (m_pos_image_user) circle_rad = 1;
6494 dc.SetPen(wxPen(GetGlobalColor(
"UBLCK"), 1));
6495 dc.SetBrush(wxBrush(GetGlobalColor(
"UIBCK")));
6496 dc.StrokeCircle(lGPSPoint.m_x, lGPSPoint.m_y, circle_rad);
6499 wxPoint rot_ctr(pos_image.GetWidth() / 2, pos_image.GetHeight() / 2);
6501 pos_image.Rotate(-(icon_rad - (PI / 2.)), rot_ctr,
true);
6504 for (
int ip = 0; ip < rot_image.GetWidth(); ip++)
6505 for (
int jp = 0; jp < rot_image.GetHeight(); jp++)
6506 if (rot_image.GetAlpha(ip, jp) > 64)
6507 rot_image.SetAlpha(ip, jp, 255);
6509 wxBitmap os_bm(rot_image);
6511 if (g_ShipScaleFactorExp > 1) {
6512 wxImage scaled_image = os_bm.ConvertToImage();
6513 double factor = (log(g_ShipScaleFactorExp) + 1.0) *
6515 os_bm = wxBitmap(scaled_image.Scale(scaled_image.GetWidth() * factor,
6516 scaled_image.GetHeight() * factor,
6517 wxIMAGE_QUALITY_HIGH));
6519 int w = os_bm.GetWidth();
6520 int h = os_bm.GetHeight();
6523 dc.DrawBitmap(os_bm, lShipMidPoint.m_x - w / 2,
6524 lShipMidPoint.m_y - h / 2,
true);
6528 if (m_pos_image_user) circle_rad = 1;
6530 dc.SetPen(wxPen(GetGlobalColor(
"UBLCK"), 1));
6531 dc.SetBrush(wxBrush(GetGlobalColor(
"UIBCK")));
6532 dc.StrokeCircle(lShipMidPoint.m_x, lShipMidPoint.m_y, circle_rad);
6535 dc.CalcBoundingBox(lShipMidPoint.m_x - w / 2,
6536 lShipMidPoint.m_y - h / 2);
6537 dc.CalcBoundingBox(lShipMidPoint.m_x - w / 2 + w,
6538 lShipMidPoint.m_y - h / 2 + h);
6543 ShipIndicatorsDraw(dc, img_height, GPSOffsetPixels, lGPSPoint);
6556void CalcGridSpacing(
float view_scale_ppm,
float &MajorSpacing,
6557 float &MinorSpacing) {
6562 const float lltab[][3] = {{0.0f, 90.0f, 30.0f},
6563 {.000001f, 45.0f, 15.0f},
6564 {.0002f, 30.0f, 10.0f},
6565 {.0003f, 10.0f, 2.0f},
6566 {.0008f, 5.0f, 1.0f},
6567 {.001f, 2.0f, 30.0f / 60.0f},
6568 {.003f, 1.0f, 20.0f / 60.0f},
6569 {.006f, 0.5f, 10.0f / 60.0f},
6570 {.03f, 15.0f / 60.0f, 5.0f / 60.0f},
6571 {.01f, 10.0f / 60.0f, 2.0f / 60.0f},
6572 {.06f, 5.0f / 60.0f, 1.0f / 60.0f},
6573 {.1f, 2.0f / 60.0f, 1.0f / 60.0f},
6574 {.4f, 1.0f / 60.0f, 0.5f / 60.0f},
6575 {.6f, 0.5f / 60.0f, 0.1f / 60.0f},
6576 {1.0f, 0.2f / 60.0f, 0.1f / 60.0f},
6577 {1e10f, 0.1f / 60.0f, 0.05f / 60.0f}};
6580 for (tabi = 0; tabi < ((
sizeof lltab) / (
sizeof *lltab)) - 1; tabi++)
6581 if (view_scale_ppm < lltab[tabi][0])
break;
6582 MajorSpacing = lltab[tabi][1];
6583 MinorSpacing = lltab[tabi][2];
6597wxString CalcGridText(
float latlon,
float spacing,
bool bPostfix) {
6598 int deg = (int)fabs(latlon);
6599 float min = fabs((fabs(latlon) - deg) * 60.0);
6609 }
else if (latlon < 0.0) {
6621 if (spacing >= 1.0) {
6622 ret.Printf(
"%3d%c %c", deg, 0x00b0, postfix);
6623 }
else if (spacing >= (1.0 / 60.0)) {
6624 ret.Printf(
"%3d%c%02.0f %c", deg, 0x00b0, min, postfix);
6626 ret.Printf(
"%3d%c%02.2f %c", deg, 0x00b0, min, postfix);
6643void ChartCanvas::GridDraw(
ocpnDC &dc) {
6644 if (!(m_bDisplayGrid && (fabs(GetVP().rotation) < 1e-5)))
return;
6646 double nlat, elon, slat, wlon;
6649 float gridlatMajor, gridlatMinor, gridlonMajor, gridlonMinor;
6651 wxPen GridPen(GetGlobalColor(
"SNDG1"), 1, wxPENSTYLE_SOLID);
6653 if (!m_pgridFont) SetupGridFont();
6654 dc.SetFont(*m_pgridFont);
6655 dc.SetTextForeground(GetGlobalColor(
"SNDG1"));
6658 h = m_canvas_height;
6669 dlon = dlon + 360.0;
6672 CalcGridSpacing(GetVP().view_scale_ppm, gridlatMajor, gridlatMinor);
6675 lat = ceil(slat / gridlatMajor) * gridlatMajor;
6678 while (lat < nlat) {
6681 CalcGridText(lat, gridlatMajor,
true);
6683 dc.
DrawLine(0, r.y, w, r.y,
false);
6684 dc.DrawText(st, 0, r.y);
6685 lat = lat + gridlatMajor;
6687 if (fabs(lat - wxRound(lat)) < 1e-5) lat = wxRound(lat);
6691 lat = ceil(slat / gridlatMinor) * gridlatMinor;
6694 while (lat < nlat) {
6697 dc.
DrawLine(0, r.y, 10, r.y,
false);
6698 dc.
DrawLine(w - 10, r.y, w, r.y,
false);
6699 lat = lat + gridlatMinor;
6703 CalcGridSpacing(GetVP().view_scale_ppm, gridlonMajor, gridlonMinor);
6706 lon = ceil(wlon / gridlonMajor) * gridlonMajor;
6709 for (
int i = 0, itermax = (
int)(dlon / gridlonMajor); i <= itermax; i++) {
6711 wxString st = CalcGridText(lon, gridlonMajor,
false);
6713 dc.
DrawLine(r.x, 0, r.x, h,
false);
6714 dc.DrawText(st, r.x, 0);
6715 lon = lon + gridlonMajor;
6720 if (fabs(lon - wxRound(lon)) < 1e-5) lon = wxRound(lon);
6724 lon = ceil(wlon / gridlonMinor) * gridlonMinor;
6726 for (
int i = 0, itermax = (
int)(dlon / gridlonMinor); i <= itermax; i++) {
6729 dc.
DrawLine(r.x, 0, r.x, 10,
false);
6730 dc.
DrawLine(r.x, h - 10, r.x, h,
false);
6731 lon = lon + gridlonMinor;
6738void ChartCanvas::ScaleBarDraw(
ocpnDC &dc) {
6740 double blat, blon, tlat, tlon;
6743 int x_origin = m_bDisplayGrid ? 60 : 20;
6744 int y_origin = m_canvas_height - 50;
6750 if (GetVP().chart_scale > 80000)
6754 pen1 = wxPen(GetGlobalColor(
"SNDG2"), 3, wxPENSTYLE_SOLID);
6755 pen2 = wxPen(GetGlobalColor(
"SNDG1"), 3, wxPENSTYLE_SOLID);
6760 pen1 = wxPen(GetGlobalColor(
"SCLBR"), 3, wxPENSTYLE_SOLID);
6761 pen2 = wxPen(GetGlobalColor(
"CHGRD"), 3, wxPENSTYLE_SOLID);
6765 double rotation = -VPoint.
rotation;
6767 ll_gc_ll(blat, blon, rotation * 180 / PI, dist, &tlat, &tlon);
6769 int l1 = (y_origin - r.y) / count;
6771 for (
int i = 0; i < count; i++) {
6778 dc.
DrawLine(x_origin, y_origin - y, x_origin, y_origin - (y + l1));
6781 double blat, blon, tlat, tlon;
6788 int y_origin = m_canvas_height - chartbar_height - 5;
6792 m_canvas_height / GetContentScaleFactor() - chartbar_height - 5;
6799 ll_gc_ll_reverse(blat, blon, tlat, tlon, 0, &d);
6804 (
unit == DISTANCE_KM ||
unit == DISTANCE_MI ||
unit == DISTANCE_NMI))
6805 unit = (
unit == DISTANCE_MI) ? DISTANCE_FT : DISTANCE_M;
6809 float places = floor(logdist), rem = logdist - places;
6810 dist = pow(10, places);
6817 wxString s = wxString::Format(
"%g ", dist) + getUsrDistanceUnit(
unit);
6818 wxPen pen1 = wxPen(GetGlobalColor(
"UBLCK"), 3, wxPENSTYLE_SOLID);
6819 double rotation = -VPoint.
rotation;
6825 int l1 = r.x - x_origin;
6827 m_scaleBarRect = wxRect(x_origin, y_origin - 12, l1,
6832 dc.
DrawLine(x_origin, y_origin, x_origin, y_origin - 12);
6833 dc.
DrawLine(x_origin, y_origin, x_origin + l1, y_origin);
6834 dc.
DrawLine(x_origin + l1, y_origin, x_origin + l1, y_origin - 12);
6836 if (!m_pgridFont) SetupGridFont();
6837 dc.SetFont(*m_pgridFont);
6838 dc.SetTextForeground(GetGlobalColor(
"UBLCK"));
6840 dc.GetTextExtent(s, &w, &h);
6846 dc.DrawText(s, x_origin + l1 / 2 - w / 2, y_origin - h - 1);
6850void ChartCanvas::JaggyCircle(
ocpnDC &dc, wxPen pen,
int x,
int y,
int radius) {
6855 double ra_max = 40.;
6857 wxPen pen_save = dc.GetPen();
6859 wxDateTime now = wxDateTime::Now();
6865 x0 = x1 = x + radius;
6870 while (angle < 360.) {
6871 double da = da_min + (((double)rand() / RAND_MAX) * (da_max - da_min));
6874 if (angle > 360.) angle = 360.;
6876 double ra = ra_min + (((double)rand() / RAND_MAX) * (ra_max - ra_min));
6884 x1 = (int)(x + cos(angle * PI / 180.) * r);
6885 y1 = (int)(y + sin(angle * PI / 180.) * r);
6895 dc.
DrawLine(x + radius, y, x1, y1);
6897 dc.SetPen(pen_save);
6900static bool bAnchorSoundPlaying =
false;
6902static void onAnchorSoundFinished(
void *ptr) {
6903 o_sound::g_anchorwatch_sound->UnLoad();
6904 bAnchorSoundPlaying =
false;
6907void ChartCanvas::AlertDraw(
ocpnDC &dc) {
6908 using namespace o_sound;
6910 bool play_sound =
false;
6912 if (AnchorAlertOn1) {
6913 wxPoint TargetPoint;
6916 JaggyCircle(dc, wxPen(GetGlobalColor(
"URED"), 2), TargetPoint.x,
6917 TargetPoint.y, 100);
6921 AnchorAlertOn1 =
false;
6924 if (AnchorAlertOn2) {
6925 wxPoint TargetPoint;
6928 JaggyCircle(dc, wxPen(GetGlobalColor(
"URED"), 2), TargetPoint.x,
6929 TargetPoint.y, 100);
6933 AnchorAlertOn2 =
false;
6936 if (!bAnchorSoundPlaying) {
6937 auto cmd_sound =
dynamic_cast<SystemCmdSound *
>(g_anchorwatch_sound);
6938 if (cmd_sound) cmd_sound->SetCmd(g_CmdSoundString.mb_str(wxConvUTF8));
6939 g_anchorwatch_sound->Load(g_anchorwatch_sound_file);
6940 if (g_anchorwatch_sound->IsOk()) {
6941 bAnchorSoundPlaying =
true;
6942 g_anchorwatch_sound->SetFinishedCallback(onAnchorSoundFinished, NULL);
6943 g_anchorwatch_sound->Play();
6949void ChartCanvas::UpdateShips() {
6952 wxClientDC dc(
this);
6955 if (!dc.IsOk() || dc.GetSize().x < 1 || dc.GetSize().y < 1)
return;
6957 wxBitmap test_bitmap(dc.GetSize().x, dc.GetSize().y);
6958 if (!test_bitmap.IsOk())
return;
6960 wxMemoryDC temp_dc(test_bitmap);
6962 temp_dc.ResetBoundingBox();
6963 temp_dc.DestroyClippingRegion();
6964 temp_dc.SetClippingRegion(0, 0, dc.GetSize().x, dc.GetSize().y);
6975 ocpndc.CalcBoundingBox(px.x, px.y);
6980 wxRect(temp_dc.MinX(), temp_dc.MinY(), temp_dc.MaxX() - temp_dc.MinX(),
6981 temp_dc.MaxY() - temp_dc.MinY());
6983 wxRect own_ship_update_rect = ship_draw_rect;
6985 if (!own_ship_update_rect.IsEmpty()) {
6988 own_ship_update_rect.Union(ship_draw_last_rect);
6989 own_ship_update_rect.Inflate(2);
6992 if (!own_ship_update_rect.IsEmpty()) RefreshRect(own_ship_update_rect,
false);
6994 ship_draw_last_rect = ship_draw_rect;
6996 temp_dc.SelectObject(wxNullBitmap);
6999void ChartCanvas::UpdateAlerts() {
7004 wxClientDC dc(
this);
7008 dc.GetSize(&sx, &sy);
7011 wxBitmap test_bitmap(sx, sy, -1);
7015 temp_dc.SelectObject(test_bitmap);
7017 temp_dc.ResetBoundingBox();
7018 temp_dc.DestroyClippingRegion();
7019 temp_dc.SetClippingRegion(wxRect(0, 0, sx, sy));
7026 wxRect alert_rect(temp_dc.MinX(), temp_dc.MinY(),
7027 temp_dc.MaxX() - temp_dc.MinX(),
7028 temp_dc.MaxY() - temp_dc.MinY());
7030 if (!alert_rect.IsEmpty())
7031 alert_rect.Inflate(2);
7033 if (!alert_rect.IsEmpty() || !alert_draw_rect.IsEmpty()) {
7036 wxRect alert_update_rect = alert_draw_rect;
7037 alert_update_rect.Union(alert_rect);
7040 RefreshRect(alert_update_rect,
false);
7044 alert_draw_rect = alert_rect;
7046 temp_dc.SelectObject(wxNullBitmap);
7049void ChartCanvas::UpdateAIS() {
7055 wxClientDC dc(
this);
7059 dc.GetSize(&sx, &sy);
7067 if (
g_pAIS->GetTargetList().size() > 10) {
7068 ais_rect = wxRect(0, 0, sx, sy);
7071 wxBitmap test_bitmap(sx, sy, -1);
7075 temp_dc.SelectObject(test_bitmap);
7077 temp_dc.ResetBoundingBox();
7078 temp_dc.DestroyClippingRegion();
7079 temp_dc.SetClippingRegion(wxRect(0, 0, sx, sy));
7083 AISDraw(ocpndc, GetVP(),
this);
7084 AISDrawAreaNotices(ocpndc, GetVP(),
this);
7088 wxRect(temp_dc.MinX(), temp_dc.MinY(), temp_dc.MaxX() - temp_dc.MinX(),
7089 temp_dc.MaxY() - temp_dc.MinY());
7091 if (!ais_rect.IsEmpty())
7092 ais_rect.Inflate(2);
7094 temp_dc.SelectObject(wxNullBitmap);
7097 if (!ais_rect.IsEmpty() || !ais_draw_rect.IsEmpty()) {
7100 wxRect ais_update_rect = ais_draw_rect;
7101 ais_update_rect.Union(ais_rect);
7104 RefreshRect(ais_update_rect,
false);
7108 ais_draw_rect = ais_rect;
7111void ChartCanvas::ToggleCPAWarn() {
7112 if (!g_AisFirstTimeUse) g_bCPAWarn = !g_bCPAWarn;
7118 g_bTCPA_Max =
false;
7122 if (STAT_FIELD_SCALE >= 4 && top_frame::Get()->GetStatusBar()) {
7123 top_frame::Get()->SetStatusText(_(
"CPA alarm ") + mess, STAT_FIELD_SCALE);
7125 if (!g_AisFirstTimeUse) {
7126 OCPNMessageBox(
this, _(
"CPA Alarm is switched") +
" " + mess.MakeLower(),
7127 _(
"CPA") +
" " + mess, 4, 4);
7132void ChartCanvas::OnActivate(wxActivateEvent &event) { ReloadVP(); }
7134void ChartCanvas::OnSize(wxSizeEvent &event) {
7135 if ((event.m_size.GetWidth() < 1) || (event.m_size.GetHeight() < 1))
return;
7137 GetClientSize(&m_canvas_width, &m_canvas_height);
7141 m_displayScale = GetContentScaleFactor();
7145 m_canvas_width *= m_displayScale;
7146 m_canvas_height *= m_displayScale;
7159 m_absolute_min_scale_ppm =
7161 (1.2 * WGS84_semimajor_axis_meters * PI);
7164 top_frame::Get()->ProcessCanvasResize();
7174 SetMUIBarPosition();
7175 UpdateFollowButtonState();
7176 m_muiBar->SetCanvasENCAvailable(m_bENCGroup);
7180 xr_margin = m_canvas_width * 95 / 100;
7181 xl_margin = m_canvas_width * 5 / 100;
7182 yt_margin = m_canvas_height * 5 / 100;
7183 yb_margin = m_canvas_height * 95 / 100;
7186 m_pQuilt->SetQuiltParameters(m_canvas_scale_factor, m_canvas_width);
7191 m_brepaint_piano =
true;
7194 m_dc_route.SelectObject(wxNullBitmap);
7197 m_dc_route.SelectObject(*proute_bm);
7211 m_glcc->OnSize(event);
7220void ChartCanvas::ProcessNewGUIScale() {
7228void ChartCanvas::CreateMUIBar() {
7229 if (g_useMUI && !m_muiBar) {
7230 m_muiBar =
new MUIBar(
this, wxHORIZONTAL, g_toolbar_scalefactor);
7231 m_muiBar->SetColorScheme(m_cs);
7232 m_muiBarHOSize = m_muiBar->m_size;
7240 SetMUIBarPosition();
7241 UpdateFollowButtonState();
7242 m_muiBar->UpdateDynamicValues();
7243 m_muiBar->SetCanvasENCAvailable(m_bENCGroup);
7247void ChartCanvas::SetMUIBarPosition() {
7251 int pianoWidth = GetClientSize().x * 0.6f;
7256 if ((m_muiBar->GetOrientation() == wxHORIZONTAL)) {
7257 if (m_muiBarHOSize.x > (GetClientSize().x - pianoWidth)) {
7259 m_muiBar =
new MUIBar(
this, wxVERTICAL, g_toolbar_scalefactor);
7260 m_muiBar->SetColorScheme(m_cs);
7264 if ((m_muiBar->GetOrientation() == wxVERTICAL)) {
7265 if (m_muiBarHOSize.x < (GetClientSize().x - pianoWidth)) {
7267 m_muiBar =
new MUIBar(
this, wxHORIZONTAL, g_toolbar_scalefactor);
7268 m_muiBar->SetColorScheme(m_cs);
7272 m_muiBar->SetBestPosition();
7276void ChartCanvas::DestroyMuiBar() {
7283void ChartCanvas::ShowCompositeInfoWindow(
7284 int x,
int n_charts,
int scale,
const std::vector<int> &index_vector) {
7286 if (NULL == m_pCIWin) {
7291 if (!m_pCIWin->IsShown() || (m_pCIWin->chart_scale !=
scale)) {
7294 s = _(
"Composite of ");
7297 s1.Printf(
"%d ", n_charts);
7305 s1.Printf(_(
"Chart scale"));
7308 s2.Printf(
"1:%d\n",
scale);
7312 s1 = _(
"Zoom in for more information");
7316 int char_width = s1.Length();
7317 int char_height = 3;
7319 if (g_bChartBarEx) {
7322 for (
int i : index_vector) {
7324 wxString path = cte.GetFullSystemPath();
7328 char_width = wxMax(char_width, path.Length());
7329 if (j++ >= 9)
break;
7332 s +=
" .\n .\n .\n";
7341 m_pCIWin->SetString(s);
7343 m_pCIWin->FitToChars(char_width, char_height);
7346 p.x = x / GetContentScaleFactor();
7347 if ((p.x + m_pCIWin->GetWinSize().x) >
7348 (m_canvas_width / GetContentScaleFactor()))
7349 p.x = ((m_canvas_width / GetContentScaleFactor()) -
7350 m_pCIWin->GetWinSize().x) /
7353 p.y = (m_canvas_height - m_Piano->GetHeight()) / GetContentScaleFactor() -
7354 4 - m_pCIWin->GetWinSize().y;
7356 m_pCIWin->dbIndex = 0;
7357 m_pCIWin->chart_scale = 0;
7358 m_pCIWin->SetPosition(p);
7359 m_pCIWin->SetBitmap();
7360 m_pCIWin->Refresh();
7364 HideChartInfoWindow();
7368void ChartCanvas::ShowChartInfoWindow(
int x,
int dbIndex) {
7370 if (NULL == m_pCIWin) {
7375 if (!m_pCIWin->IsShown() || (m_pCIWin->dbIndex != dbIndex)) {
7384 dbIndex, FULL_INIT);
7386 int char_width, char_height;
7387 s =
ChartData->GetFullChartInfo(pc, dbIndex, &char_width, &char_height);
7388 if (pc)
ChartData->UnLockCacheChart(dbIndex);
7390 m_pCIWin->SetString(s);
7391 m_pCIWin->FitToChars(char_width, char_height);
7394 p.x = x / GetContentScaleFactor();
7395 if ((p.x + m_pCIWin->GetWinSize().x) >
7396 (m_canvas_width / GetContentScaleFactor()))
7397 p.x = ((m_canvas_width / GetContentScaleFactor()) -
7398 m_pCIWin->GetWinSize().x) /
7401 p.y = (m_canvas_height - m_Piano->GetHeight()) / GetContentScaleFactor() -
7402 4 - m_pCIWin->GetWinSize().y;
7404 m_pCIWin->dbIndex = dbIndex;
7405 m_pCIWin->SetPosition(p);
7406 m_pCIWin->SetBitmap();
7407 m_pCIWin->Refresh();
7411 HideChartInfoWindow();
7415void ChartCanvas::HideChartInfoWindow() {
7418 m_pCIWin->Destroy();
7422 androidForceFullRepaint();
7427void ChartCanvas::PanTimerEvent(wxTimerEvent &event) {
7428 wxMouseEvent ev(wxEVT_MOTION);
7431 ev.m_leftDown = mouse_leftisdown;
7433 wxEvtHandler *evthp = GetEventHandler();
7435 ::wxPostEvent(evthp, ev);
7438void ChartCanvas::MovementTimerEvent(wxTimerEvent &) {
7439 if ((m_panx_target_final - m_panx_target_now) ||
7440 (m_pany_target_final - m_pany_target_now)) {
7441 DoTimedMovementTarget();
7446void ChartCanvas::MovementStopTimerEvent(wxTimerEvent &) { StopMovement(); }
7448bool ChartCanvas::CheckEdgePan(
int x,
int y,
bool bdragging,
int margin,
7450 if (m_disable_edge_pan)
return false;
7453 int pan_margin = m_canvas_width * margin / 100;
7454 int pan_timer_set = 200;
7455 double pan_delta = GetVP().
pix_width * delta / 100;
7459 if (x > m_canvas_width - pan_margin) {
7464 else if (x < pan_margin) {
7469 if (y < pan_margin) {
7474 else if (y > m_canvas_height - pan_margin) {
7483 wxMouseState state = ::wxGetMouseState();
7484#if wxCHECK_VERSION(3, 0, 0)
7485 if (!state.LeftIsDown())
7487 if (!state.LeftDown())
7492 if ((bft) && !pPanTimer->IsRunning()) {
7494 pPanTimer->Start(pan_timer_set, wxTIMER_ONE_SHOT);
7500 if ((!bft) && pPanTimer->IsRunning()) {
7510void ChartCanvas::FindRoutePointsAtCursor(
float selectRadius,
7511 bool setBeingEdited) {
7512 m_lastRoutePointEditTarget = m_pRoutePointEditTarget;
7513 m_pRoutePointEditTarget = NULL;
7514 m_pFoundPoint = NULL;
7517 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
7518 SelectableItemList SelList =
pSelect->FindSelectionList(
7528 bool brp_viz =
false;
7529 if (m_pEditRouteArray) {
7530 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++) {
7531 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
7532 if (pr->IsVisible()) {
7538 brp_viz = frp->IsVisible();
7542 if (m_pEditRouteArray)
7544 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++) {
7545 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
7548 m_bRouteEditing = setBeingEdited;
7551 frp->m_bRPIsBeingEdited = setBeingEdited;
7552 m_bMarkEditing = setBeingEdited;
7555 m_pRoutePointEditTarget = frp;
7556 m_pFoundPoint = pFind;
7561std::shared_ptr<HostApi121::PiPointContext>
7562ChartCanvas::GetCanvasContextAtPoint(
int x,
int y) {
7576 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
7578 pFindAIS =
pSelectAIS->FindSelection(ctx, slat, slon, SELTYPE_AISTARGET);
7579 pFindRP =
pSelect->FindSelection(ctx, slat, slon, SELTYPE_ROUTEPOINT);
7580 pFindRouteSeg =
pSelect->FindSelection(ctx, slat, slon, SELTYPE_ROUTESEGMENT);
7581 pFindTrackSeg =
pSelect->FindSelection(ctx, slat, slon, SELTYPE_TRACKSEGMENT);
7585 pSelectTC->FindSelection(ctx, slat, slon, SELTYPE_CURRENTPOINT);
7588 pFindTide =
pSelectTC->FindSelection(ctx, slat, slon, SELTYPE_TIDEPOINT);
7593 int FoundAIS_MMSI = 0;
7595 FoundAIS_MMSI = pFindAIS->GetUserData();
7598 if (
g_pAIS->Get_Target_Data_From_MMSI(FoundAIS_MMSI))
7599 seltype |= SELTYPE_AISTARGET;
7605 Route *SelectedRoute = NULL;
7611 Route *pSelectedActiveRoute = NULL;
7612 Route *pSelectedVizRoute = NULL;
7615 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
7616 SelectableItemList SelList =
7617 pSelect->FindSelectionList(ctx, slat, slon, SELTYPE_ROUTEPOINT);
7625 bool brp_viz =
false;
7627 for (
unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
7629 if (pr->IsVisible()) {
7634 if (!brp_viz && prp->IsShared())
7636 brp_viz = prp->IsVisible();
7639 brp_viz = prp->IsVisible();
7641 if ((NULL == pFirstVizPoint) && brp_viz) pFirstVizPoint = prp;
7647 for (
unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
7650 pSelectedActiveRoute = pr;
7651 pFoundActiveRoutePoint = prp;
7656 if (NULL == pSelectedVizRoute) {
7657 for (
unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
7659 if (pr->IsVisible()) {
7660 pSelectedVizRoute = pr;
7661 pFoundVizRoutePoint = prp;
7667 delete proute_array;
7672 if (pFoundActiveRoutePoint) {
7673 FoundRoutePoint = pFoundActiveRoutePoint;
7674 SelectedRoute = pSelectedActiveRoute;
7675 }
else if (pFoundVizRoutePoint) {
7676 FoundRoutePoint = pFoundVizRoutePoint;
7677 SelectedRoute = pSelectedVizRoute;
7680 FoundRoutePoint = pFirstVizPoint;
7682 if (SelectedRoute) {
7683 if (SelectedRoute->IsVisible()) seltype |= SELTYPE_ROUTEPOINT;
7684 }
else if (FoundRoutePoint) {
7685 seltype |= SELTYPE_MARKPOINT;
7690 if (m_pFoundRoutePoint) {
7694 .CalculateDCRect(m_dc_route,
this, &wp_rect);
7695 RefreshRect(wp_rect,
true);
7704 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
7705 SelectableItemList SelList =
7706 pSelect->FindSelectionList(ctx, slat, slon, SELTYPE_ROUTESEGMENT);
7708 if (NULL == SelectedRoute)
7713 if (pr->IsVisible()) {
7720 if (SelectedRoute) {
7721 if (NULL == FoundRoutePoint)
7722 FoundRoutePoint = (
RoutePoint *)pFindRouteSeg->m_pData1;
7725 seltype |= SELTYPE_ROUTESEGMENT;
7729 if (pFindTrackSeg) {
7730 m_pSelectedTrack = NULL;
7731 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
7732 SelectableItemList SelList =
7733 pSelect->FindSelectionList(ctx, slat, slon, SELTYPE_TRACKSEGMENT);
7738 if (pt->IsVisible()) {
7739 m_pSelectedTrack = pt;
7743 if (m_pSelectedTrack) seltype |= SELTYPE_TRACKSEGMENT;
7746 if (0 == seltype) seltype |= SELTYPE_UNKNOWN;
7749 auto rstruct = std::make_shared<HostApi121::PiPointContext>();
7750 rstruct->object_type = HostApi121::PiContextObjectType::kObjectChart;
7751 rstruct->object_ident =
"";
7753 if (seltype == SELTYPE_AISTARGET) {
7754 rstruct->object_type = HostApi121::PiContextObjectType::kObjectAisTarget;
7756 val.Printf(
"%d", FoundAIS_MMSI);
7757 rstruct->object_ident = val.ToStdString();
7758 }
else if (seltype & SELTYPE_MARKPOINT) {
7759 if (FoundRoutePoint) {
7760 rstruct->object_type = HostApi121::PiContextObjectType::kObjectRoutepoint;
7761 rstruct->object_ident = FoundRoutePoint->
m_GUID.ToStdString();
7763 }
else if (seltype & SELTYPE_ROUTESEGMENT) {
7764 if (SelectedRoute) {
7765 rstruct->object_type =
7766 HostApi121::PiContextObjectType::kObjectRoutesegment;
7767 rstruct->object_ident = SelectedRoute->
m_GUID.ToStdString();
7769 }
else if (seltype & SELTYPE_TRACKSEGMENT) {
7770 if (m_pSelectedTrack) {
7771 rstruct->object_type =
7772 HostApi121::PiContextObjectType::kObjectTracksegment;
7773 rstruct->object_ident = m_pSelectedTrack->m_GUID.ToStdString();
7780void ChartCanvas::MouseTimedEvent(wxTimerEvent &event) {
7781 if (singleClickEventIsValid) {
7782 const bool wasReplaying = m_isReplayingClickEvent;
7783 m_isReplayingClickEvent =
true;
7784 MouseEvent(singleClickEvent);
7785 m_isReplayingClickEvent = wasReplaying;
7788 singleClickEventIsValid =
false;
7789 m_DoubleClickTimer->Stop();
7794bool ChartCanvas::MouseEventOverlayWindows(wxMouseEvent &event) {
7795 if (!m_bChartDragging && !m_bDrawingRoute) {
7800 if (m_Compass && m_Compass->IsShown()) {
7802 bool isInCompass = logicalRect.Contains(event.GetPosition());
7803 if (isInCompass || m_mouseWasInCompass) {
7804 if (m_Compass->MouseEvent(event)) {
7805 cursor_region = CENTER;
7806 if (!g_btouch) SetCanvasCursor(event);
7807 m_mouseWasInCompass = isInCompass;
7811 m_mouseWasInCompass = isInCompass;
7814 if (m_notification_button && m_notification_button->IsShown()) {
7816 bool isinButton = logicalRect.Contains(event.GetPosition());
7818 SetCursor(*pCursorArrow);
7819 if (event.LeftDown()) HandleNotificationMouseClick();
7824 if (MouseEventToolbar(event))
return true;
7826 if (MouseEventChartBar(event))
return true;
7828 if (MouseEventMUIBar(event))
return true;
7830 if (MouseEventIENCBar(event))
return true;
7835void ChartCanvas::HandleNotificationMouseClick() {
7836 if (!m_NotificationsList) {
7840 m_NotificationsList->RecalculateSize();
7841 m_NotificationsList->Hide();
7844 if (m_NotificationsList->IsShown()) {
7845 m_NotificationsList->Hide();
7847 m_NotificationsList->RecalculateSize();
7848 m_NotificationsList->ReloadNotificationList();
7849 m_NotificationsList->Show();
7852bool ChartCanvas::MouseEventChartBar(wxMouseEvent &event) {
7853 if (!g_bShowChartBar)
return false;
7855 if (!m_Piano->MouseEvent(event))
return false;
7857 cursor_region = CENTER;
7858 if (!g_btouch) SetCanvasCursor(event);
7862bool ChartCanvas::MouseEventToolbar(wxMouseEvent &event) {
7863 if (!IsPrimaryCanvas())
return false;
7872 cursor_region = CENTER;
7873 if (!g_btouch) SetCanvasCursor(event);
7877bool ChartCanvas::MouseEventIENCBar(wxMouseEvent &event) {
7878 if (!IsPrimaryCanvas())
return false;
7891bool ChartCanvas::MouseEventMUIBar(wxMouseEvent &event) {
7893 if (!m_muiBar->MouseEvent(event))
return false;
7896 cursor_region = CENTER;
7897 if (!g_btouch) SetCanvasCursor(event);
7909 event.GetPosition(&x, &y);
7911 x *= m_displayScale;
7912 y *= m_displayScale;
7914 m_MouseDragging =
event.Dragging();
7920 if (event.Dragging()) {
7921 if ((x == mouse_x) && (y == mouse_y))
return true;
7927 mouse_leftisdown =
event.LeftDown();
7931 cursor_region = CENTER;
7935 if (m_Compass && m_Compass->IsShown() &&
7936 m_Compass->
GetRect().Contains(event.GetPosition())) {
7937 cursor_region = CENTER;
7938 }
else if (x > xr_margin) {
7939 cursor_region = MID_RIGHT;
7940 }
else if (x < xl_margin) {
7941 cursor_region = MID_LEFT;
7942 }
else if (y > yb_margin - chartbar_height &&
7943 y < m_canvas_height - chartbar_height) {
7944 cursor_region = MID_TOP;
7945 }
else if (y < yt_margin) {
7946 cursor_region = MID_BOT;
7948 cursor_region = CENTER;
7951 if (!g_btouch) SetCanvasCursor(event);
7955 leftIsDown =
event.LeftDown();
7958 if (event.LeftDown()) {
7959 if (g_bShowMenuBar ==
false && g_bTempShowMenuBar ==
true) {
7962 g_bTempShowMenuBar =
false;
7963 top_frame::Get()->ApplyGlobalSettings(
false);
7971 if (event.ControlDown()) m_modkeys |= wxMOD_CONTROL;
7972 if (event.AltDown()) m_modkeys |= wxMOD_ALT;
7977 if (!m_isReplayingClickEvent) {
7978 if (event.ButtonDown() && !HasCapture()) CaptureMouse();
7979 if (event.ButtonUp() && HasCapture()) ReleaseMouse();
7983 event.SetEventObject(
this);
7984 if (SendMouseEventToPlugins(event))
7991 if (g_btouch && !m_inPinch && m_bChartDragging && event.LeftUp()) {
7992 StartChartDragInertia();
7995 if (!g_btouch && b_handle_dclick && event.LeftUp() &&
7996 !singleClickEventIsValid) {
7998 if (m_DoubleClickTimer->IsRunning()) {
7999 m_DoubleClickTimer->Stop();
8004 m_DoubleClickTimer->Start(350, wxTIMER_ONE_SHOT);
8005 singleClickEvent = event;
8006 singleClickEventIsValid =
true;
8015 if (event.LeftDown() || event.LeftUp() || event.Dragging()) {
8016 if (g_click_stop > 0) {
8024 if (GetUpMode() == COURSE_UP_MODE) {
8025 m_b_rot_hidef =
false;
8026 pRotDefTimer->Start(500, wxTIMER_ONE_SHOT);
8028 pRotDefTimer->Stop();
8031 bool bRoll = !g_btouch;
8036 if ((m_pRouteRolloverWin && m_pRouteRolloverWin->IsActive()) ||
8037 (m_pTrackRolloverWin && m_pTrackRolloverWin->IsActive()) ||
8038 (m_pAISRolloverWin && m_pAISRolloverWin->IsActive()))
8039 m_RolloverPopupTimer.Start(
8043 m_RolloverPopupTimer.Start(m_rollover_popup_timer_msec, wxTIMER_ONE_SHOT);
8047 pCurTrackTimer->Start(m_curtrack_timer_msec, wxTIMER_ONE_SHOT);
8056#if !defined(__WXGTK__) && !defined(__WXQT__)
8064 if ((x >= 0) && (y >= 0))
8069 if ((m_bMeasure_Active && (m_nMeasureState >= 2)) || (m_routeState > 1)) {
8070 wxPoint p = ClientToScreen(wxPoint(x, y));
8076 if (m_routeState >= 2) {
8079 m_bDrawingRoute =
true;
8081 if (!g_btouch) CheckEdgePan(x, y, event.Dragging(), 5, 2);
8086 if (m_bMeasure_Active && (m_nMeasureState >= 2)) {
8089 m_bDrawingRoute =
true;
8091 if (!g_btouch) CheckEdgePan(x, y, event.Dragging(), 5, 2);
8104#if defined(__WXMAC__) || defined(__ANDROID__)
8108 wxClientDC cdc(GetParent());
8120 if (m_pSelectedRoute) {
8122 m_pSelectedRoute->DeSelectRoute();
8124 if (g_bopengl && m_glcc) {
8129 RouteGui(*m_pSelectedRoute).Draw(dc,
this, GetVP().GetBBox());
8132 if (m_pFoundRoutePoint) {
8140 if (g_btouch && m_pRoutePointEditTarget) {
8143 RoutePointGui(*m_pRoutePointEditTarget).EnableDragHandle(
false);
8147 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
8148 pFindAIS =
pSelectAIS->FindSelection(ctx, slat, slon, SELTYPE_AISTARGET);
8149 pFindRP =
pSelect->FindSelection(ctx, slat, slon, SELTYPE_ROUTEPOINT);
8150 pFindRouteSeg =
pSelect->FindSelection(ctx, slat, slon, SELTYPE_ROUTESEGMENT);
8151 pFindTrackSeg =
pSelect->FindSelection(ctx, slat, slon, SELTYPE_TRACKSEGMENT);
8155 pSelectTC->FindSelection(ctx, slat, slon, SELTYPE_CURRENTPOINT);
8158 pFindTide =
pSelectTC->FindSelection(ctx, slat, slon, SELTYPE_TIDEPOINT);
8164 m_FoundAIS_MMSI = pFindAIS->GetUserData();
8167 if (
g_pAIS->Get_Target_Data_From_MMSI(m_FoundAIS_MMSI))
8168 seltype |= SELTYPE_AISTARGET;
8173 m_pFoundRoutePoint = NULL;
8178 Route *pSelectedActiveRoute = NULL;
8179 Route *pSelectedVizRoute = NULL;
8182 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
8183 SelectableItemList SelList =
8184 pSelect->FindSelectionList(ctx, slat, slon, SELTYPE_ROUTEPOINT);
8192 bool brp_viz =
false;
8194 for (
unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
8196 if (pr->IsVisible()) {
8201 if (!brp_viz && prp->IsShared())
8203 brp_viz = prp->IsVisible();
8206 brp_viz = prp->IsVisible();
8208 if ((NULL == pFirstVizPoint) && brp_viz) pFirstVizPoint = prp;
8213 m_pSelectedRoute = NULL;
8215 for (
unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
8218 pSelectedActiveRoute = pr;
8219 pFoundActiveRoutePoint = prp;
8224 if (NULL == pSelectedVizRoute) {
8225 for (
unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
8227 if (pr->IsVisible()) {
8228 pSelectedVizRoute = pr;
8229 pFoundVizRoutePoint = prp;
8235 delete proute_array;
8240 if (pFoundActiveRoutePoint) {
8241 m_pFoundRoutePoint = pFoundActiveRoutePoint;
8242 m_pSelectedRoute = pSelectedActiveRoute;
8243 }
else if (pFoundVizRoutePoint) {
8244 m_pFoundRoutePoint = pFoundVizRoutePoint;
8245 m_pSelectedRoute = pSelectedVizRoute;
8248 m_pFoundRoutePoint = pFirstVizPoint;
8250 if (m_pSelectedRoute) {
8251 if (m_pSelectedRoute->IsVisible()) seltype |= SELTYPE_ROUTEPOINT;
8252 }
else if (m_pFoundRoutePoint) {
8253 seltype |= SELTYPE_MARKPOINT;
8257 if (m_pFoundRoutePoint) {
8261 .CalculateDCRect(m_dc_route,
this, &wp_rect);
8262 RefreshRect(wp_rect,
true);
8270 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
8271 SelectableItemList SelList =
8272 pSelect->FindSelectionList(ctx, slat, slon, SELTYPE_ROUTESEGMENT);
8274 if (NULL == m_pSelectedRoute)
8279 if (pr->IsVisible()) {
8280 m_pSelectedRoute = pr;
8286 if (m_pSelectedRoute) {
8287 if (NULL == m_pFoundRoutePoint)
8288 m_pFoundRoutePoint = (
RoutePoint *)pFindRouteSeg->m_pData1;
8293 if (g_bopengl && m_glcc) {
8298 RouteGui(*m_pSelectedRoute).Draw(dc,
this, GetVP().GetBBox());
8300 seltype |= SELTYPE_ROUTESEGMENT;
8304 if (pFindTrackSeg) {
8305 m_pSelectedTrack = NULL;
8306 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
8307 SelectableItemList SelList =
8308 pSelect->FindSelectionList(ctx, slat, slon, SELTYPE_TRACKSEGMENT);
8313 if (pt->IsVisible()) {
8314 m_pSelectedTrack = pt;
8318 if (m_pSelectedTrack) seltype |= SELTYPE_TRACKSEGMENT;
8324 m_pIDXCandidate = FindBestCurrentObject(slat, slon);
8325 seltype |= SELTYPE_CURRENTPOINT;
8328 else if (pFindTide) {
8329 m_pIDXCandidate = (
IDX_entry *)pFindTide->m_pData1;
8330 seltype |= SELTYPE_TIDEPOINT;
8335 if (0 == seltype) seltype |= SELTYPE_UNKNOWN;
8340IDX_entry *ChartCanvas::FindBestCurrentObject(
double lat,
double lon) {
8350 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
8351 SelectableItemList SelList =
8352 pSelectTC->FindSelectionList(ctx, lat, lon, SELTYPE_CURRENTPOINT);
8355 pFind = *SelList.begin();
8356 pIDX_best_candidate = (
IDX_entry *)(pFind->m_pData1);
8358 auto node = SelList.begin();
8359 if (SelList.size() > 1) {
8360 for (++node; node != SelList.end(); ++node) {
8363 if (pIDX_candidate->
IDX_type ==
'c') {
8364 pIDX_best_candidate = pIDX_candidate;
8369 pFind = *SelList.begin();
8370 pIDX_best_candidate = (
IDX_entry *)(pFind->m_pData1);
8373 return pIDX_best_candidate;
8375void ChartCanvas::CallPopupMenu(
int x,
int y) {
8379 InvokeCanvasMenu(x, y, SELTYPE_ROUTECREATE);
8387 if (SELTYPE_CURRENTPOINT == seltype) {
8393 if (SELTYPE_TIDEPOINT == seltype) {
8399 InvokeCanvasMenu(x, y, seltype);
8402 if (m_pSelectedRoute &&
g_pRouteMan->IsRouteValid(m_pSelectedRoute)) {
8406 m_pSelectedRoute = NULL;
8408 if (m_pFoundRoutePoint) {
8409 if (
pSelect->IsSelectableRoutePointValid(m_pFoundRoutePoint))
8412 m_pFoundRoutePoint = NULL;
8421 if (m_pRoutePointEditTarget &&
8422 !
pSelect->IsSelectableRoutePointValid(m_pRoutePointEditTarget)) {
8423 m_pRoutePointEditTarget = NULL;
8424 m_lastRoutePointEditTarget = NULL;
8425 m_pFoundPoint = NULL;
8426 m_bRouteEditing =
false;
8427 m_bMarkEditing =
false;
8434bool ChartCanvas::MouseEventProcessObjects(wxMouseEvent &event) {
8442 event.GetPosition(&x, &y);
8448 SelectRadius = g_Platform->GetSelectRadiusPix() /
8449 (m_true_scale_ppm * 1852 * 60);
8456 if (event.LeftDClick() && (cursor_region == CENTER)) {
8457 m_DoubleClickTimer->Start();
8458 singleClickEventIsValid =
false;
8464 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
8467 pFindAIS =
pSelectAIS->FindSelection(ctx, zlat, zlon, SELTYPE_AISTARGET);
8470 m_FoundAIS_MMSI = pFindAIS->GetUserData();
8471 if (
g_pAIS->Get_Target_Data_From_MMSI(m_FoundAIS_MMSI)) {
8472 ShowAISTargetQueryDialog(
this, m_FoundAIS_MMSI);
8478 SelectableItemList rpSelList =
8479 pSelect->FindSelectionList(ctx, zlat, zlon, SELTYPE_ROUTEPOINT);
8480 bool b_onRPtarget =
false;
8483 if (m_pRoutePointEditTarget && (frp == m_pRoutePointEditTarget)) {
8484 b_onRPtarget =
true;
8492 std::unique_ptr<HostApi> host_api =
GetHostApi();
8493 auto *api_121 =
dynamic_cast<HostApi121 *
>(host_api.get());
8495 if (m_pRoutePointEditTarget) {
8497 if ((api_121->GetContextMenuMask() &
8498 api_121->kContextMenuDisableWaypoint))
8500 ShowMarkPropertiesDialog(m_pRoutePointEditTarget);
8506 RoutePointGui(*m_pRoutePointEditTarget).EnableDragHandle(
false);
8509 .CalculateDCRect(m_dc_route,
this, &wp_rect);
8510 m_pRoutePointEditTarget = NULL;
8511 RefreshRect(wp_rect,
true);
8515 auto node = rpSelList.begin();
8516 if (node != rpSelList.end()) {
8520 wxArrayPtrVoid *proute_array =
8525 bool brp_viz =
false;
8527 for (
unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
8529 if (pr->IsVisible()) {
8534 delete proute_array;
8538 brp_viz = frp->IsVisible();
8540 brp_viz = frp->IsVisible();
8543 if ((api_121->GetContextMenuMask() &
8544 api_121->kContextMenuDisableWaypoint))
8547 ShowMarkPropertiesDialog(frp);
8556 cursorItem =
pSelect->FindSelection(ctx, zlat, zlon, SELTYPE_ROUTESEGMENT);
8558 if ((api_121->GetContextMenuMask() & api_121->kContextMenuDisableRoute))
8561 if (pr->IsVisible()) {
8562 ShowRoutePropertiesDialog(_(
"Route Properties"), pr);
8567 cursorItem =
pSelect->FindSelection(ctx, zlat, zlon, SELTYPE_TRACKSEGMENT);
8569 if ((api_121->GetContextMenuMask() & api_121->kContextMenuDisableTrack))
8572 if (pt->IsVisible()) {
8573 ShowTrackPropertiesDialog(pt);
8582 if (m_bShowCurrent) {
8584 pSelectTC->FindSelection(ctx, zlat, zlon, SELTYPE_CURRENTPOINT);
8586 m_pIDXCandidate = FindBestCurrentObject(zlat, zlon);
8588 auto plugin_array = PluginLoader::GetInstance()->
GetPlugInArray();
8589 for (
unsigned int i = 0; i < plugin_array->GetCount(); i++) {
8591 if (pic->m_enabled && pic->m_init_state &&
8592 (pic->
m_cap_flag & WANTS_TIDECURRENT_CLICK)) {
8595 if (m_pIDXCandidate) {
8596 info.point_type = CURRENT_STATION;
8600 info.getTide = [](time_t t,
int idx,
float &value,
float &dir) {
8601 return ptcmgr->GetTideOrCurrentMeters(t, idx, value, dir);
8605 if (plugin) plugin->OnTideCurrentClick(info);
8620 pFindTide =
pSelectTC->FindSelection(ctx, zlat, zlon, SELTYPE_TIDEPOINT);
8622 m_pIDXCandidate = (
IDX_entry *)pFindTide->m_pData1;
8624 auto plugin_array = PluginLoader::GetInstance()->
GetPlugInArray();
8625 for (
unsigned int i = 0; i < plugin_array->GetCount(); i++) {
8627 if (pic->m_enabled && pic->m_init_state &&
8628 (pic->
m_cap_flag & WANTS_TIDECURRENT_CLICK)) {
8631 if (m_pIDXCandidate) {
8632 info.point_type = TIDE_STATION;
8636 info.getTide = [](time_t t,
int idx,
float &value,
float &dir) {
8637 return ptcmgr->GetTideOrCurrent(t, idx, value, dir);
8641 if (plugin) plugin->OnTideCurrentClick(info);
8656 ShowObjectQueryWindow(x, y, zlat, zlon);
8661 if (event.LeftDown()) {
8677 bool appending =
false;
8678 bool inserting =
false;
8681 SetCursor(*pCursorPencil);
8685 m_bRouteEditing =
true;
8687 if (m_routeState == 1) {
8688 m_pMouseRoute =
new Route();
8689 NavObj_dB::GetInstance().InsertRoute(m_pMouseRoute);
8699 double nearby_radius_meters =
8700 g_Platform->GetSelectRadiusPix() / m_true_scale_ppm;
8703 pWayPointMan->GetNearbyWaypoint(rlat, rlon, nearby_radius_meters);
8704 if (pNearbyPoint && (pNearbyPoint != m_prev_pMousePoint) &&
8705 !pNearbyPoint->
m_bIsInLayer && pNearbyPoint->IsVisible()) {
8706 wxArrayPtrVoid *proute_array =
8711 bool brp_viz =
false;
8713 for (
unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
8715 if (pr->IsVisible()) {
8720 delete proute_array;
8722 pNearbyPoint->IsShared())
8725 pNearbyPoint->IsVisible();
8727 brp_viz = pNearbyPoint->IsVisible();
8730 wxString msg = _(
"Use nearby waypoint?");
8732 const bool noname(pNearbyPoint->GetName() ==
"");
8735 _(
"Use nearby nameless waypoint and name it M with"
8736 " a unique number?");
8739 m_FinishRouteOnKillFocus =
false;
8741 OCPNMessageBox(
this, msg, _(
"OpenCPN Route Create"),
8742 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
8743 m_FinishRouteOnKillFocus =
true;
8744 if (dlg_return == wxID_YES) {
8746 if (m_pMouseRoute) {
8747 int last_wp_num = m_pMouseRoute->GetnPoints();
8749 wxString guid_short = m_pMouseRoute->GetGUID().Left(2);
8750 wxString wp_name = wxString::Format(
8751 "M%002i-%s", last_wp_num + 1, guid_short);
8752 pNearbyPoint->SetName(wp_name);
8754 pNearbyPoint->SetName(
"WPXX");
8756 pMousePoint = pNearbyPoint;
8759 if (m_routeState > 1)
8760 undo->BeforeUndoableAction(Undo_AppendWaypoint, pMousePoint,
8761 Undo_HasParent, NULL);
8764 g_pRouteMan->FindVisibleRouteContainingWaypoint(pMousePoint);
8765 bool procede =
false;
8769 if (m_routeState > 1 && m_pMouseRoute && tail == m_pMouseRoute)
8775 m_FinishRouteOnKillFocus =
false;
8781 _(
"Insert first part of this route in the new route?");
8782 if (tail->GetIndexOf(pMousePoint) ==
8785 dmsg = _(
"Insert this route in the new route?");
8787 if (tail->GetIndexOf(pMousePoint) > 0) {
8788 dlg_return = OCPNMessageBox(
8789 this, dmsg, _(
"OpenCPN Route Create"),
8790 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
8791 m_FinishRouteOnKillFocus =
true;
8793 if (dlg_return == wxID_YES) {
8800 _(
"Append last part of this route to the new route?");
8801 if (tail->GetIndexOf(pMousePoint) == 1)
8803 "Append this route to the new route?");
8808 if (tail->GetLastPoint() != pMousePoint) {
8809 dlg_return = OCPNMessageBox(
8810 this, dmsg, _(
"OpenCPN Route Create"),
8811 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
8812 m_FinishRouteOnKillFocus =
true;
8814 if (dlg_return == wxID_YES) {
8825 if (!FindRouteContainingWaypoint(pMousePoint))
8826 pMousePoint->SetShared(
true);
8831 if (NULL == pMousePoint) {
8832 pMousePoint =
new RoutePoint(rlat, rlon, g_default_routepoint_icon,
8834 pMousePoint->SetNameShown(
false);
8838 pSelect->AddSelectableRoutePoint(rlat, rlon, pMousePoint);
8840 if (m_routeState > 1)
8841 undo->BeforeUndoableAction(Undo_AppendWaypoint, pMousePoint,
8842 Undo_IsOrphanded, NULL);
8845 if (m_pMouseRoute) {
8846 if (m_routeState == 1) {
8848 m_pMouseRoute->AddPoint(pMousePoint);
8851 double rhumbBearing, rhumbDist, gcBearing, gcDist;
8852 DistanceBearingMercator(rlat, rlon, m_prev_rlat, m_prev_rlon,
8853 &rhumbBearing, &rhumbDist);
8854 Geodesic::GreatCircleDistBear(m_prev_rlon, m_prev_rlat, rlon,
8855 rlat, &gcDist, &gcBearing, NULL);
8856 double gcDistNM = gcDist / 1852.0;
8859 int segmentCount = (3.0 + (rhumbDist - gcDistNM)) /
8860 pow(rhumbDist - gcDistNM - 1, 0.5);
8863 msg << _(
"For this leg the Great Circle route is ")
8865 << _(
" shorter than rhumbline.\n\n")
8866 << _(
"Would you like include the Great Circle routing points "
8869 m_FinishRouteOnKillFocus =
false;
8870 m_disable_edge_pan =
true;
8873 int answer = OCPNMessageBox(
this, msg, _(
"OpenCPN Route Create"),
8874 wxYES_NO | wxNO_DEFAULT);
8876 m_disable_edge_pan =
false;
8877 m_FinishRouteOnKillFocus =
true;
8879 if (answer == wxID_YES) {
8881 RoutePoint *prevGcPoint = m_prev_pMousePoint;
8882 wxRealPoint gcCoord;
8884 for (
int i = 1; i <= segmentCount; i++) {
8885 double fraction = (double)i * (1.0 / (
double)segmentCount);
8886 Geodesic::GreatCircleTravel(m_prev_rlon, m_prev_rlat,
8887 gcDist * fraction, gcBearing,
8888 &gcCoord.x, &gcCoord.y, NULL);
8890 if (i < segmentCount) {
8891 gcPoint =
new RoutePoint(gcCoord.y, gcCoord.x,
"xmblue",
"",
8893 gcPoint->SetNameShown(
false);
8895 NavObj_dB::GetInstance().InsertRoutePoint(gcPoint);
8897 pSelect->AddSelectableRoutePoint(gcCoord.y, gcCoord.x,
8900 gcPoint = pMousePoint;
8903 m_pMouseRoute->AddPoint(gcPoint);
8904 pSelect->AddSelectableRouteSegment(
8905 prevGcPoint->m_lat, prevGcPoint->m_lon, gcPoint->m_lat,
8906 gcPoint->m_lon, prevGcPoint, gcPoint, m_pMouseRoute);
8907 prevGcPoint = gcPoint;
8910 undo->CancelUndoableAction(
true);
8913 m_pMouseRoute->AddPoint(pMousePoint);
8914 pSelect->AddSelectableRouteSegment(
8915 m_prev_rlat, m_prev_rlon, rlat, rlon, m_prev_pMousePoint,
8916 pMousePoint, m_pMouseRoute);
8917 undo->AfterUndoableAction(m_pMouseRoute);
8921 m_pMouseRoute->AddPoint(pMousePoint);
8922 pSelect->AddSelectableRouteSegment(m_prev_rlat, m_prev_rlon, rlat,
8923 rlon, m_prev_pMousePoint,
8924 pMousePoint, m_pMouseRoute);
8925 undo->AfterUndoableAction(m_pMouseRoute);
8931 m_prev_pMousePoint = pMousePoint;
8939 int connect = tail->GetIndexOf(pMousePoint);
8944 int length = tail->GetnPoints();
8949 start = connect + 1;
8955 m_pMouseRoute->RemovePoint(m_pMouseRoute->GetLastPoint());
8957 for (i = start; i <= stop; i++) {
8958 m_pMouseRoute->AddPointAndSegment(tail->GetPoint(i),
false);
8961 m_pMouseRoute->GetnPoints();
8963 top_frame::Get()->RefreshAllCanvas();
8967 m_pMouseRoute->GetPoint(m_pMouseRoute->GetnPoints())->m_lat;
8969 m_pMouseRoute->GetPoint(m_pMouseRoute->GetnPoints())->m_lon;
8970 m_pMouseRoute->FinalizeForRendering();
8972 top_frame::Get()->RefreshAllCanvas();
8976 else if (m_bMeasure_Active && (m_nMeasureState >= 1))
8978 SetCursor(*pCursorPencil);
8980 if (!m_pMeasureRoute) {
8981 m_pMeasureRoute =
new Route();
8985 if (m_nMeasureState == 1) {
8990 if (!
g_pRouteMan->IsRouteInList(m_pMeasureRoute)) {
8991 wxLogMessage(
"Stale measure route");
8992 CancelMeasureRoute();
8993 top_frame::Get()->RefreshAllCanvas();
8998 wxEmptyString, wxEmptyString);
9000 pMousePoint->SetShowWaypointRangeRings(
false);
9002 m_pMeasureRoute->AddPoint(pMousePoint);
9006 m_prev_pMousePoint = pMousePoint;
9008 m_pMeasureRoute->GetnPoints();
9011 top_frame::Get()->RefreshAllCanvas();
9017 FindRoutePointsAtCursor(SelectRadius,
true);
9021 m_last_touch_down_pos =
event.GetPosition();
9023 if ((m_bMeasure_Active && m_nMeasureState) || (m_routeState)) {
9031 if (ret)
return true;
9034 if (event.Dragging()) {
9041 if (m_pRoutePointEditTarget &&
9042 !
pSelect->IsSelectableRoutePointValid(m_pRoutePointEditTarget)) {
9043 m_pRoutePointEditTarget = NULL;
9044 m_lastRoutePointEditTarget = NULL;
9045 m_pFoundPoint = NULL;
9046 m_bRouteEditing =
false;
9047 m_bMarkEditing =
false;
9053 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
9055 if (m_pRoutePointEditTarget && !m_bIsInRadius) {
9057 SelectableItemList SelList =
pSelect->FindSelectionList(
9061 if (m_pRoutePointEditTarget == frp) m_bIsInRadius =
true;
9066 if (m_pRoutePointEditTarget &&
9067 m_pRoutePointEditTarget->IsDragHandleEnabled()) {
9069 SelectableItemList SelList =
pSelect->FindSelectionList(
9073 if (m_pRoutePointEditTarget == frp) {
9074 m_bIsInRadius =
true;
9079 if (!m_dragoffsetSet) {
9081 .PresetDragOffset(
this, mouse_x, mouse_y);
9082 m_dragoffsetSet =
true;
9087 if (m_bRouteEditing && m_pRoutePointEditTarget) {
9088 bool DraggingAllowed = g_btouch ? m_bIsInRadius :
true;
9091 if (g_bWayPointPreventDragging) DraggingAllowed =
false;
9093 DraggingAllowed =
false;
9095 if (m_pRoutePointEditTarget &&
9096 (m_pRoutePointEditTarget->GetIconName() ==
"mob"))
9097 DraggingAllowed =
false;
9099 if (m_pRoutePointEditTarget->
m_bIsInLayer) DraggingAllowed =
false;
9101 if (DraggingAllowed) {
9102 if (!undo->InUndoableAction()) {
9103 undo->BeforeUndoableAction(Undo_MoveWaypoint, m_pRoutePointEditTarget,
9104 Undo_NeedsCopy, m_pFoundPoint);
9110 if (!g_bopengl && m_pEditRouteArray) {
9111 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++) {
9112 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
9119 RouteGui(*pr).CalculateDCRect(m_dc_route,
this, &route_rect);
9120 pre_rect.Union(route_rect);
9128 if (CheckEdgePan(x, y,
true, 5, 2))
9136 .SetPointFromDraghandlePoint(
this, mouse_x, mouse_y);
9138 pSelect->ModifySelectablePoint(new_cursor_lat, new_cursor_lon,
9139 m_pRoutePointEditTarget,
9140 SELTYPE_DRAGHANDLE);
9141 m_pFoundPoint->m_slat =
9142 m_pRoutePointEditTarget->m_lat;
9143 m_pFoundPoint->m_slon = m_pRoutePointEditTarget->m_lon;
9145 m_pRoutePointEditTarget->m_lat =
9147 m_pRoutePointEditTarget->m_lon = new_cursor_lon;
9148 m_pRoutePointEditTarget->
m_wpBBox.Invalidate();
9149 m_pFoundPoint->m_slat =
9151 m_pFoundPoint->m_slon = new_cursor_lon;
9167 if (m_pEditRouteArray) {
9168 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount();
9170 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
9173 RouteGui(*pr).CalculateDCRect(m_dc_route,
this, &route_rect);
9174 post_rect.Union(route_rect);
9180 pre_rect.Union(post_rect);
9181 RefreshRect(pre_rect,
false);
9183 top_frame::Get()->RefreshCanvasOther(
this);
9184 m_bRoutePoinDragging =
true;
9189 else if (m_bMarkEditing && m_pRoutePointEditTarget) {
9190 bool DraggingAllowed = g_btouch ? m_bIsInRadius :
true;
9193 if (g_bWayPointPreventDragging) DraggingAllowed =
false;
9195 DraggingAllowed =
false;
9197 if (m_pRoutePointEditTarget &&
9198 (m_pRoutePointEditTarget->GetIconName() ==
"mob"))
9199 DraggingAllowed =
false;
9201 if (m_pRoutePointEditTarget->
m_bIsInLayer) DraggingAllowed =
false;
9203 if (DraggingAllowed) {
9204 if (!undo->InUndoableAction()) {
9205 undo->BeforeUndoableAction(Undo_MoveWaypoint, m_pRoutePointEditTarget,
9206 Undo_NeedsCopy, m_pFoundPoint);
9220 lppmax = wxMax(lpp1 + 10, lpp2 + 10);
9226 .CalculateDCRect(m_dc_route,
this, &pre_rect);
9227 if ((lppmax > pre_rect.width / 2) || (lppmax > pre_rect.height / 2))
9228 pre_rect.Inflate((
int)(lppmax - (pre_rect.width / 2)),
9229 (
int)(lppmax - (pre_rect.height / 2)));
9237 .SetPointFromDraghandlePoint(
this, mouse_x, mouse_y);
9240 m_pRoutePointEditTarget,
9241 SELTYPE_DRAGHANDLE);
9242 m_pFoundPoint->m_slat =
9243 m_pRoutePointEditTarget->m_lat;
9244 m_pFoundPoint->m_slon = m_pRoutePointEditTarget->m_lon;
9246 m_pRoutePointEditTarget->m_lat =
9249 m_pRoutePointEditTarget->
m_wpBBox.Invalidate();
9262 if (!g_btouch) InvalidateGL();
9268 .CalculateDCRect(m_dc_route,
this, &post_rect);
9269 if ((lppmax > post_rect.width / 2) || (lppmax > post_rect.height / 2))
9270 post_rect.Inflate((
int)(lppmax - (post_rect.width / 2)),
9271 (
int)(lppmax - (post_rect.height / 2)));
9274 pre_rect.Union(post_rect);
9275 RefreshRect(pre_rect,
false);
9277 top_frame::Get()->RefreshCanvasOther(
this);
9278 m_bRoutePoinDragging =
true;
9280 ret = g_btouch ? m_bRoutePoinDragging :
true;
9283 if (ret)
return true;
9286 if (event.LeftUp()) {
9287 bool b_startedit_route =
false;
9288 m_dragoffsetSet =
false;
9291 m_bChartDragging =
false;
9292 m_bIsInRadius =
false;
9299 int dx = x - m_touchdownPos.x;
9300 int dy = y - m_touchdownPos.y;
9301 int dist2 = dx * dx + dy * dy;
9302 bool wasPan = (dist2 > 20 * 20);
9309 m_bedge_pan =
false;
9314 bool appending =
false;
9315 bool inserting =
false;
9321 if (m_pRoutePointEditTarget) {
9327 .CalculateDCRect(m_dc_route,
this, &wp_rect);
9328 RefreshRect(wp_rect,
true);
9330 m_pRoutePointEditTarget = NULL;
9332 m_bRouteEditing =
true;
9334 if (m_routeState == 1) {
9335 m_pMouseRoute =
new Route();
9336 m_pMouseRoute->SetHiLite(50);
9340 NavObj_dB::GetInstance().InsertRoute(m_pMouseRoute);
9347 double nearby_radius_meters =
9348 g_Platform->GetSelectRadiusPix() / m_true_scale_ppm;
9351 pWayPointMan->GetNearbyWaypoint(rlat, rlon, nearby_radius_meters);
9352 if (pNearbyPoint && (pNearbyPoint != m_prev_pMousePoint) &&
9353 !pNearbyPoint->
m_bIsInLayer && pNearbyPoint->IsVisible()) {
9356 m_FinishRouteOnKillFocus =
9358 dlg_return = OCPNMessageBox(
9359 this, _(
"Use nearby waypoint?"), _(
"OpenCPN Route Create"),
9360 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
9361 m_FinishRouteOnKillFocus =
true;
9363 dlg_return = wxID_YES;
9365 if (dlg_return == wxID_YES) {
9366 pMousePoint = pNearbyPoint;
9369 if (m_routeState > 1)
9370 undo->BeforeUndoableAction(Undo_AppendWaypoint, pMousePoint,
9371 Undo_HasParent, NULL);
9372 tail =
g_pRouteMan->FindVisibleRouteContainingWaypoint(pMousePoint);
9374 bool procede =
false;
9378 if (m_routeState > 1 && m_pMouseRoute && tail == m_pMouseRoute)
9384 m_FinishRouteOnKillFocus =
false;
9385 if (m_routeState == 1) {
9389 _(
"Insert first part of this route in the new route?");
9390 if (tail->GetIndexOf(pMousePoint) ==
9393 dmsg = _(
"Insert this route in the new route?");
9395 if (tail->GetIndexOf(pMousePoint) != 1) {
9397 OCPNMessageBox(
this, dmsg, _(
"OpenCPN Route Create"),
9398 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
9399 m_FinishRouteOnKillFocus =
true;
9401 if (dlg_return == wxID_YES) {
9408 _(
"Append last part of this route to the new route?");
9409 if (tail->GetIndexOf(pMousePoint) == 1)
9411 "Append this route to the new route?");
9415 if (tail->GetLastPoint() != pMousePoint) {
9417 OCPNMessageBox(
this, dmsg, _(
"OpenCPN Route Create"),
9418 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
9419 m_FinishRouteOnKillFocus =
true;
9421 if (dlg_return == wxID_YES) {
9432 if (!FindRouteContainingWaypoint(pMousePoint))
9433 pMousePoint->SetShared(
true);
9437 if (NULL == pMousePoint) {
9438 pMousePoint =
new RoutePoint(rlat, rlon, g_default_routepoint_icon,
9440 pMousePoint->SetNameShown(
false);
9442 pSelect->AddSelectableRoutePoint(rlat, rlon, pMousePoint);
9444 if (m_routeState > 1)
9445 undo->BeforeUndoableAction(Undo_AppendWaypoint, pMousePoint,
9446 Undo_IsOrphanded, NULL);
9449 if (m_routeState == 1) {
9451 m_pMouseRoute->AddPoint(pMousePoint);
9452 NavObj_dB::GetInstance().UpdateRoute(m_pMouseRoute);
9456 double rhumbBearing, rhumbDist, gcBearing, gcDist;
9457 DistanceBearingMercator(rlat, rlon, m_prev_rlat, m_prev_rlon,
9458 &rhumbBearing, &rhumbDist);
9459 Geodesic::GreatCircleDistBear(m_prev_rlon, m_prev_rlat, rlon, rlat,
9460 &gcDist, &gcBearing, NULL);
9461 double gcDistNM = gcDist / 1852.0;
9464 int segmentCount = (3.0 + (rhumbDist - gcDistNM)) /
9465 pow(rhumbDist - gcDistNM - 1, 0.5);
9468 msg << _(
"For this leg the Great Circle route is ")
9470 << _(
" shorter than rhumbline.\n\n")
9471 << _(
"Would you like include the Great Circle routing points "
9475 m_FinishRouteOnKillFocus =
false;
9476 int answer = OCPNMessageBox(
this, msg, _(
"OpenCPN Route Create"),
9477 wxYES_NO | wxNO_DEFAULT);
9478 m_FinishRouteOnKillFocus =
true;
9480 int answer = wxID_NO;
9483 if (answer == wxID_YES) {
9485 RoutePoint *prevGcPoint = m_prev_pMousePoint;
9486 wxRealPoint gcCoord;
9488 for (
int i = 1; i <= segmentCount; i++) {
9489 double fraction = (double)i * (1.0 / (
double)segmentCount);
9490 Geodesic::GreatCircleTravel(m_prev_rlon, m_prev_rlat,
9491 gcDist * fraction, gcBearing,
9492 &gcCoord.x, &gcCoord.y, NULL);
9494 if (i < segmentCount) {
9495 gcPoint =
new RoutePoint(gcCoord.y, gcCoord.x,
"xmblue",
"",
9497 gcPoint->SetNameShown(
false);
9498 pSelect->AddSelectableRoutePoint(gcCoord.y, gcCoord.x,
9501 gcPoint = pMousePoint;
9504 m_pMouseRoute->AddPoint(gcPoint);
9505 NavObj_dB::GetInstance().UpdateRoute(m_pMouseRoute);
9507 pSelect->AddSelectableRouteSegment(
9508 prevGcPoint->m_lat, prevGcPoint->m_lon, gcPoint->m_lat,
9509 gcPoint->m_lon, prevGcPoint, gcPoint, m_pMouseRoute);
9510 prevGcPoint = gcPoint;
9513 undo->CancelUndoableAction(
true);
9516 m_pMouseRoute->AddPoint(pMousePoint);
9517 NavObj_dB::GetInstance().UpdateRoute(m_pMouseRoute);
9518 pSelect->AddSelectableRouteSegment(m_prev_rlat, m_prev_rlon, rlat,
9519 rlon, m_prev_pMousePoint,
9520 pMousePoint, m_pMouseRoute);
9521 undo->AfterUndoableAction(m_pMouseRoute);
9525 m_pMouseRoute->AddPoint(pMousePoint);
9526 NavObj_dB::GetInstance().UpdateRoute(m_pMouseRoute);
9528 pSelect->AddSelectableRouteSegment(m_prev_rlat, m_prev_rlon, rlat,
9529 rlon, m_prev_pMousePoint,
9530 pMousePoint, m_pMouseRoute);
9531 undo->AfterUndoableAction(m_pMouseRoute);
9537 m_prev_pMousePoint = pMousePoint;
9544 int connect = tail->GetIndexOf(pMousePoint);
9549 int length = tail->GetnPoints();
9559 m_pMouseRoute->RemovePoint(
9563 for (i = start; i < stop; i++) {
9564 m_pMouseRoute->AddPointAndSegment(tail->GetPoint(i + 1),
false);
9567 m_pMouseRoute->GetnPoints();
9569 top_frame::Get()->RefreshAllCanvas();
9575 m_pMouseRoute->GetPoint(m_pMouseRoute->GetnPoints())->m_lat;
9577 m_pMouseRoute->GetPoint(m_pMouseRoute->GetnPoints())->m_lon;
9578 m_pMouseRoute->FinalizeForRendering();
9583 }
else if (m_bMeasure_Active && m_nMeasureState)
9586 m_bedge_pan =
false;
9592 int mdx = x - m_touchdownPos.x;
9593 int mdy = y - m_touchdownPos.y;
9594 if (mdx * mdx + mdy * mdy > 20 * 20)
return false;
9597 if (m_nMeasureState == 1) {
9598 m_pMeasureRoute =
new Route();
9604 if (!
g_pRouteMan->IsRouteInList(m_pMeasureRoute)) {
9605 wxLogMessage(
"Stale measure route");
9606 CancelMeasureRoute();
9607 top_frame::Get()->RefreshAllCanvas();
9610 if (m_pMeasureRoute) {
9613 wxEmptyString, wxEmptyString);
9616 m_pMeasureRoute->AddPoint(pMousePoint);
9620 m_prev_pMousePoint = pMousePoint;
9622 m_pMeasureRoute->GetnPoints();
9626 CancelMeasureRoute();
9632 bool bSelectAllowed =
true;
9634 if (g_bWayPointPreventDragging) bSelectAllowed =
false;
9636 bSelectAllowed =
false;
9640 int significant_drag = g_Platform->GetSelectRadiusPix() * 2;
9641 if ((abs(m_last_touch_down_pos.x - event.GetPosition().x) >
9642 significant_drag) ||
9643 (abs(m_last_touch_down_pos.y - event.GetPosition().y) >
9644 significant_drag)) {
9645 bSelectAllowed =
false;
9653 if (m_bRoutePoinDragging) bSelectAllowed =
false;
9655 if (bSelectAllowed) {
9656 bool b_was_editing_mark = m_bMarkEditing;
9657 bool b_was_editing_route = m_bRouteEditing;
9658 FindRoutePointsAtCursor(SelectRadius,
9664 if (m_pRoutePointEditTarget && m_pRoutePointEditTarget->
m_bIsInLayer)
9665 m_pRoutePointEditTarget = NULL;
9667 if (!b_was_editing_route) {
9668 if (m_pEditRouteArray) {
9669 b_startedit_route =
true;
9673 if (m_pTrackRolloverWin && m_pTrackRolloverWin->IsActive()) {
9674 m_pTrackRolloverWin->IsActive(
false);
9676 if (m_pRouteRolloverWin && m_pRouteRolloverWin->IsActive()) {
9677 m_pRouteRolloverWin->IsActive(
false);
9681 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount();
9683 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
9691 RouteGui(*pr).CalculateDCRect(m_dc_route,
this, &route_rect);
9692 pre_rect.Union(route_rect);
9695 RefreshRect(pre_rect,
true);
9698 b_startedit_route =
false;
9702 if (m_pRoutePointEditTarget) {
9703 if (b_was_editing_mark ||
9704 b_was_editing_route) {
9705 if (m_lastRoutePointEditTarget) {
9709 .EnableDragHandle(
false);
9710 pSelect->DeleteSelectablePoint(m_lastRoutePointEditTarget,
9711 SELTYPE_DRAGHANDLE);
9715 if (m_pRoutePointEditTarget) {
9718 RoutePointGui(*m_pRoutePointEditTarget).EnableDragHandle(
true);
9719 wxPoint2DDouble dragHandlePoint =
9721 .GetDragHandlePoint(
this);
9723 dragHandlePoint.m_y, dragHandlePoint.m_x,
9724 m_pRoutePointEditTarget, SELTYPE_DRAGHANDLE);
9727 if (m_lastRoutePointEditTarget) {
9731 .EnableDragHandle(
false);
9732 pSelect->DeleteSelectablePoint(m_lastRoutePointEditTarget,
9733 SELTYPE_DRAGHANDLE);
9736 wxArrayPtrVoid *lastEditRouteArray =
9738 m_lastRoutePointEditTarget);
9739 if (lastEditRouteArray) {
9740 for (
unsigned int ir = 0; ir < lastEditRouteArray->GetCount();
9742 Route *pr = (
Route *)lastEditRouteArray->Item(ir);
9747 delete lastEditRouteArray;
9758 if (m_lastRoutePointEditTarget) {
9761 .CalculateDCRect(m_dc_route,
this, &wp_rect);
9762 RefreshRect(wp_rect,
true);
9765 if (m_pRoutePointEditTarget) {
9768 .CalculateDCRect(m_dc_route,
this, &wp_rect);
9769 RefreshRect(wp_rect,
true);
9777 SelectCtx ctx(m_bShowNavobjects, GetCanvasTrueScale(), GetScaleValue());
9778 bool b_start_rollover =
false;
9782 if (pFind) b_start_rollover =
true;
9785 if (!b_start_rollover && !b_startedit_route) {
9786 SelectableItemList SelList =
pSelect->FindSelectionList(
9790 if (pr && pr->IsVisible()) {
9791 b_start_rollover =
true;
9797 if (!b_start_rollover && !b_startedit_route) {
9798 SelectableItemList SelList =
pSelect->FindSelectionList(
9802 if (tr && tr->IsVisible()) {
9803 b_start_rollover =
true;
9809 if (b_start_rollover)
9810 m_RolloverPopupTimer.Start(m_rollover_popup_timer_msec,
9814 bool appending =
false;
9815 bool inserting =
false;
9817 if (m_bRouteEditing ) {
9819 if (m_pRoutePointEditTarget) {
9825 if (m_bRoutePoinDragging && !m_pRoutePointEditTarget->
m_bIsActive) {
9826 double nearby_radius_meters =
9827 g_Platform->GetSelectRadiusPix() / m_true_scale_ppm;
9828 RoutePoint *pNearbyPoint = pWayPointMan->GetOtherNearbyWaypoint(
9829 m_pRoutePointEditTarget->m_lat, m_pRoutePointEditTarget->m_lon,
9830 nearby_radius_meters, m_pRoutePointEditTarget->
m_GUID);
9832 pWayPointMan->IsReallyVisible(pNearbyPoint)) {
9836 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount();
9838 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
9842 std::find(list->begin(), list->end(), pNearbyPoint);
9843 if (pos != list->end()) {
9855 if (m_pEditRouteArray->GetCount() == 1) duplicate =
false;
9860 OCPNMessageBox(
this,
9861 _(
"Replace this RoutePoint by the nearby "
9863 _(
"OpenCPN RoutePoint change"),
9864 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
9865 if (dlg_return == wxID_YES) {
9870 tail =
g_pRouteMan->FindVisibleRouteContainingWaypoint(
9873 FindRouteContainingWaypoint(m_pRoutePointEditTarget);
9875 if (tail && current && (tail != current)) {
9877 connect = tail->GetIndexOf(pNearbyPoint);
9878 int index_current_route =
9879 current->GetIndexOf(m_pRoutePointEditTarget);
9880 index_last = current->GetIndexOf(current->GetLastPoint());
9881 dlg_return1 = wxID_NO;
9883 index_current_route) {
9885 if (connect != tail->GetnPoints()) {
9888 _(
"Last part of route to be appended to dragged "
9892 _(
"Full route to be appended to dragged route?");
9894 dlg_return1 = OCPNMessageBox(
9895 this, dmsg, _(
"OpenCPN Route Create"),
9896 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
9897 if (dlg_return1 == wxID_YES) {
9901 }
else if (index_current_route ==
9906 _(
"First part of route to be inserted into dragged "
9908 if (connect == tail->GetnPoints())
9910 "Full route to be inserted into dragged route?");
9912 dlg_return1 = OCPNMessageBox(
9913 this, dmsg, _(
"OpenCPN Route Create"),
9914 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
9915 if (dlg_return1 == wxID_YES) {
9922 if (m_pRoutePointEditTarget->IsShared()) {
9924 dlg_return = OCPNMessageBox(
9926 _(
"Do you really want to delete and replace this "
9928 "\n" + _(
"which has been created manually?"),
9929 (
"OpenCPN RoutePoint warning"),
9930 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
9933 if (dlg_return == wxID_YES) {
9934 pMousePoint = pNearbyPoint;
9936 pMousePoint->SetShared(
true);
9946 pSelect->UpdateSelectableRouteSegments(m_pRoutePointEditTarget);
9948 if (m_pEditRouteArray) {
9949 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount();
9951 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
9956 auto pos = std::find(list->begin(), list->end(),
9957 m_pRoutePointEditTarget);
9959 pSelect->DeleteAllSelectableRoutePoints(pr);
9960 pSelect->DeleteAllSelectableRouteSegments(pr);
9963 pos = std::find(list->begin(), list->end(),
9964 m_pRoutePointEditTarget);
9967 pSelect->AddAllSelectableRouteSegments(pr);
9968 pSelect->AddAllSelectableRoutePoints(pr);
9970 pr->FinalizeForRendering();
9971 pr->UpdateSegmentDistances();
9972 if (m_bRoutePoinDragging) {
9974 NavObj_dB::GetInstance().UpdateRoute(pr);
9982 if (m_pEditRouteArray) {
9983 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount();
9985 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
10004 NavObj_dB::GetInstance().DeleteRoutePoint(m_pRoutePointEditTarget);
10011 delete m_pRoutePointEditTarget;
10012 m_lastRoutePointEditTarget = NULL;
10013 m_pRoutePointEditTarget = NULL;
10014 undo->AfterUndoableAction(pMousePoint);
10015 undo->InvalidateUndo();
10020 else if (m_bMarkEditing) {
10021 if (m_pRoutePointEditTarget)
10022 if (m_bRoutePoinDragging) {
10024 NavObj_dB::GetInstance().UpdateRoutePoint(m_pRoutePointEditTarget);
10028 if (m_pRoutePointEditTarget)
10029 undo->AfterUndoableAction(m_pRoutePointEditTarget);
10031 if (!m_pRoutePointEditTarget) {
10032 delete m_pEditRouteArray;
10033 m_pEditRouteArray = NULL;
10034 m_bRouteEditing =
false;
10036 m_bRoutePoinDragging =
false;
10043 int length = tail->GetnPoints();
10044 for (
int i = connect + 1; i <= length; i++) {
10045 current->AddPointAndSegment(tail->GetPoint(i),
false);
10048 top_frame::Get()->RefreshAllCanvas();
10051 current->FinalizeForRendering();
10055 NavObj_dB::GetInstance().UpdateRoute(current);
10058 pSelect->DeleteAllSelectableRoutePoints(current);
10059 pSelect->DeleteAllSelectableRouteSegments(current);
10060 for (
int i = 1; i < connect + 1; i++) {
10061 current->InsertPointAndSegment(tail->GetPoint(i), i - 1,
false);
10063 pSelect->AddAllSelectableRouteSegments(current);
10064 pSelect->AddAllSelectableRoutePoints(current);
10065 current->FinalizeForRendering();
10068 NavObj_dB::GetInstance().UpdateRoute(current);
10073 if (m_pEditRouteArray) {
10074 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++) {
10075 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
10088 if (m_bRouteEditing) {
10090 Route *current = 0;
10091 bool appending =
false;
10092 bool inserting =
false;
10095 if (m_pRoutePointEditTarget) {
10096 m_pRoutePointEditTarget->
m_bBlink =
false;
10100 if (m_bRoutePoinDragging && !m_pRoutePointEditTarget->
m_bIsActive) {
10101 double nearby_radius_meters =
10102 g_Platform->GetSelectRadiusPix() / m_true_scale_ppm;
10103 RoutePoint *pNearbyPoint = pWayPointMan->GetOtherNearbyWaypoint(
10104 m_pRoutePointEditTarget->m_lat, m_pRoutePointEditTarget->m_lon,
10105 nearby_radius_meters, m_pRoutePointEditTarget->
m_GUID);
10107 pWayPointMan->IsReallyVisible(pNearbyPoint)) {
10108 bool duplicate =
false;
10110 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount();
10112 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
10116 std::find(list->begin(), list->end(), pNearbyPoint);
10117 if (pos != list->end()) {
10129 if (m_pEditRouteArray->GetCount() == 1) duplicate =
false;
10134 OCPNMessageBox(
this,
10135 _(
"Replace this RoutePoint by the nearby "
10137 _(
"OpenCPN RoutePoint change"),
10138 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
10139 if (dlg_return == wxID_YES) {
10143 tail =
g_pRouteMan->FindVisibleRouteContainingWaypoint(
10146 FindRouteContainingWaypoint(m_pRoutePointEditTarget);
10148 if (tail && current && (tail != current)) {
10150 connect = tail->GetIndexOf(pNearbyPoint);
10151 int index_current_route =
10152 current->GetIndexOf(m_pRoutePointEditTarget);
10153 index_last = current->GetIndexOf(current->GetLastPoint());
10154 dlg_return1 = wxID_NO;
10156 index_current_route) {
10158 if (connect != tail->GetnPoints()) {
10161 _(
"Last part of route to be appended to dragged "
10165 _(
"Full route to be appended to dragged route?");
10167 dlg_return1 = OCPNMessageBox(
10168 this, dmsg, _(
"OpenCPN Route Create"),
10169 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
10170 if (dlg_return1 == wxID_YES) {
10174 }
else if (index_current_route ==
10176 if (connect != 0) {
10179 _(
"First part of route to be inserted into dragged "
10181 if (connect == tail->GetnPoints())
10183 "Full route to be inserted into dragged route?");
10185 dlg_return1 = OCPNMessageBox(
10186 this, dmsg, _(
"OpenCPN Route Create"),
10187 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
10188 if (dlg_return1 == wxID_YES) {
10195 if (m_pRoutePointEditTarget->IsShared()) {
10196 dlg_return = wxID_NO;
10197 dlg_return = OCPNMessageBox(
10199 _(
"Do you really want to delete and replace this "
10201 "\n" + _(
"which has been created manually?"),
10202 (
"OpenCPN RoutePoint warning"),
10203 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
10206 if (dlg_return == wxID_YES) {
10207 pMousePoint = pNearbyPoint;
10209 pMousePoint->SetShared(
true);
10219 pSelect->UpdateSelectableRouteSegments(m_pRoutePointEditTarget);
10221 if (m_pEditRouteArray) {
10222 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount();
10224 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
10228 auto pos = std::find(list->begin(), list->end(),
10229 m_pRoutePointEditTarget);
10231 pSelect->DeleteAllSelectableRoutePoints(pr);
10232 pSelect->DeleteAllSelectableRouteSegments(pr);
10235 pos = std::find(list->begin(), list->end(),
10236 m_pRoutePointEditTarget);
10237 if (pos != list->end()) list->erase(pos);
10240 pSelect->AddAllSelectableRouteSegments(pr);
10241 pSelect->AddAllSelectableRoutePoints(pr);
10243 pr->FinalizeForRendering();
10244 pr->UpdateSegmentDistances();
10247 if (m_bRoutePoinDragging) {
10252 NavObj_dB::GetInstance().UpdateRoutePoint(
10253 m_pRoutePointEditTarget);
10255 NavObj_dB::GetInstance().UpdateRoute(pr);
10267 int length = tail->GetnPoints();
10268 for (
int i = connect + 1; i <= length; i++) {
10269 current->AddPointAndSegment(tail->GetPoint(i),
false);
10273 top_frame::Get()->RefreshAllCanvas();
10276 current->FinalizeForRendering();
10280 NavObj_dB::GetInstance().UpdateRoute(current);
10283 pSelect->DeleteAllSelectableRoutePoints(current);
10284 pSelect->DeleteAllSelectableRouteSegments(current);
10285 for (
int i = 1; i < connect + 1;
10287 current->InsertPointAndSegment(tail->GetPoint(i), i - 1,
false);
10290 pSelect->AddAllSelectableRouteSegments(current);
10291 pSelect->AddAllSelectableRoutePoints(current);
10292 current->FinalizeForRendering();
10295 NavObj_dB::GetInstance().UpdateRoute(current);
10300 if (m_pEditRouteArray) {
10301 for (
unsigned int ir = 0; ir < m_pEditRouteArray->GetCount();
10303 Route *pr = (
Route *)m_pEditRouteArray->Item(ir);
10315 NavObj_dB::GetInstance().DeleteRoutePoint(m_pRoutePointEditTarget);
10322 delete m_pRoutePointEditTarget;
10323 m_lastRoutePointEditTarget = NULL;
10324 undo->AfterUndoableAction(pMousePoint);
10325 undo->InvalidateUndo();
10330 undo->AfterUndoableAction(m_pRoutePointEditTarget);
10333 delete m_pEditRouteArray;
10334 m_pEditRouteArray = NULL;
10338 m_bRouteEditing =
false;
10339 m_pRoutePointEditTarget = NULL;
10345 else if (m_bMarkEditing) {
10346 if (m_pRoutePointEditTarget) {
10347 if (m_bRoutePoinDragging) {
10349 NavObj_dB::GetInstance().UpdateRoutePoint(m_pRoutePointEditTarget);
10351 undo->AfterUndoableAction(m_pRoutePointEditTarget);
10356 .CalculateDCRect(m_dc_route,
this, &wp_rect);
10358 RefreshRect(wp_rect,
true);
10361 m_pRoutePointEditTarget = NULL;
10362 m_bMarkEditing =
false;
10367 else if (leftIsDown) {
10368 leftIsDown =
false;
10372 if (!m_bChartDragging && !m_bMeasure_Active) {
10374 m_bChartDragging =
false;
10378 m_bRoutePoinDragging =
false;
10381 if (ret)
return true;
10384 if (event.RightDown()) {
10395 m_FinishRouteOnKillFocus =
false;
10396 CallPopupMenu(mx, my);
10397 m_FinishRouteOnKillFocus =
true;
10407 if (event.ShiftDown()) {
10411 event.GetPosition(&x, &y);
10413 x *= m_displayScale;
10414 y *= m_displayScale;
10420 int wheel_dir =
event.GetWheelRotation();
10423 int mouse_wheel_oneshot = abs(wheel_dir) * 4;
10424 wheel_dir = wheel_dir > 0 ? 1 : -1;
10426 double factor = g_mouse_zoom_sensitivity;
10427 if (wheel_dir < 0) factor = 1 / factor;
10430 if ((m_wheelstopwatch.Time() < m_wheelzoom_stop_oneshot)) {
10431 if (wheel_dir == m_last_wheel_dir) {
10432 m_wheelzoom_stop_oneshot += mouse_wheel_oneshot;
10437 m_wheelzoom_stop_oneshot = mouse_wheel_oneshot;
10438 m_wheelstopwatch.Start(0);
10443 m_last_wheel_dir = wheel_dir;
10448 if (event.LeftDown()) {
10454 last_drag.x = x, last_drag.y = y;
10455 m_touchdownPos = wxPoint(x, y);
10456 panleftIsDown =
true;
10459 if (event.LeftUp()) {
10460 if (panleftIsDown) {
10462 panleftIsDown =
false;
10465 if (!m_bChartDragging && !m_bMeasure_Active) {
10466 switch (cursor_region) {
10488 PanCanvas(x - GetVP().pix_width / 2, y - GetVP().pix_height / 2);
10493 m_bChartDragging =
false;
10499 if (event.Dragging() && event.LeftIsDown()) {
10515 if (g_btouch && !m_inPinch) {
10516 struct timespec now;
10517 clock_gettime(CLOCK_MONOTONIC, &now);
10518 uint64_t tnow = (1e9 * now.tv_sec) + now.tv_nsec;
10520 bool trigger_hold =
false;
10521 if (
false == m_bChartDragging) {
10522 if (m_DragTrigger < 0) {
10525 m_DragTriggerStartTime = tnow;
10526 trigger_hold =
true;
10528 if (((tnow - m_DragTriggerStartTime) / 1e6) > 20) {
10529 m_DragTrigger = -1;
10534 if (trigger_hold)
return true;
10536 if (
false == m_bChartDragging) {
10539 last_drag.x = x - 1, last_drag.y = y - 1;
10540 m_bChartDragging =
true;
10541 m_chart_drag_total_time = 0;
10542 m_chart_drag_total_x = 0;
10543 m_chart_drag_total_y = 0;
10544 m_inertia_last_drag_x = x;
10545 m_inertia_last_drag_y = y;
10546 m_drag_vec_x.clear();
10547 m_drag_vec_y.clear();
10548 m_drag_vec_t.clear();
10549 m_last_drag_time = tnow;
10553 uint64_t delta_t = tnow - m_last_drag_time;
10554 double delta_tf = delta_t / 1e9;
10556 m_chart_drag_total_time += delta_tf;
10557 m_chart_drag_total_x += m_inertia_last_drag_x - x;
10558 m_chart_drag_total_y += m_inertia_last_drag_y - y;
10560 m_drag_vec_x.push_back(m_inertia_last_drag_x - x);
10561 m_drag_vec_y.push_back(m_inertia_last_drag_y - y);
10562 m_drag_vec_t.push_back(delta_tf);
10564 m_inertia_last_drag_x = x;
10565 m_inertia_last_drag_y = y;
10566 m_last_drag_time = tnow;
10568 if ((abs(last_drag.x - x) > 2) || (abs(last_drag.y - y) > 2)) {
10569 m_bChartDragging =
true;
10570 StartTimedMovement();
10571 m_pan_drag.x += last_drag.x - x;
10572 m_pan_drag.y += last_drag.y - y;
10573 last_drag.x = x, last_drag.y = y;
10575 }
else if (!g_btouch) {
10576 if ((last_drag.x != x) || (last_drag.y != y)) {
10577 if (!m_routeState) {
10580 m_bChartDragging =
true;
10581 StartTimedMovement();
10582 m_pan_drag.x += last_drag.x - x;
10583 m_pan_drag.y += last_drag.y - y;
10584 last_drag.x = x, last_drag.y = y;
10591 if ((m_bMeasure_Active && m_nMeasureState) || (m_routeState)) {
10593 m_DoubleClickTimer->Start();
10594 singleClickEventIsValid =
false;
10602void ChartCanvas::MouseEvent(wxMouseEvent &event) {
10603 if (MouseEventOverlayWindows(event))
return;
10607 bool nm = MouseEventProcessObjects(event);
10611void ChartCanvas::SetCanvasCursor(wxMouseEvent &event) {
10614 wxCursor *ptarget_cursor = pCursorArrow;
10615 if (!pPlugIn_Cursor) {
10616 ptarget_cursor = pCursorArrow;
10617 if ((!m_routeState) &&
10618 (!m_bMeasure_Active) ) {
10619 if (cursor_region == MID_RIGHT) {
10620 ptarget_cursor = pCursorRight;
10621 }
else if (cursor_region == MID_LEFT) {
10622 ptarget_cursor = pCursorLeft;
10623 }
else if (cursor_region == MID_TOP) {
10624 ptarget_cursor = pCursorDown;
10625 }
else if (cursor_region == MID_BOT) {
10626 ptarget_cursor = pCursorUp;
10628 ptarget_cursor = pCursorArrow;
10630 }
else if (m_bMeasure_Active ||
10632 ptarget_cursor = pCursorPencil;
10634 ptarget_cursor = pPlugIn_Cursor;
10637 SetCursor(*ptarget_cursor);
10640void ChartCanvas::LostMouseCapture(wxMouseCaptureLostEvent &event) {
10641 SetCursor(*pCursorArrow);
10644void ChartCanvas::ShowObjectQueryWindow(
int x,
int y,
float zlat,
float zlon) {
10648 wxArrayString files;
10650 ChartBase *target_chart = GetChartAtCursor();
10651 if (target_chart) {
10652 file.Assign(target_chart->GetFullPath());
10653 if ((target_chart->GetChartType() == CHART_TYPE_PLUGIN) &&
10654 (target_chart->GetChartFamily() == CHART_FAMILY_VECTOR))
10657 Chs57 =
dynamic_cast<s57chart *
>(target_chart);
10659 std::vector<int> stackIndexArray = m_pQuilt->GetExtendedStackIndexArray();
10660 unsigned int im = stackIndexArray.size();
10661 int scale = 2147483647;
10662 if (VPoint.b_quilt && im > 0) {
10663 for (
unsigned int is = 0; is < im; is++) {
10664 if (
ChartData->GetDBChartType(stackIndexArray[is]) ==
10665 CHART_TYPE_MBTILES) {
10666 if (IsTileOverlayIndexInNoShow(stackIndexArray[is]))
continue;
10668 VPoint.
GetLLFromPix(wxPoint(mouse_x, mouse_y), &lat, &lon);
10669 if (
ChartData->GetChartTableEntry(stackIndexArray[is])
10671 .Contains(lat, lon)) {
10672 if (
ChartData->GetChartTableEntry(stackIndexArray[is]).GetScale() <
10675 ChartData->GetChartTableEntry(stackIndexArray[is]).GetScale();
10676 file.Assign(
ChartData->GetDBChartFileName(stackIndexArray[is]));
10684 std::vector<Ais8_001_22 *> area_notices;
10686 if (
g_pAIS && m_bShowAIS && g_bShowAreaNotices) {
10689 for (
const auto &target :
g_pAIS->GetAreaNoticeSourcesList()) {
10690 auto target_data = target.second;
10691 if (!target_data->area_notices.empty()) {
10692 for (
auto &ani : target_data->area_notices) {
10697 for (Ais8_001_22_SubAreaList::iterator sa =
10698 area_notice.sub_areas.begin();
10699 sa != area_notice.sub_areas.end(); ++sa) {
10700 switch (sa->shape) {
10701 case AIS8_001_22_SHAPE_CIRCLE: {
10702 wxPoint target_point;
10704 bbox.Expand(target_point);
10705 if (sa->radius_m > 0.0) bbox.EnLarge(sa->radius_m * vp_scale);
10708 case AIS8_001_22_SHAPE_RECT: {
10709 wxPoint target_point;
10711 bbox.Expand(target_point);
10712 if (sa->e_dim_m > sa->n_dim_m)
10713 bbox.EnLarge(sa->e_dim_m * vp_scale);
10715 bbox.EnLarge(sa->n_dim_m * vp_scale);
10718 case AIS8_001_22_SHAPE_POLYGON:
10719 case AIS8_001_22_SHAPE_POLYLINE: {
10720 for (
int i = 0; i < 4; ++i) {
10721 double lat = sa->latitude;
10722 double lon = sa->longitude;
10723 ll_gc_ll(lat, lon, sa->angles[i], sa->dists_m[i] / 1852.0,
10725 wxPoint target_point;
10727 bbox.Expand(target_point);
10731 case AIS8_001_22_SHAPE_SECTOR: {
10732 double lat1 = sa->latitude;
10733 double lon1 = sa->longitude;
10735 wxPoint target_point;
10737 bbox.Expand(target_point);
10738 for (
int i = 0; i < 18; ++i) {
10741 sa->left_bound_deg +
10742 i * (sa->right_bound_deg - sa->left_bound_deg) / 18,
10743 sa->radius_m / 1852.0, &lat, &lon);
10745 bbox.Expand(target_point);
10747 ll_gc_ll(lat1, lon1, sa->right_bound_deg, sa->radius_m / 1852.0,
10750 bbox.Expand(target_point);
10756 if (bbox.GetValid() && bbox.PointInBox(x, y)) {
10757 area_notices.push_back(&area_notice);
10764 if (target_chart || !area_notices.empty() || file.HasName()) {
10766 int sel_rad_pix = 5;
10767 float SelectRadius = sel_rad_pix / (GetVP().
view_scale_ppm * 1852 * 60);
10772 SetCursor(wxCURSOR_WAIT);
10773 bool lightsVis = m_encShowLights;
10774 if (!lightsVis) SetShowENCLights(
true);
10777 ListOfObjRazRules *rule_list = NULL;
10778 ListOfPI_S57Obj *pi_rule_list = NULL;
10781 Chs57->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &GetVP());
10782 else if (target_plugin_chart)
10783 pi_rule_list =
g_pi_manager->GetPlugInObjRuleListAtLatLon(
10784 target_plugin_chart, zlat, zlon, SelectRadius, GetVP());
10786 ListOfObjRazRules *overlay_rule_list = NULL;
10787 ChartBase *overlay_chart = GetOverlayChartAtCursor();
10790 if (CHs57_Overlay) {
10791 overlay_rule_list = CHs57_Overlay->GetObjRuleListAtLatLon(
10792 zlat, zlon, SelectRadius, &GetVP());
10795 if (!lightsVis) SetShowENCLights(
false);
10798 wxFont *dFont = FontMgr::Get().
GetFont(_(
"ObjectQuery"));
10799 wxString face = dFont->GetFaceName();
10803 new S57QueryDialog(
this, -1, _(
"Object Query"), wxDefaultPosition,
10804 wxSize(g_S57_dialog_sx, g_S57_dialog_sy));
10808 wxColor fg = FontMgr::Get().
GetFontColor(_(
"ObjectQuery"));
10816 "<html><body bgcolor=#%02x%02x%02x><font color=#%02x%02x%02x>",
10817 bg.Red(), bg.Green(), bg.Blue(), fg.Red(), fg.Green(), fg.Blue());
10820 int points = dFont->GetPointSize();
10822 int points = dFont->GetPointSize() + 1;
10826 for (
int i = -2; i < 5; i++) {
10827 sizes[i + 2] = points + i + (i > 0 ? i : 0);
10831 if (wxFONTSTYLE_ITALIC == dFont->GetStyle()) objText +=
"<i>";
10833 if (overlay_rule_list && CHs57_Overlay) {
10834 objText << CHs57_Overlay->CreateObjDescriptions(overlay_rule_list);
10835 objText <<
"<hr noshade>";
10838 for (std::vector<Ais8_001_22 *>::iterator an = area_notices.begin();
10839 an != area_notices.end(); ++an) {
10840 objText <<
"<b>AIS Area Notice:</b> ";
10841 objText << ais8_001_22_notice_names[(*an)->notice_type];
10842 for (std::vector<Ais8_001_22_SubArea>::iterator sa =
10843 (*an)->sub_areas.begin();
10844 sa != (*an)->sub_areas.end(); ++sa)
10845 if (!sa->text.empty()) objText << sa->text;
10846 objText <<
"<br>expires: " << (*an)->expiry_time.Format();
10847 objText <<
"<hr noshade>";
10851 objText << Chs57->CreateObjDescriptions(rule_list);
10852 else if (target_plugin_chart)
10853 objText <<
g_pi_manager->CreateObjDescriptions(target_plugin_chart,
10856 if (wxFONTSTYLE_ITALIC == dFont->GetStyle()) objText <<
"</i>";
10859 wxString AddFiles, filenameOK;
10861 if (!target_plugin_chart) {
10864 AddFiles = wxString::Format(
10865 "<hr noshade><br><b>Additional info files attached to: </b> "
10867 "size=-2>%s</font><br><table border=0 cellspacing=0 "
10869 file.GetFullName());
10871 file.Assign(file.GetPath(),
"");
10872 wxDir dir(file.GetFullPath());
10874 bool cont = dir.GetFirst(&filename,
"", wxDIR_FILES);
10876 file.Assign(dir.GetNameWithSep().append(filename));
10877 wxString FormatString =
10878 "<td valign=top><font size=-2><a "
10879 "href=\"%s\">%s</a></font></td>";
10880 if (g_ObjQFileExt.Find(file.GetExt().Lower()) != wxNOT_FOUND) {
10881 filenameOK = file.GetFullPath();
10883 if (3 * ((
int)filecount / 3) == filecount)
10884 FormatString.Prepend(
"<tr>");
10886 FormatString.Prepend(
10887 "<td>  </td>");
10890 AddFiles << wxString::Format(FormatString, file.GetFullPath(),
10891 file.GetFullName());
10894 cont = dir.GetNext(&filename);
10896 objText << AddFiles <<
"</table>";
10898 objText <<
"</font>";
10899 objText <<
"</body></html>";
10901 if (Chs57 || target_plugin_chart || (filecount > 1)) {
10905 if ((!Chs57 && filecount == 1)) {
10907 wxHtmlLinkInfo hli(filenameOK);
10908 wxHtmlLinkEvent hle(1, hli);
10912 if (rule_list) rule_list->Clear();
10915 if (overlay_rule_list) overlay_rule_list->Clear();
10916 delete overlay_rule_list;
10918 if (pi_rule_list) pi_rule_list->Clear();
10919 delete pi_rule_list;
10921 SetCursor(wxCURSOR_ARROW);
10925void ChartCanvas::ShowMarkPropertiesDialog(
RoutePoint *markPoint) {
10934 wxSize canvas_size = GetSize();
10941 wxPoint canvas_pos = GetPosition();
10944 bool newFit =
false;
10945 if (canvas_size.x < fitted_size.x) {
10946 fitted_size.x = canvas_size.x - 40;
10947 if (canvas_size.y < fitted_size.y)
10948 fitted_size.y -= 40;
10950 if (canvas_size.y < fitted_size.y) {
10951 fitted_size.y = canvas_size.y - 40;
10952 if (canvas_size.x < fitted_size.x)
10953 fitted_size.x -= 40;
10964 wxString title_base = _(
"Mark Properties");
10966 title_base = _(
"Waypoint Properties");
10971 wxString caption(wxString::Format(
"%s, %s: %s", title_base, _(
"Layer"),
10983void ChartCanvas::ShowRoutePropertiesDialog(wxString title,
Route *selected) {
10993 if (g_bresponsive) {
10994 wxSize canvas_size = GetSize();
10995 wxPoint canvas_pos = GetPosition();
10999 if (canvas_size.x < fitted_size.x) {
11000 fitted_size.x = canvas_size.x;
11001 if (canvas_size.y < fitted_size.y)
11002 fitted_size.y -= 20;
11004 if (canvas_size.y < fitted_size.y) {
11005 fitted_size.y = canvas_size.y;
11006 if (canvas_size.x < fitted_size.x)
11007 fitted_size.x -= 20;
11016 wxPoint xxp = ClientToScreen(canvas_pos);
11027void ChartCanvas::ShowTrackPropertiesDialog(
Track *selected) {
11039void pupHandler_PasteWaypoint() {
11042 int pasteBuffer = kml.ParsePasteBuffer();
11043 RoutePoint *pasted = kml.GetParsedRoutePoint();
11044 if (!pasted)
return;
11046 double nearby_radius_meters =
11047 g_Platform->GetSelectRadiusPix() / top_frame::Get()->GetCanvasTrueScale();
11049 RoutePoint *nearPoint = pWayPointMan->GetNearbyWaypoint(
11050 pasted->m_lat, pasted->m_lon, nearby_radius_meters);
11052 int answer = wxID_NO;
11056 "There is an existing waypoint at the same location as the one you are "
11057 "pasting. Would you like to merge the pasted data with it?\n\n");
11058 msg << _(
"Answering 'No' will create a new waypoint at the same location.");
11059 answer = OCPNMessageBox(NULL, msg, _(
"Merge waypoint?"),
11060 (
long)wxYES_NO | wxCANCEL | wxNO_DEFAULT);
11063 if (answer == wxID_YES) {
11064 nearPoint->SetName(pasted->GetName());
11070 if (answer == wxID_NO) {
11073 pSelect->AddSelectableRoutePoint(newPoint->m_lat, newPoint->m_lon,
11076 NavObj_dB::GetInstance().InsertRoutePoint(newPoint);
11085 top_frame::Get()->InvalidateAllGL();
11086 top_frame::Get()->RefreshAllCanvas(
false);
11089void pupHandler_PasteRoute() {
11092 int pasteBuffer = kml.ParsePasteBuffer();
11093 Route *pasted = kml.GetParsedRoute();
11094 if (!pasted)
return;
11096 double nearby_radius_meters =
11097 g_Platform->GetSelectRadiusPix() / top_frame::Get()->GetCanvasTrueScale();
11103 bool mergepoints =
false;
11104 bool createNewRoute =
true;
11105 int existingWaypointCounter = 0;
11107 for (
int i = 1; i <= pasted->GetnPoints(); i++) {
11108 curPoint = pasted->GetPoint(i);
11109 nearPoint = pWayPointMan->GetNearbyWaypoint(
11110 curPoint->m_lat, curPoint->m_lon, nearby_radius_meters);
11112 mergepoints =
true;
11113 existingWaypointCounter++;
11121 int answer = wxID_NO;
11125 "There are existing waypoints at the same location as some of the ones "
11126 "you are pasting. Would you like to just merge the pasted data into "
11128 msg << _(
"Answering 'No' will create all new waypoints for this route.");
11129 answer = OCPNMessageBox(NULL, msg, _(
"Merge waypoints?"),
11130 (
long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
11132 if (answer == wxID_CANCEL) {
11139 if (mergepoints && answer == wxID_YES &&
11140 existingWaypointCounter == pasted->GetnPoints()) {
11143 createNewRoute =
false;
11149 Route *newRoute = 0;
11152 if (createNewRoute) {
11153 newRoute =
new Route();
11157 for (
int i = 1; i <= pasted->GetnPoints(); i++) {
11158 curPoint = pasted->GetPoint(i);
11161 newPoint = pWayPointMan->GetNearbyWaypoint(
11162 curPoint->m_lat, curPoint->m_lon, nearby_radius_meters);
11163 newPoint->SetName(curPoint->GetName());
11166 if (createNewRoute) newRoute->AddPoint(newPoint);
11172 newPoint->SetIconName(
"circle");
11175 newPoint->SetShared(
false);
11177 newRoute->AddPoint(newPoint);
11178 pSelect->AddSelectableRoutePoint(newPoint->m_lat, newPoint->m_lon,
11181 NavObj_dB::GetInstance().InsertRoutePoint(newPoint);
11184 if (i > 1 && createNewRoute)
11185 pSelect->AddSelectableRouteSegment(prevPoint->m_lat, prevPoint->m_lon,
11186 curPoint->m_lat, curPoint->m_lon,
11187 prevPoint, newPoint, newRoute);
11188 prevPoint = newPoint;
11191 if (createNewRoute) {
11194 NavObj_dB::GetInstance().InsertRoute(newRoute);
11204 top_frame::Get()->InvalidateAllGL();
11205 top_frame::Get()->RefreshAllCanvas(
false);
11211void pupHandler_PasteTrack() {
11214 int pasteBuffer = kml.ParsePasteBuffer();
11215 Track *pasted = kml.GetParsedTrack();
11216 if (!pasted)
return;
11224 newTrack->SetName(pasted->GetName());
11226 for (
int i = 0; i < pasted->GetnPoints(); i++) {
11227 curPoint = pasted->GetPoint(i);
11231 wxDateTime now = wxDateTime::Now();
11234 newTrack->AddPoint(newPoint);
11237 pSelect->AddSelectableTrackSegment(prevPoint->m_lat, prevPoint->m_lon,
11238 newPoint->m_lat, newPoint->m_lon,
11239 prevPoint, newPoint, newTrack);
11241 prevPoint = newPoint;
11246 NavObj_dB::GetInstance().InsertTrack(newTrack);
11248 top_frame::Get()->InvalidateAllGL();
11249 top_frame::Get()->RefreshAllCanvas(
false);
11252bool ChartCanvas::InvokeCanvasMenu(
int x,
int y,
int seltype) {
11255 v[
"CursorPosition_x"] = x;
11256 v[
"CursorPosition_y"] = y;
11259 if (seltype & SELTYPE_UNKNOWN) v[
"SelectionType"] =
"Canvas";
11260 if (seltype & SELTYPE_ROUTEPOINT) v[
"SelectionType"] =
"RoutePoint";
11261 if (seltype & SELTYPE_AISTARGET) v[
"SelectionType"] =
"AISTarget";
11263 std::string out = v.dump();
11264 SendMessageToAllPlugins(
"OCPN_CONTEXT_CLICK", out);
11266 json_msg.
Notify(std::make_shared<nlohmann::json>(v),
"OCPN_CONTEXT_CLICK");
11269#define SELTYPE_UNKNOWN 0x0001
11270#define SELTYPE_ROUTEPOINT 0x0002
11271#define SELTYPE_ROUTESEGMENT 0x0004
11272#define SELTYPE_TIDEPOINT 0x0008
11273#define SELTYPE_CURRENTPOINT 0x0010
11274#define SELTYPE_ROUTECREATE 0x0020
11275#define SELTYPE_AISTARGET 0x0040
11276#define SELTYPE_MARKPOINT 0x0080
11277#define SELTYPE_TRACKSEGMENT 0x0100
11278#define SELTYPE_DRAGHANDLE 0x0200
11281 if (g_bhide_context_menus)
return true;
11283 m_pFoundRoutePoint, m_FoundAIS_MMSI,
11284 m_pIDXCandidate, m_nmea_log);
11287 wxEVT_COMMAND_MENU_SELECTED,
11288 (wxObjectEventFunction)(wxEventFunction)&ChartCanvas::PopupMenuHandler);
11294 if (m_inLongPress) {
11295 m_canvasMenu->CanvasPopupMenu(x, y, seltype);
11296 m_inLongPress =
false;
11300 m_canvasMenu->CanvasPopupMenu(x, y, seltype);
11303 wxEVT_COMMAND_MENU_SELECTED,
11304 (wxObjectEventFunction)(wxEventFunction)&ChartCanvas::PopupMenuHandler);
11306 delete m_canvasMenu;
11307 m_canvasMenu = NULL;
11317void ChartCanvas::PopupMenuHandler(wxCommandEvent &event) {
11320 if (m_canvasMenu) {
11321 m_canvasMenu->PopupMenuHandler(event);
11326void ChartCanvas::StartRoute() {
11328 if (g_brouteCreating)
return;
11332 g_brouteCreating =
true;
11334 m_bDrawingRoute =
false;
11335 SetCursor(*pCursorPencil);
11337 top_frame::Get()->SetMasterToolbarItemState(ID_MENU_ROUTE_NEW,
true);
11339 HideGlobalToolbar();
11342 androidSetRouteAnnunciator(
true);
11346wxString ChartCanvas::FinishRoute() {
11348 m_prev_pMousePoint = NULL;
11349 m_bDrawingRoute =
false;
11351 if (m_pMouseRoute) rv = m_pMouseRoute->
m_GUID;
11354 top_frame::Get()->SetMasterToolbarItemState(ID_MENU_ROUTE_NEW,
false);
11356 androidSetRouteAnnunciator(
false);
11359 SetCursor(*pCursorArrow);
11361 if (m_pMouseRoute) {
11362 if (m_bAppendingRoute) {
11363 NavObj_dB::GetInstance().UpdateRoute(m_pMouseRoute);
11365 if (m_pMouseRoute->GetnPoints() > 1) {
11367 NavObj_dB::GetInstance().UpdateRoute(m_pMouseRoute);
11370 m_pMouseRoute = NULL;
11373 if (m_pMouseRoute) m_pMouseRoute->SetHiLite(0);
11385 m_bAppendingRoute =
false;
11386 m_pMouseRoute = NULL;
11388 m_pSelectedRoute = NULL;
11390 undo->InvalidateUndo();
11391 top_frame::Get()->RefreshAllCanvas(
true);
11395 ShowGlobalToolbar();
11397 g_brouteCreating =
false;
11402void ChartCanvas::HideGlobalToolbar() {
11403 if (m_canvasIndex == 0) {
11404 m_last_TBviz = top_frame::Get()->SetGlobalToolbarViz(
false);
11408void ChartCanvas::ShowGlobalToolbar() {
11409 if (m_canvasIndex == 0) {
11410 if (m_last_TBviz) top_frame::Get()->SetGlobalToolbarViz(
true);
11414void ChartCanvas::ShowAISTargetList() {
11415 if (NULL == g_pAISTargetList) {
11419 g_pAISTargetList->UpdateAISTargetList();
11422void ChartCanvas::RenderAllChartOutlines(
ocpnDC &dc,
ViewPort &vp) {
11423 if (!m_bShowOutlines)
return;
11427 int nEntry =
ChartData->GetChartTableEntries();
11429 for (
int i = 0; i < nEntry; i++) {
11433 bool b_group_draw =
false;
11434 if (m_groupIndex > 0) {
11435 for (
unsigned int ig = 0; ig < pt->GetGroupArray().size(); ig++) {
11436 int index = pt->GetGroupArray()[ig];
11437 if (m_groupIndex == index) {
11438 b_group_draw =
true;
11443 b_group_draw =
true;
11446 if (pt->IsBasemap())
continue;
11448 if (b_group_draw) RenderChartOutline(dc, i, vp);
11454 if (VPoint.b_quilt) {
11455 for (
ChartBase *pch = GetFirstQuiltChart(); pch; pch = GetNextQuiltChart())
11456 if (pch->GetChartType() == CHART_TYPE_CM93COMP) {
11460 }
else if (m_singleChart &&
11461 (m_singleChart->GetChartType() == CHART_TYPE_CM93COMP))
11465 double chart_native_ppm = m_canvas_scale_factor / pcm93->GetNativeScale();
11468 if (zoom_factor > 8.0) {
11469 wxPen mPen(GetGlobalColor(
"UINFM"), 2, wxPENSTYLE_SHORT_DASH);
11472 wxPen mPen(GetGlobalColor(
"UINFM"), 1, wxPENSTYLE_SOLID);
11476 pcm93->RenderNextSmallerCellOutlines(dc, vp,
this);
11480void ChartCanvas::RenderChartOutline(
ocpnDC &dc,
int dbIndex,
ViewPort &vp) {
11482 if (g_bopengl && m_glcc) {
11484 m_glcc->RenderChartOutline(dc, dbIndex, vp);
11489 if (
ChartData->GetDBChartType(dbIndex) == CHART_TYPE_PLUGIN) {
11490 if (!
ChartData->IsChartAvailable(dbIndex))
return;
11493 float plylat, plylon;
11494 float plylat1, plylon1;
11496 int pixx, pixy, pixx1, pixy1;
11499 ChartData->GetDBBoundingBox(dbIndex, box);
11503 if (box.GetLonRange() == 360)
return;
11505 double lon_bias = 0;
11507 if (box.IntersectOutGetBias(vp.GetBBox(), lon_bias))
return;
11509 int nPly =
ChartData->GetDBPlyPoint(dbIndex, 0, &plylat, &plylon);
11511 if (
ChartData->GetDBChartType(dbIndex) == CHART_TYPE_CM93)
11512 dc.SetPen(wxPen(GetGlobalColor(
"YELO1"), 1, wxPENSTYLE_SOLID));
11514 else if (
ChartData->GetDBChartFamily(dbIndex) == CHART_FAMILY_VECTOR)
11515 dc.SetPen(wxPen(GetGlobalColor(
"UINFG"), 1, wxPENSTYLE_SOLID));
11518 dc.SetPen(wxPen(GetGlobalColor(
"UINFR"), 1, wxPENSTYLE_SOLID));
11521 int nAuxPlyEntries =
ChartData->GetnAuxPlyEntries(dbIndex);
11522 if (0 == nAuxPlyEntries)
11526 ChartData->GetDBPlyPoint(dbIndex, 0, &plylat, &plylon);
11527 plylon += lon_bias;
11533 for (
int i = 0; i < nPly - 1; i++) {
11534 ChartData->GetDBPlyPoint(dbIndex, i + 1, &plylat1, &plylon1);
11535 plylon1 += lon_bias;
11541 int pixxs1 = pixx1;
11542 int pixys1 = pixy1;
11544 bool b_skip =
false;
11548 double dist = sqrt(pow((
double)(pixx1 - pixx), 2) +
11549 pow((
double)(pixy1 - pixy), 2)) /
11555 DistGreatCircle(plylat, plylon, plylat1, plylon1) * 1852.;
11560 if (fabs(dist - distgc) > 10000. * 1852.)
11566 ClipResult res = cohen_sutherland_line_clip_i(
11568 if (res != Invisible && !b_skip)
11569 dc.
DrawLine(pixx, pixy, pixx1, pixy1,
false);
11577 ChartData->GetDBPlyPoint(dbIndex, 0, &plylat1, &plylon1);
11578 plylon1 += lon_bias;
11584 ClipResult res = cohen_sutherland_line_clip_i(
11586 if (res != Invisible) dc.
DrawLine(pixx, pixy, pixx1, pixy1,
false);
11593 int nAuxPlyEntries =
ChartData->GetnAuxPlyEntries(dbIndex);
11594 for (
int j = 0; j < nAuxPlyEntries; j++) {
11596 ChartData->GetDBAuxPlyPoint(dbIndex, 0, j, &plylat, &plylon);
11601 for (
int i = 0; i < nAuxPly - 1; i++) {
11602 ChartData->GetDBAuxPlyPoint(dbIndex, i + 1, j, &plylat1, &plylon1);
11608 int pixxs1 = pixx1;
11609 int pixys1 = pixy1;
11611 bool b_skip =
false;
11615 double dist = sqrt((
double)((pixx1 - pixx) * (pixx1 - pixx)) +
11616 ((pixy1 - pixy) * (pixy1 - pixy))) /
11621 DistGreatCircle(plylat, plylon, plylat1, plylon1) * 1852.;
11626 if (fabs(dist - distgc) > 10000. * 1852.)
11632 ClipResult res = cohen_sutherland_line_clip_i(
11634 if (res != Invisible && !b_skip) dc.
DrawLine(pixx, pixy, pixx1, pixy1);
11642 ChartData->GetDBAuxPlyPoint(dbIndex, 0, j, &plylat1, &plylon1);
11647 ClipResult res = cohen_sutherland_line_clip_i(
11649 if (res != Invisible) dc.
DrawLine(pixx, pixy, pixx1, pixy1,
false);
11654static void RouteLegInfo(
ocpnDC &dc, wxPoint ref_point,
11655 const wxArrayString &legend) {
11656 wxFont *dFont = FontMgr::Get().
GetFont(_(
"RouteLegInfoRollover"));
11658 int pointsize = dFont->GetPointSize();
11662 pointsize, dFont->GetFamily(), dFont->GetStyle(), dFont->GetWeight(),
11663 false, dFont->GetFaceName());
11665 dc.SetFont(*psRLI_font);
11672 int hilite_offset = 3;
11674 for (wxString line : legend) {
11677 sdc.GetTextExtent(line, &wl, &hl, NULL, NULL, psRLI_font);
11679 dc.GetTextExtent(line, &wl, &hl);
11688 xp = ref_point.x - w;
11690 yp += hilite_offset;
11692 AlphaBlending(dc, xp, yp, w, h, 0.0, GetGlobalColor(
"YELO1"), 172);
11694 dc.SetPen(wxPen(GetGlobalColor(
"UBLCK")));
11695 dc.SetTextForeground(GetGlobalColor(
"UBLCK"));
11697 for (wxString line : legend) {
11698 dc.DrawText(line, xp, yp);
11703void ChartCanvas::RenderShipToActive(
ocpnDC &dc,
bool Use_Opengl) {
11704 if (!g_bAllowShipToActive)
return;
11710 wxPoint2DDouble pa, pb;
11717 if (rt->
m_width != wxPENSTYLE_INVALID)
11719 wxPenStyle style = (wxPenStyle)::StyleValues[wxMin(
11720 g_shipToActiveStyle, 5)];
11721 if (style == wxPENSTYLE_INVALID) style = wxPENSTYLE_SOLID;
11723 g_shipToActiveColor > 0 ? GpxxColors[wxMin(g_shipToActiveColor - 1, 15)]
11726 wxPen *mypen = wxThePenList->FindOrCreatePen(color, width, style);
11729 dc.SetBrush(wxBrush(color, wxBRUSHSTYLE_SOLID));
11732 RouteGui(*rt).RenderSegment(dc, (
int)pa.m_x, (
int)pa.m_y, (
int)pb.m_x,
11733 (
int)pb.m_y, GetVP(),
true);
11737#ifdef USE_ANDROID_GLES2
11738 dc.
DrawLine(pa.m_x, pa.m_y, pb.m_x, pb.m_y);
11740 if (style != wxPENSTYLE_SOLID) {
11741 if (glChartCanvas::dash_map.find(style) !=
11742 glChartCanvas::dash_map.end()) {
11743 mypen->SetDashes(2, &glChartCanvas::dash_map[style][0]);
11747 dc.
DrawLine(pa.m_x, pa.m_y, pb.m_x, pb.m_y);
11750 RouteGui(*rt).RenderSegmentArrowsGL(dc, (
int)pa.m_x, (
int)pa.m_y,
11751 (
int)pb.m_x, (
int)pb.m_y, GetVP());
11757void ChartCanvas::RenderRouteLegs(
ocpnDC &dc) {
11759 if (m_routeState >= 2) route = m_pMouseRoute;
11760 if (m_pMeasureRoute && m_bMeasure_Active && (m_nMeasureState >= 2))
11761 route = m_pMeasureRoute;
11763 if (!route)
return;
11771 int np = route->GetnPoints();
11773 if (g_btouch && (np > 1)) np--;
11775 render_lat = rp.m_lat;
11776 render_lon = rp.m_lon;
11779 double rhumbBearing, rhumbDist;
11781 &rhumbBearing, &rhumbDist);
11782 double brg = rhumbBearing;
11783 double dist = rhumbDist;
11787 double gcBearing, gcBearing2, gcDist;
11788 Geodesic::GreatCircleDistBear(render_lon, render_lat,
m_cursor_lon,
11791 double gcDistm = gcDist / 1852.0;
11794 rhumbBearing = 90.;
11796 wxPoint destPoint, lastPoint;
11799 int milesDiff = rhumbDist - gcDistm;
11800 if (milesDiff > 1) {
11811 for (
int i = 1; i <= milesDiff; i++) {
11812 double p = (double)i * (1.0 / (
double)milesDiff);
11814 Geodesic::GreatCircleTravel(render_lon, render_lat, gcDist * p, brg,
11815 &pLon, &pLat, &gcBearing2);
11817 RouteGui(*route).DrawSegment(dc,
this, &lastPoint, &destPoint, GetVP(),
11819 lastPoint = destPoint;
11822 if (r_rband.x && r_rband.y) {
11823 RouteGui(*route).DrawSegment(dc,
this, &lastPoint, &r_rband, GetVP(),
11825 if (m_bMeasure_DistCircle) {
11826 double distanceRad = sqrtf(powf((
float)(r_rband.x - lastPoint.x), 2) +
11827 powf((
float)(r_rband.y - lastPoint.y), 2));
11830 dc.SetBrush(*wxTRANSPARENT_BRUSH);
11831 dc.StrokeCircle(lastPoint.x, lastPoint.y, distanceRad);
11837 wxString routeInfo;
11838 wxArrayString infoArray;
11841 routeInfo << wxString::Format(wxString(
"%03d%c(T) ", wxConvUTF8), (
int)brg,
11847 varBrg = top_frame::Get()->GetMag(brg, latAverage, lonAverage);
11849 routeInfo << wxString::Format(wxString(
"%03d%c(M) ", wxConvUTF8),
11850 (
int)varBrg, 0x00B0);
11853 infoArray.Add(routeInfo);
11859 routeInfo <<
"Reverse: ";
11861 routeInfo << wxString::Format(wxString(
"%03d%c(T) ", wxConvUTF8),
11862 (
int)(brg + 180.) % 360, 0x00B0);
11864 routeInfo << wxString::Format(wxString(
"%03d%c(M) ", wxConvUTF8),
11865 (
int)(varBrg + 180.) % 360, 0x00B0);
11866 infoArray.Add(routeInfo);
11872 s0.Append(_(
"Route") +
": ");
11874 s0.Append(_(
"Layer Route: "));
11877 if (!g_btouch) disp_length += dist;
11883 RouteLegInfo(dc, r_rband, infoArray);
11885 m_brepaint_piano =
true;
11888void ChartCanvas::RenderVisibleSectorLights(
ocpnDC &dc) {
11889 if (!m_bShowVisibleSectors)
return;
11891 if (g_bDeferredInitDone) {
11893 double rhumbBearing, rhumbDist;
11894 DistanceBearingMercator(
gLat,
gLon, m_sector_glat, m_sector_glon,
11895 &rhumbBearing, &rhumbDist);
11897 if (rhumbDist > 0.05)
11899 s57_GetVisibleLightSectors(
this,
gLat,
gLon, GetVP(),
11900 m_sectorlegsVisible);
11901 m_sector_glat =
gLat;
11902 m_sector_glon =
gLon;
11904 s57_DrawExtendedLightSectors(dc, VPoint, m_sectorlegsVisible);
11908void ChartCanvas::WarpPointerDeferred(
int x,
int y) {
11916void ChartCanvas::UpdateCanvasS52PLIBConfig() {
11917 if (!ps52plib)
return;
11919 if (VPoint.b_quilt) {
11920 if (!m_pQuilt || !m_pQuilt->IsComposed())
return;
11922 if (m_pQuilt->IsQuiltVector()) {
11923 if (ps52plib->GetStateHash() != m_s52StateHash) {
11925 m_s52StateHash = ps52plib->GetStateHash();
11929 if (ps52plib->GetStateHash() != m_s52StateHash) {
11931 m_s52StateHash = ps52plib->GetStateHash();
11936 bool bSendPlibState =
true;
11937 if (VPoint.b_quilt) {
11938 if (!m_pQuilt->DoesQuiltContainPlugins()) bSendPlibState =
false;
11941 if (bSendPlibState) {
11943 v[
"OpenCPN Version Major"] = VERSION_MAJOR;
11944 v[
"OpenCPN Version Minor"] = VERSION_MINOR;
11945 v[
"OpenCPN Version Patch"] = VERSION_PATCH;
11946 v[
"OpenCPN Version Date"] = VERSION_DATE;
11947 v[
"OpenCPN Version Full"] = VERSION_FULL;
11950 v[
"OpenCPN S52PLIB ShowText"] = GetShowENCText();
11951 v[
"OpenCPN S52PLIB ShowSoundings"] = GetShowENCDepth();
11952 v[
"OpenCPN S52PLIB ShowLights"] = GetShowENCLights();
11953 v[
"OpenCPN S52PLIB ShowAnchorConditions"] = m_encShowAnchor;
11954 v[
"OpenCPN S52PLIB ShowQualityOfData"] = GetShowENCDataQual();
11955 v[
"OpenCPN S52PLIB ShowATONLabel"] = GetShowENCBuoyLabels();
11956 v[
"OpenCPN S52PLIB ShowLightDescription"] = GetShowENCLightDesc();
11960 v[
"OpenCPN S52PLIB SoundingsFactor"] = g_ENCSoundingScaleFactor;
11961 v[
"OpenCPN S52PLIB TextFactor"] = g_ENCTextScaleFactor;
11965 v[
"OpenCPN S52PLIB MetaDisplay"] = ps52plib->m_bShowMeta;
11966 v[
"OpenCPN S52PLIB DeclutterText"] = ps52plib->m_bDeClutterText;
11967 v[
"OpenCPN S52PLIB ShowNationalText"] = ps52plib->m_bShowNationalTexts;
11968 v[
"OpenCPN S52PLIB ShowImportantTextOnly"] =
11969 ps52plib->m_bShowS57ImportantTextOnly;
11970 v[
"OpenCPN S52PLIB UseSCAMIN"] = ps52plib->m_bUseSCAMIN;
11971 v[
"OpenCPN S52PLIB UseSUPER_SCAMIN"] = ps52plib->m_bUseSUPER_SCAMIN;
11972 v[
"OpenCPN S52PLIB SymbolStyle"] = ps52plib->m_nSymbolStyle;
11973 v[
"OpenCPN S52PLIB BoundaryStyle"] = ps52plib->m_nBoundaryStyle;
11974 v[
"OpenCPN S52PLIB ColorShades"] = S52_getMarinerParam(S52_MAR_TWO_SHADES);
11977 v[
"OpenCPN Zoom Mod Vector"] = g_chart_zoom_modifier_vector;
11978 v[
"OpenCPN Zoom Mod Other"] = g_chart_zoom_modifier_raster;
11979 v[
"OpenCPN Scale Factor Exp"] =
11980 g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor);
11983 const std::string out = v.dump();
11985 if (!g_lastS52PLIBPluginMessage.IsSameAs(out)) {
11986 SendMessageToAllPlugins(
"OpenCPN Config", out);
11987 g_lastS52PLIBPluginMessage = out;
11994 wxPaintDC dc(
this);
12004 if (!m_b_paint_enable) {
12012 if (!g_bdisable_opengl && m_glcc) m_glcc->Show(g_bopengl);
12014 if (m_glcc && g_bopengl) {
12015 if (!s_in_update) {
12025 if ((GetVP().pix_width == 0) || (GetVP().pix_height == 0))
return;
12027 wxRegion ru = GetUpdateRegion();
12029 int rx, ry, rwidth, rheight;
12030 ru.GetBox(rx, ry, rwidth, rheight);
12032#ifdef ocpnUSE_DIBSECTION
12035 wxMemoryDC temp_dc;
12043 if (!style->chartStatusWindowTransparent && g_bShowChartBar)
12044 height += m_Piano->GetHeight();
12046 wxRegion rgn_chart(0, 0, GetVP().pix_width, height);
12050 int thumbx, thumby, thumbsx, thumbsy;
12051 pthumbwin->GetPosition(&thumbx, &thumby);
12052 pthumbwin->GetSize(&thumbsx, &thumbsy);
12053 wxRegion rgn_thumbwin(thumbx, thumby, thumbsx - 1, thumbsy - 1);
12056 rgn_chart.Subtract(rgn_thumbwin);
12057 ru.Subtract(rgn_thumbwin);
12063 wxRegion rgn_blit = ru;
12064 if (g_bShowChartBar) {
12065 wxRect chart_bar_rect(0, GetClientSize().y - m_Piano->GetHeight(),
12066 GetClientSize().x, m_Piano->GetHeight());
12069 if (ru.Contains(chart_bar_rect) != wxOutRegion) {
12070 if (style->chartStatusWindowTransparent)
12071 m_brepaint_piano =
true;
12073 ru.Subtract(chart_bar_rect);
12077 if (m_Compass && m_Compass->IsShown()) {
12078 wxRect compassRect = m_Compass->
GetRect();
12079 if (ru.Contains(compassRect) != wxOutRegion) {
12080 ru.Subtract(compassRect);
12084 if (m_notification_button) {
12085 wxRect noteRect = m_notification_button->
GetRect();
12086 if (ru.Contains(noteRect) != wxOutRegion) {
12087 ru.Subtract(noteRect);
12092 bool b_newview =
true;
12097 m_cache_vp.IsValid()) {
12103 bool b_rcache_ok =
false;
12104 if (fabs(VPoint.
skew) > 0.01 || fabs(VPoint.
rotation) > 0.01)
12105 b_rcache_ok = !b_newview;
12108 if (VPoint.b_MercatorProjectionOverride)
12109 VPoint.SetProjectionType(PROJECTION_MERCATOR);
12123 if (b_rcache_ok) chart_get_region.Clear();
12126 if (VPoint.b_quilt)
12128 if (!m_pQuilt || !m_pQuilt->IsComposed())
return;
12130 bool bvectorQuilt = m_pQuilt->IsQuiltVector();
12135 AbstractPlatform::ShowBusySpinner();
12139 if ((m_working_bm.GetWidth() != svp.
pix_width) ||
12140 (m_working_bm.GetHeight() != svp.
pix_height))
12144 if (fabs(VPoint.
rotation) < 0.01) {
12145 bool b_save =
true;
12150 m_cache_vp.Invalidate();
12164 if (m_bm_cache_vp.IsValid() && m_cache_vp.IsValid() ) {
12169 int dy = c_new.y - c_old.y;
12170 int dx = c_new.x - c_old.x;
12175 if (m_pQuilt->IsVPBlittable(VPoint, dx, dy,
true)) {
12179 temp_dc.SelectObject(m_working_bm);
12181 wxMemoryDC cache_dc;
12182 cache_dc.SelectObject(m_cached_chart_bm);
12186 temp_dc.Blit(0, 0, VPoint.
pix_width - dx,
12189 temp_dc.Blit(-dx, 0, VPoint.
pix_width + dx,
12195 temp_dc.Blit(0, -dy, VPoint.
pix_width - dx,
12198 temp_dc.Blit(-dx, -dy, VPoint.
pix_width + dx,
12206 update_region.Union(
12209 update_region.Union(wxRect(0, 0, VPoint.
pix_width, -dy));
12214 update_region.Union(
12217 update_region.Union(wxRect(0, 0, -dx, VPoint.
pix_height));
12221 m_pQuilt->RenderQuiltRegionViewOnDCNoText(temp_dc, svp,
12223 cache_dc.SelectObject(wxNullBitmap);
12227 temp_dc.SelectObject(m_cached_chart_bm);
12230 m_pQuilt->ComputeRenderRegion(svp, chart_get_region);
12234 temp_dc.SelectObject(m_working_bm);
12235 m_pQuilt->RenderQuiltRegionViewOnDCNoText(temp_dc, svp,
12240 temp_dc.SelectObject(m_cached_chart_bm);
12245 temp_dc.SelectObject(m_working_bm);
12246 m_pQuilt->RenderQuiltRegionViewOnDCNoText(temp_dc, svp,
12259 wxMemoryDC scratch_dc_0;
12260 scratch_dc_0.SelectObject(m_cached_chart_bm);
12263 scratch_dc_0.SelectObject(wxNullBitmap);
12272 temp_dc.SelectObject(m_working_bm);
12275 m_pQuilt->RenderQuiltRegionViewOnDCNoText(temp_dc, svp,
12276 chart_get_all_region);
12279 AbstractPlatform::HideBusySpinner();
12285 if (!m_singleChart) {
12286 dc.SetBackground(wxBrush(*wxLIGHT_GREY));
12291 if (!chart_get_region.IsEmpty()) {
12292 m_singleChart->RenderRegionViewOnDC(temp_dc, svp, chart_get_region);
12296 if (temp_dc.IsOk()) {
12301 if (!VPoint.b_quilt) {
12304 if (m_singleChart->GetChartFamily() == CHART_FAMILY_VECTOR)
12305 m_singleChart->GetValidCanvasRegion(svp, &chartValidRegion);
12312 m_singleChart->GetValidCanvasRegion(VPoint, &chartValidRegion);
12313 chartValidRegion.Offset(-VPoint.rv_rect.x, -VPoint.rv_rect.y);
12316 chartValidRegion = m_pQuilt->GetFullQuiltRenderedRegion();
12318 temp_dc.DestroyClippingRegion();
12323 if (chartValidRegion.IsOk()) backgroundRegion.Subtract(chartValidRegion);
12325 if (!backgroundRegion.IsEmpty()) {
12331 wxColour water = pWorldBackgroundChart->water;
12332 if (water.IsOk()) {
12333 temp_dc.SetPen(*wxTRANSPARENT_PEN);
12334 temp_dc.SetBrush(wxBrush(water));
12336 while (upd.HaveRects()) {
12337 wxRect rect = upd.GetRect();
12338 temp_dc.DrawRectangle(rect);
12343 wxRegion *clip_region = backgroundRegion.GetNew_wxRegion();
12344 temp_dc.SetDeviceClippingRegion(*clip_region);
12345 delete clip_region;
12349 SetVPRotation(VPoint.
skew);
12358 wxMemoryDC *pChartDC = &temp_dc;
12359 wxMemoryDC rotd_dc;
12361 if (((fabs(GetVP().rotation) > 0.01)) || (fabs(GetVP().skew) > 0.01)) {
12363 if (!b_rcache_ok) {
12365 wxMemoryDC tbase_dc;
12367 tbase_dc.SelectObject(bm_base);
12369 tbase_dc.SelectObject(wxNullBitmap);
12371 const wxBitmap &bm_base = temp_dc.GetSelectedBitmap();
12374 wxImage base_image;
12375 if (bm_base.IsOk()) base_image = bm_base.ConvertToImage();
12383 bool b_rot_ok =
false;
12384 if (base_image.IsOk()) {
12387 m_b_rot_hidef =
false;
12391 wxPoint(GetVP().rv_rect.width / 2, GetVP().rv_rect.height / 2),
12392 m_b_rot_hidef, &m_roffset);
12397 rot_vp.IsValid() && (ri.IsOk())) {
12404 m_prot_bm =
new wxBitmap(ri);
12407 m_roffset.x += VPoint.rv_rect.x;
12408 m_roffset.y += VPoint.rv_rect.y;
12411 if (m_prot_bm && m_prot_bm->IsOk()) {
12412 rotd_dc.SelectObject(*m_prot_bm);
12413 pChartDC = &rotd_dc;
12415 pChartDC = &temp_dc;
12416 m_roffset = wxPoint(0, 0);
12419 pChartDC = &temp_dc;
12420 m_roffset = wxPoint(0, 0);
12423 wxPoint offset = m_roffset;
12426 m_cache_vp = VPoint;
12429 wxMemoryDC mscratch_dc;
12430 mscratch_dc.SelectObject(*pscratch_bm);
12432 mscratch_dc.ResetBoundingBox();
12433 mscratch_dc.DestroyClippingRegion();
12434 mscratch_dc.SetDeviceClippingRegion(rgn_chart);
12437 wxRegionIterator upd(rgn_blit);
12439 wxRect rect = upd.GetRect();
12441 mscratch_dc.Blit(rect.x, rect.y, rect.width, rect.height, pChartDC,
12442 rect.x - offset.x, rect.y - offset.y);
12448 if (m_show_focus_bar && (g_canvasConfig != 0)) {
12449 if (
this == wxWindow::FindFocus()) {
12452 wxColour colour = GetGlobalColor(
"BLUE4");
12453 mscratch_dc.SetPen(wxPen(colour));
12454 mscratch_dc.SetBrush(wxBrush(colour));
12456 wxRect activeRect(0, 0, GetClientSize().x, m_focus_indicator_pix);
12457 mscratch_dc.DrawRectangle(activeRect);
12462 std::vector<int> stackIndexArray = m_pQuilt->GetExtendedStackIndexArray();
12463 unsigned int im = stackIndexArray.size();
12464 if (VPoint.b_quilt && im > 0) {
12465 std::vector<int> tiles_to_show;
12466 for (
unsigned int is = 0; is < im; is++) {
12468 ChartData->GetChartTableEntry(stackIndexArray[is]);
12469 if (IsTileOverlayIndexInNoShow(stackIndexArray[is])) {
12472 if (cte.GetChartType() == CHART_TYPE_MBTILES) {
12473 tiles_to_show.push_back(stackIndexArray[is]);
12477 if (tiles_to_show.size())
12478 SetAlertString(_(
"MBTile requires OpenGL to be enabled"));
12484 ocpnDC scratch_dc(mscratch_dc);
12485 RenderAlertMessage(mscratch_dc, GetVP());
12491#ifdef ocpnUSE_DIBSECTION
12496 wxBitmap qbm(GetVP().pix_width, GetVP().pix_height);
12497 q_dc.SelectObject(qbm);
12500 q_dc.Blit(0, 0, GetVP().pix_width, GetVP().pix_height, &mscratch_dc, 0, 0);
12503 wxBrush qbr(*wxBLACK, wxBRUSHSTYLE_FDIAGONAL_HATCH);
12504 q_dc.SetBrush(qbr);
12505 q_dc.DrawRectangle(0, 0, GetVP().pix_width, GetVP().pix_height);
12508 mscratch_dc.Blit(0, 0, GetVP().pix_width, GetVP().pix_height, &q_dc, 0, 0,
12511 q_dc.SelectObject(wxNullBitmap);
12520 if( VPoint.b_quilt ) {
12521 if(m_pQuilt->IsQuiltVector() && ps52plib && ps52plib->GetShowS57Text()){
12522 ChartBase *chart = m_pQuilt->GetRefChart();
12523 if( chart && chart->GetChartType() != CHART_TYPE_CM93COMP){
12528 ChPI->ClearPLIBTextList();
12531 ps52plib->ClearTextList();
12535 wxBitmap qbm( GetVP().pix_width, GetVP().pix_height );
12537 wxColor maskBackground = wxColour(1,0,0);
12538 t_dc.SelectObject( qbm );
12539 t_dc.SetBackground(wxBrush(maskBackground));
12543 t_dc.Blit( 0, 0, GetVP().pix_width, GetVP().pix_height, scratch_dc.GetDC(), 0, 0, wxCOPY );
12546 OCPNRegion chart_all_text_region( wxRect( 0, 0, GetVP().pix_width, GetVP().pix_height ) );
12547 m_pQuilt->RenderQuiltRegionViewOnDCTextOnly( t_dc, svp, chart_all_text_region );
12550 wxRegionIterator upd_final( ru );
12551 while( upd_final ) {
12552 wxRect rect = upd_final.GetRect();
12553 scratch_dc.GetDC()->Blit( rect.x, rect.y, rect.width, rect.height, &t_dc, rect.x, rect.y, wxCOPY,
true );
12557 t_dc.SelectObject( wxNullBitmap );
12563 if (VPoint.b_quilt) {
12564 if (m_pQuilt->IsQuiltVector() && ps52plib && ps52plib->GetShowS57Text()) {
12565 ChartBase *chart = m_pQuilt->GetRefChart();
12566 if (chart && chart->GetChartType() != CHART_TYPE_CM93COMP) {
12570 ChPI->ClearPLIBTextList();
12572 if (ps52plib) ps52plib->ClearTextList();
12577 wxRect(0, 0, GetVP().pix_width, GetVP().pix_height));
12579 if (g_bShowChartBar && m_Piano) {
12580 wxRect chart_bar_rect(0, GetVP().pix_height - m_Piano->GetHeight(),
12581 GetVP().pix_width, m_Piano->GetHeight());
12584 if (!style->chartStatusWindowTransparent)
12585 chart_all_text_region.Subtract(chart_bar_rect);
12588 if (m_Compass && m_Compass->IsShown()) {
12589 wxRect compassRect = m_Compass->
GetRect();
12590 if (chart_all_text_region.Contains(compassRect) != wxOutRegion) {
12591 chart_all_text_region.Subtract(compassRect);
12595 mscratch_dc.DestroyClippingRegion();
12597 m_pQuilt->RenderQuiltRegionViewOnDCTextOnly(mscratch_dc, svp,
12598 chart_all_text_region);
12604 ocpnDC scratch_dc(mscratch_dc);
12605 DrawOverlayObjects(scratch_dc, ru);
12608 wxRegionIterator upd_final(rgn_blit);
12609 while (upd_final) {
12610 wxRect rect = upd_final.GetRect();
12611 dc.Blit(rect.x, rect.y, rect.width, rect.height, &mscratch_dc, rect.x,
12618 temp_dc.SelectObject(wxNullBitmap);
12620 mscratch_dc.SelectObject(wxNullBitmap);
12622 dc.DestroyClippingRegion();
12627void ChartCanvas::PaintCleanup() {
12629 if (m_inPinch)
return;
12640 m_bTCupdate =
false;
12644 WarpPointer(warp_x, warp_y);
12651 pMovementTimer->Start(1, wxTIMER_ONE_SHOT);
12652 m_VPMovementTimer.Start(1, wxTIMER_ONE_SHOT);
12656wxColour GetErrorGraphicColor(
double val)
12675 if((val > 0) && (val < 1)) c.Set(
"#002ad9");
12676 else if((val >= 1) && (val < 2)) c.Set(
"#006ed9");
12677 else if((val >= 2) && (val < 3)) c.Set(
"#00b2d9");
12678 else if((val >= 3) && (val < 4)) c.Set(
"#00d4d4");
12679 else if((val >= 4) && (val < 5)) c.Set(
"#00d9a6");
12680 else if((val >= 5) && (val < 7)) c.Set(
"#00d900");
12681 else if((val >= 7) && (val < 9)) c.Set(
"#95d900");
12682 else if((val >= 9) && (val < 12)) c.Set(
"#d9d900");
12683 else if((val >= 12) && (val < 15)) c.Set(
"#d9ae00");
12684 else if((val >= 15) && (val < 18)) c.Set(
"#d98300");
12685 else if((val >= 18) && (val < 21)) c.Set(
"#d95700");
12686 else if((val >= 21) && (val < 24)) c.Set(
"#d90000");
12687 else if((val >= 24) && (val < 27)) c.Set(
"#ae0000");
12688 else if((val >= 27) && (val < 30)) c.Set(
"#8c0000");
12689 else if((val >= 30) && (val < 36)) c.Set(
"#870000");
12690 else if((val >= 36) && (val < 42)) c.Set(
"#690000");
12691 else if((val >= 42) && (val < 48)) c.Set(
"#550000");
12692 else if( val >= 48) c.Set(
"#410000");
12697void ChartCanvas::RenderGeorefErrorMap( wxMemoryDC *pmdc,
ViewPort *vp)
12700 gr_image.InitAlpha();
12702 double maxval = -10000;
12703 double minval = 10000;
12720 maxval = wxMax(maxval, (glat - rlat));
12721 minval = wxMin(minval, (glat - rlat));
12738 double f = ((glat - rlat)-minval)/(maxval - minval);
12740 double dy = (f * 40);
12742 wxColour c = GetErrorGraphicColor(dy);
12743 unsigned char r = c.Red();
12744 unsigned char g = c.Green();
12745 unsigned char b = c.Blue();
12747 gr_image.SetRGB(j, i, r,g,b);
12748 if((glat - rlat )!= 0)
12749 gr_image.SetAlpha(j, i, 128);
12751 gr_image.SetAlpha(j, i, 255);
12758 wxBitmap *pbm =
new wxBitmap(gr_image);
12759 wxMask *gr_mask =
new wxMask(*pbm, wxColour(0,0,0));
12760 pbm->SetMask(gr_mask);
12762 pmdc->DrawBitmap(*pbm, 0,0);
12770void ChartCanvas::CancelMouseRoute() {
12772 m_pMouseRoute = NULL;
12773 m_bDrawingRoute =
false;
12776int ChartCanvas::GetNextContextMenuId() {
12777 return CanvasMenuHandler::GetNextContextMenuId();
12780bool ChartCanvas::SetCursor(
const wxCursor &c) {
12782 if (g_bopengl && m_glcc)
12783 return m_glcc->SetCursor(c);
12786 return wxWindow::SetCursor(c);
12789void ChartCanvas::Refresh(
bool eraseBackground,
const wxRect *rect) {
12790 if (g_bquiting)
return;
12800 if (!m_RolloverPopupTimer.IsRunning() &&
12801 ((m_pRouteRolloverWin && m_pRouteRolloverWin->IsActive()) ||
12802 (m_pTrackRolloverWin && m_pTrackRolloverWin->IsActive()) ||
12803 (m_pAISRolloverWin && m_pAISRolloverWin->IsActive())))
12804 m_RolloverPopupTimer.Start(500, wxTIMER_ONE_SHOT);
12807 if (m_glcc && g_bopengl) {
12810 if (eraseBackground && m_glcc->UsingFBO()) m_glcc->Invalidate();
12812 m_glcc->Refresh(eraseBackground,
12829 if (m_pCIWin && m_pCIWin->IsShown()) {
12831 m_pCIWin->Refresh(
false);
12839 wxWindow::Refresh(eraseBackground, rect);
12842void ChartCanvas::Update() {
12843 if (m_glcc && g_bopengl) {
12848 wxWindow::Update();
12852 if (!pemboss)
return;
12853 int x = pemboss->x, y = pemboss->y;
12854 const double factor = 200;
12856 wxASSERT_MSG(dc.GetDC(),
"DrawEmboss has no dc (opengl?)");
12857 wxMemoryDC *pmdc =
dynamic_cast<wxMemoryDC *
>(dc.GetDC());
12858 wxASSERT_MSG(pmdc,
"dc to EmbossCanvas not a memory dc");
12861 wxMemoryDC snip_dc;
12862 wxBitmap snip_bmp(pemboss->width, pemboss->height, -1);
12863 snip_dc.SelectObject(snip_bmp);
12865 snip_dc.Blit(0, 0, pemboss->width, pemboss->height, pmdc, x, y);
12866 snip_dc.SelectObject(wxNullBitmap);
12868 wxImage snip_img = snip_bmp.ConvertToImage();
12871 unsigned char *pdata = snip_img.GetData();
12873 for (
int y = 0; y < pemboss->height; y++) {
12874 int map_index = (y * pemboss->width);
12875 for (
int x = 0; x < pemboss->width; x++) {
12876 double val = (pemboss->pmap[map_index] * factor) / 256.;
12878 int nred = (int)((*pdata) + val);
12879 nred = nred > 255 ? 255 : (nred < 0 ? 0 : nred);
12880 *pdata++ = (
unsigned char)nred;
12882 int ngreen = (int)((*pdata) + val);
12883 ngreen = ngreen > 255 ? 255 : (ngreen < 0 ? 0 : ngreen);
12884 *pdata++ = (
unsigned char)ngreen;
12886 int nblue = (int)((*pdata) + val);
12887 nblue = nblue > 255 ? 255 : (nblue < 0 ? 0 : nblue);
12888 *pdata++ = (
unsigned char)nblue;
12896 wxBitmap emb_bmp(snip_img);
12899 wxMemoryDC result_dc;
12900 result_dc.SelectObject(emb_bmp);
12903 pmdc->Blit(x, y, pemboss->width, pemboss->height, &result_dc, 0, 0);
12905 result_dc.SelectObject(wxNullBitmap);
12911 if (GetQuiltMode()) {
12913 int refIndex = GetQuiltRefChartdbIndex();
12914 if (refIndex >= 0) {
12916 ChartTypeEnum current_type = (ChartTypeEnum)cte.GetChartType();
12917 if (current_type == CHART_TYPE_MBTILES) {
12918 ChartBase *pChart = m_pQuilt->GetRefChart();
12921 zoom_factor = ptc->GetZoomFactor();
12926 if (zoom_factor <= 3.9)
return NULL;
12928 if (m_singleChart) {
12929 if (zoom_factor <= 3.9)
return NULL;
12934 if (m_pEM_OverZoom) {
12935 m_pEM_OverZoom->x = 4;
12936 m_pEM_OverZoom->y = 0;
12938 wxRect masterToolbarRect =
g_MainToolbar->GetToolbarRect();
12939 m_pEM_OverZoom->x = masterToolbarRect.width + 4;
12942 return m_pEM_OverZoom;
12945void ChartCanvas::DrawOverlayObjects(
ocpnDC &dc,
const wxRegion &ru) {
12958 g_pi_manager->SendViewPortToRequestingPlugIns(GetVP());
12959 g_pi_manager->RenderAllCanvasOverlayPlugIns(dc, GetVP(), m_canvasIndex,
12963 AISDrawAreaNotices(dc, GetVP(),
this);
12965 wxDC *pdc = dc.GetDC();
12967 pdc->DestroyClippingRegion();
12968 wxDCClipper(*pdc, ru);
12971 if (m_bShowNavobjects) {
12972 DrawAllTracksInBBox(dc, GetVP().GetBBox());
12973 DrawAllRoutesInBBox(dc, GetVP().GetBBox());
12974 DrawAllWaypointsInBBox(dc, GetVP().GetBBox());
12975 DrawAnchorWatchPoints(dc);
12977 DrawActiveTrackInBBox(dc, GetVP().GetBBox());
12978 DrawActiveRouteInBBox(dc, GetVP().GetBBox());
12981 AISDraw(dc, GetVP(),
this);
12985 RenderVisibleSectorLights(dc);
12987 RenderAllChartOutlines(dc, GetVP());
12988 RenderRouteLegs(dc);
12989 RenderShipToActive(dc,
false);
12991 s57_DrawExtendedLightSectors(dc, VPoint, extendedSectorLegs);
12993 g_pi_manager->RenderAllCanvasOverlayPlugIns(dc, GetVP(), m_canvasIndex,
12997 if (!g_bhide_depth_units) DrawEmboss(dc, EmbossDepthScale());
12998 if (!g_bhide_overzoom_flag) DrawEmboss(dc, EmbossOverzoomIndicator(dc));
13001 g_pi_manager->RenderAllCanvasOverlayPlugIns(dc, GetVP(), m_canvasIndex,
13006 RebuildTideSelectList(GetVP().GetBBox());
13007 DrawAllTidesInBBox(dc, GetVP().GetBBox());
13010 if (m_bShowCurrent) {
13011 RebuildCurrentSelectList(GetVP().GetBBox());
13012 DrawAllCurrentsInBBox(dc, GetVP().GetBBox());
13015 if (!g_PrintingInProgress) {
13016 if (IsPrimaryCanvas()) {
13020 if (IsPrimaryCanvas()) {
13024 if (m_muiBar) m_muiBar->DrawDC(dc, 1.0);
13026 if (m_pTrackRolloverWin) {
13027 m_pTrackRolloverWin->Draw(dc);
13028 m_brepaint_piano =
true;
13031 if (m_pRouteRolloverWin) {
13032 m_pRouteRolloverWin->Draw(dc);
13033 m_brepaint_piano =
true;
13036 if (m_pAISRolloverWin) {
13037 m_pAISRolloverWin->Draw(dc);
13038 m_brepaint_piano =
true;
13040 if (m_brepaint_piano && g_bShowChartBar) {
13041 m_Piano->Paint(GetClientSize().y - m_Piano->GetHeight(), dc);
13044 if (m_Compass) m_Compass->Paint(dc);
13046 if (!g_CanvasHideNotificationIcon) {
13047 if (IsPrimaryCanvas()) {
13048 auto ¬eman = NotificationManager::GetInstance();
13049 if (m_notification_button) {
13050 if (noteman.GetNotificationCount()) {
13051 m_notification_button->SetIconSeverity(noteman.GetMaxSeverity());
13052 if (m_notification_button->UpdateStatus()) Refresh();
13053 m_notification_button->Show(
true);
13054 m_notification_button->Paint(dc);
13056 m_notification_button->Show(
false);
13063 g_pi_manager->RenderAllCanvasOverlayPlugIns(dc, GetVP(), m_canvasIndex,
13069 if (!m_bShowDepthUnits)
return NULL;
13071 int depth_unit_type = DEPTH_UNIT_UNKNOWN;
13073 if (GetQuiltMode()) {
13074 wxString s = m_pQuilt->GetQuiltDepthUnit();
13077 depth_unit_type = DEPTH_UNIT_FEET;
13078 else if (s.StartsWith(
"FATHOMS"))
13079 depth_unit_type = DEPTH_UNIT_FATHOMS;
13080 else if (s.StartsWith(
"METERS"))
13081 depth_unit_type = DEPTH_UNIT_METERS;
13082 else if (s.StartsWith(
"METRES"))
13083 depth_unit_type = DEPTH_UNIT_METERS;
13084 else if (s.StartsWith(
"METRIC"))
13085 depth_unit_type = DEPTH_UNIT_METERS;
13086 else if (s.StartsWith(
"METER"))
13087 depth_unit_type = DEPTH_UNIT_METERS;
13090 if (m_singleChart) {
13091 depth_unit_type = m_singleChart->GetDepthUnitType();
13092 if (m_singleChart->GetChartFamily() == CHART_FAMILY_VECTOR)
13093 depth_unit_type = ps52plib->m_nDepthUnitDisplay + 1;
13098 switch (depth_unit_type) {
13099 case DEPTH_UNIT_FEET:
13102 case DEPTH_UNIT_METERS:
13103 ped = m_pEM_Meters;
13105 case DEPTH_UNIT_FATHOMS:
13106 ped = m_pEM_Fathoms;
13112 ped->x = (GetVP().
pix_width - ped->width);
13114 if (m_Compass && m_bShowCompassWin && g_bShowCompassWin) {
13115 wxRect r = m_Compass->
GetRect();
13116 ped->y = r.y + r.height;
13123void ChartCanvas::CreateDepthUnitEmbossMaps(ColorScheme cs) {
13126 if (style->embossFont == wxEmptyString) {
13127 wxFont *dFont = FontMgr::Get().
GetFont(_(
"Dialog"), 0);
13129 font.SetPointSize(60);
13130 font.SetWeight(wxFONTWEIGHT_BOLD);
13132 font = wxFont(style->embossHeight, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
13133 wxFONTWEIGHT_BOLD,
false, style->embossFont);
13135 int emboss_width = 500;
13136 int emboss_height = 200;
13140 delete m_pEM_Meters;
13141 delete m_pEM_Fathoms;
13145 CreateEmbossMapData(font, emboss_width, emboss_height, _(
"Feet"), cs);
13147 CreateEmbossMapData(font, emboss_width, emboss_height, _(
"Meters"), cs);
13149 CreateEmbossMapData(font, emboss_width, emboss_height, _(
"Fathoms"), cs);
13152#define OVERZOOM_TEXT _("OverZoom")
13154void ChartCanvas::SetOverzoomFont() {
13159 if (style->embossFont == wxEmptyString) {
13160 wxFont *dFont = FontMgr::Get().
GetFont(_(
"Dialog"), 0);
13162 font.SetPointSize(40);
13163 font.SetWeight(wxFONTWEIGHT_BOLD);
13165 font = wxFont(style->embossHeight, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
13166 wxFONTWEIGHT_BOLD,
false, style->embossFont);
13168 wxClientDC dc(
this);
13170 dc.GetTextExtent(OVERZOOM_TEXT, &w, &h);
13172 while (font.GetPointSize() > 10 && (w > 500 || h > 100)) {
13173 font.SetPointSize(font.GetPointSize() - 1);
13175 dc.GetTextExtent(OVERZOOM_TEXT, &w, &h);
13177 m_overzoomFont = font;
13178 m_overzoomTextWidth = w;
13179 m_overzoomTextHeight = h;
13182void ChartCanvas::CreateOZEmbossMapData(ColorScheme cs) {
13183 delete m_pEM_OverZoom;
13185 if (m_overzoomTextWidth > 0 && m_overzoomTextHeight > 0)
13187 CreateEmbossMapData(m_overzoomFont, m_overzoomTextWidth + 10,
13188 m_overzoomTextHeight + 10, OVERZOOM_TEXT, cs);
13191emboss_data *ChartCanvas::CreateEmbossMapData(wxFont &font,
int width,
13192 int height,
const wxString &str,
13197 wxBitmap bmp(width, height, -1);
13200 wxMemoryDC temp_dc;
13201 temp_dc.SelectObject(bmp);
13204 temp_dc.SetBackground(*wxWHITE_BRUSH);
13205 temp_dc.SetTextBackground(*wxWHITE);
13206 temp_dc.SetTextForeground(*wxBLACK);
13210 temp_dc.SetFont(font);
13213 temp_dc.GetTextExtent(str, &str_w, &str_h);
13215 temp_dc.DrawText(str, 1, 1);
13218 temp_dc.SelectObject(wxNullBitmap);
13221 wxImage img = bmp.ConvertToImage();
13223 int image_width = str_w * 105 / 100;
13224 int image_height = str_h * 105 / 100;
13225 wxRect r(0, 0, wxMin(image_width, img.GetWidth()),
13226 wxMin(image_height, img.GetHeight()));
13227 wxImage imgs = img.GetSubImage(r);
13231 case GLOBAL_COLOR_SCHEME_DAY:
13232 case GLOBAL_COLOR_SCHEME_DAY_HICON:
13236 case GLOBAL_COLOR_SCHEME_DUSK:
13237 case GLOBAL_COLOR_SCHEME_NIGHT_HICON:
13240 case GLOBAL_COLOR_SCHEME_NIGHT:
13247 const int w = imgs.GetWidth();
13248 const int h = imgs.GetHeight();
13249 pmap = (
int *)calloc(w * h *
sizeof(
int), 1);
13254 for (
int y = 1; y < h - 1; y++) {
13255 for (
int x = 1; x < w - 1; x++) {
13257 img.GetRed(x + 1, y + 1) - img.GetRed(x - 1, y - 1);
13258 val = (int)(val * val_factor);
13259 index = (y * w) + x;
13272void ChartCanvas::DrawAllTracksInBBox(
ocpnDC &dc, LLBBox &BltBBox) {
13273 Track *active_track = NULL;
13276 active_track = pTrackDraw;
13280 TrackGui(*pTrackDraw).Draw(
this, dc, GetVP(), BltBBox);
13283 if (active_track)
TrackGui(*active_track).Draw(
this, dc, GetVP(), BltBBox);
13286void ChartCanvas::DrawActiveTrackInBBox(
ocpnDC &dc, LLBBox &BltBBox) {
13287 Track *active_track = NULL;
13290 active_track = pTrackDraw;
13294 if (active_track)
TrackGui(*active_track).Draw(
this, dc, GetVP(), BltBBox);
13297void ChartCanvas::DrawAllRoutesInBBox(
ocpnDC &dc, LLBBox &BltBBox) {
13298 Route *active_route = NULL;
13300 if (pRouteDraw->IsActive() || pRouteDraw->IsSelected()) {
13301 active_route = pRouteDraw;
13306 RouteGui(*pRouteDraw).Draw(dc,
this, BltBBox);
13311 if (active_route)
RouteGui(*active_route).Draw(dc,
this, BltBBox);
13314void ChartCanvas::DrawActiveRouteInBBox(
ocpnDC &dc, LLBBox &BltBBox) {
13315 Route *active_route = NULL;
13318 if (pRouteDraw->IsActive() || pRouteDraw->IsSelected()) {
13319 active_route = pRouteDraw;
13323 if (active_route)
RouteGui(*active_route).Draw(dc,
this, BltBBox);
13326void ChartCanvas::DrawAllWaypointsInBBox(
ocpnDC &dc, LLBBox &BltBBox) {
13327 if (!pWayPointMan)
return;
13329 auto node = pWayPointMan->GetWaypointList()->begin();
13331 while (node != pWayPointMan->GetWaypointList()->end()) {
13340 if (BltBBox.Contains(pWP->m_lat, pWP->m_lon))
13344 if (pWP->GetShowWaypointRangeRings() &&
13345 (pWP->GetWaypointRangeRingsNumber() > 0)) {
13346 double factor = 1.00;
13347 if (pWP->GetWaypointRangeRingsStepUnits() ==
13349 factor = 1 / 1.852;
13351 double radius = factor * pWP->GetWaypointRangeRingsNumber() *
13352 pWP->GetWaypointRangeRingsStep() / 60.;
13356 radar_box.Set(pWP->m_lat - radius, pWP->m_lon - radius,
13357 pWP->m_lat + radius, pWP->m_lon + radius);
13358 if (!BltBBox.IntersectOut(radar_box)) {
13369void ChartCanvas::DrawBlinkObjects() {
13371 wxRect update_rect;
13373 if (!pWayPointMan)
return;
13375 for (
RoutePoint *pWP : *pWayPointMan->GetWaypointList()) {
13382 if (!update_rect.IsEmpty()) RefreshRect(update_rect);
13385void ChartCanvas::DrawAnchorWatchPoints(
ocpnDC &dc) {
13390 wxPoint lAnchorPoint1, lAnchorPoint2;
13404 wxPen ppPeng(GetGlobalColor(
"UGREN"), 2);
13405 wxPen ppPenr(GetGlobalColor(
"URED"), 2);
13407 wxBrush *ppBrush = wxTheBrushList->FindOrCreateBrush(
13408 wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT);
13409 dc.SetBrush(*ppBrush);
13413 dc.StrokeCircle(lAnchorPoint1.x, lAnchorPoint1.y, fabs(lpp1));
13418 dc.StrokeCircle(lAnchorPoint2.x, lAnchorPoint2.y, fabs(lpp2));
13423 dc.StrokeCircle(lAnchorPoint1.x, lAnchorPoint1.y, fabs(lpp1));
13428 dc.StrokeCircle(lAnchorPoint2.x, lAnchorPoint2.y, fabs(lpp2));
13433double ChartCanvas::GetAnchorWatchRadiusPixels(
RoutePoint *pAnchorWatchPoint) {
13436 wxPoint lAnchorPoint;
13439 double tlat1, tlon1;
13441 if (pAnchorWatchPoint) {
13442 (pAnchorWatchPoint->GetName()).ToDouble(&d1);
13444 dabs = fabs(d1 / 1852.);
13445 ll_gc_ll(pAnchorWatchPoint->m_lat, pAnchorWatchPoint->m_lon, 0, dabs,
13450 lpp = sqrt(pow((
double)(lAnchorPoint.x - r1.x), 2) +
13451 pow((
double)(lAnchorPoint.y - r1.y), 2));
13454 if (d1 < 0) lpp = -lpp;
13462void ChartCanvas::RebuildTideSelectList(LLBBox &BBox) {
13464 if (
this != wxWindow::FindFocus())
return;
13466 pSelectTC->DeleteAllSelectableTypePoints(SELTYPE_TIDEPOINT);
13468 for (
int i = 1; i <
ptcmgr->Get_max_IDX() + 1; i++) {
13474 if ((type ==
't') || (type ==
'T')) {
13475 if (BBox.Contains(lat, lon)) {
13477 pSelectTC->AddSelectablePoint(lat, lon, pIDX, SELTYPE_TIDEPOINT);
13483void ChartCanvas::DrawAllTidesInBBox(
ocpnDC &dc, LLBBox &BBox) {
13486 wxDateTime this_now = gTimeSource;
13487 bool cur_time = !gTimeSource.IsValid();
13488 if (cur_time) this_now = wxDateTime::Now();
13489 time_t t_this_now = this_now.GetTicks();
13491 wxPen *pblack_pen = wxThePenList->FindOrCreatePen(GetGlobalColor(
"UINFD"), 1,
13493 wxPen *pyelo_pen = wxThePenList->FindOrCreatePen(
13494 GetGlobalColor(cur_time ?
"YELO1" :
"YELO2"), 1, wxPENSTYLE_SOLID);
13495 wxPen *pblue_pen = wxThePenList->FindOrCreatePen(
13496 GetGlobalColor(cur_time ?
"BLUE2" :
"BLUE3"), 1, wxPENSTYLE_SOLID);
13498 wxBrush *pgreen_brush = wxTheBrushList->FindOrCreateBrush(
13499 GetGlobalColor(
"GREEN1"), wxBRUSHSTYLE_SOLID);
13500 wxBrush *pblue_brush = wxTheBrushList->FindOrCreateBrush(
13501 GetGlobalColor(cur_time ?
"BLUE2" :
"BLUE3"), wxBRUSHSTYLE_SOLID);
13502 wxBrush *pyelo_brush = wxTheBrushList->FindOrCreateBrush(
13503 GetGlobalColor(cur_time ?
"YELO1" :
"YELO2"), wxBRUSHSTYLE_SOLID);
13505 wxFont *dFont = FontMgr::Get().
GetFont(_(
"ExtendedTideIcon"));
13506 dc.SetTextForeground(FontMgr::Get().GetFontColor(_(
"ExtendedTideIcon")));
13507 int font_size = wxMax(10, dFont->GetPointSize());
13510 font_size, dFont->GetFamily(), dFont->GetStyle(), dFont->GetWeight(),
13511 false, dFont->GetFaceName());
13513 dc.SetPen(*pblack_pen);
13514 dc.SetBrush(*pgreen_brush);
13518 case GLOBAL_COLOR_SCHEME_DAY:
13519 case GLOBAL_COLOR_SCHEME_DAY_HICON:
13522 case GLOBAL_COLOR_SCHEME_DUSK:
13523 case GLOBAL_COLOR_SCHEME_NIGHT_HICON:
13526 case GLOBAL_COLOR_SCHEME_NIGHT:
13527 bm = m_bmTideNight;
13534 int bmw = bm.GetWidth();
13535 int bmh = bm.GetHeight();
13537 float scale_factor = 1.0;
13541 float icon_pixelRefDim = 45;
13546 sdc.GetTextExtent(
"M", NULL, &height, NULL, NULL, plabelFont);
13548 float pix_factor = (1.5 * height) / icon_pixelRefDim;
13550 scale_factor *= pix_factor;
13557 scale_factor *= user_scale_factor;
13558 scale_factor *= GetContentScaleFactor();
13561 double marge = 0.05;
13562 std::vector<LLBBox> drawn_boxes;
13563 for (
int i = 1; i <
ptcmgr->Get_max_IDX() + 1; i++) {
13567 if ((type ==
't') || (type ==
'T'))
13572 if (BBox.ContainsMarge(lat, lon, marge)) {
13574 if (GetVP().chart_scale < 500000) {
13575 bool bdrawn =
false;
13576 for (
size_t i = 0; i < drawn_boxes.size(); i++) {
13577 if (drawn_boxes[i].Contains(lat, lon)) {
13582 if (bdrawn)
continue;
13585 this_box.Set(lat, lon, lat, lon);
13586 this_box.EnLarge(.005);
13587 drawn_boxes.push_back(this_box);
13593 if (GetVP().chart_scale > 500000) {
13594 dc.DrawBitmap(bm, r.x - bmw / 2, r.y - bmh / 2,
true);
13598 dc.SetFont(*plabelFont);
13610 if (
ptcmgr->GetTideFlowSens(
13611 t_this_now, BACKWARD_TEN_MINUTES_STEP,
13615 ptcmgr->GetHightOrLowTide(
13616 t_this_now + BACKWARD_TEN_MINUTES_STEP,
13617 FORWARD_TEN_MINUTES_STEP, FORWARD_ONE_MINUTES_STEP, val,
13629 if (tctime > t_this_now)
13630 ptcmgr->GetHightOrLowTide(
13631 t_this_now, BACKWARD_TEN_MINUTES_STEP,
13632 BACKWARD_ONE_MINUTES_STEP, nowlev, wt,
13636 ptcmgr->GetHightOrLowTide(
13637 t_this_now, FORWARD_TEN_MINUTES_STEP,
13638 FORWARD_ONE_MINUTES_STEP, nowlev, wt, pIDX->
IDX_rec_num,
13652 int width = (int)(12 * scale_factor + 0.5);
13653 int height = (int)(45 * scale_factor + 0.5);
13654 int linew = wxMax(1, (
int)(scale_factor));
13655 int xDraw = r.x - (width / 2);
13656 int yDraw = r.y - (height / 2);
13659 float ts = 1 - ((nowlev - ltleve) / (htleve - ltleve));
13660 int hs = (httime > lttime) ? -4 : 4;
13661 hs *= (int)(scale_factor + 0.5);
13662 if (ts > 0.995 || ts < 0.005) hs = 0;
13663 int ht_y = (int)(height * ts);
13666 pblack_pen->SetWidth(linew);
13667 dc.SetPen(*pblack_pen);
13668 dc.SetBrush(*pyelo_brush);
13669 dc.DrawRectangle(xDraw, yDraw, width, height);
13673 dc.SetPen(*pblue_pen);
13674 dc.SetBrush(*pblue_brush);
13675 dc.DrawRectangle((xDraw + 2 * linew), yDraw + ht_y,
13676 (width - (4 * linew)), height - ht_y);
13682 arrow[0].x = xDraw + 2 * linew;
13683 arrow[1].x = xDraw + width / 2;
13684 arrow[2].x = xDraw + width - 2 * linew;
13685 pyelo_pen->SetWidth(linew);
13686 pblue_pen->SetWidth(linew);
13687 if (ts > 0.35 || ts < 0.15)
13689 hl = (int)(height * 0.25) + yDraw;
13691 arrow[1].y = hl + hs;
13694 dc.SetPen(*pyelo_pen);
13696 dc.SetPen(*pblue_pen);
13697 dc.DrawLines(3, arrow);
13699 if (ts > 0.60 || ts < 0.40)
13701 hl = (int)(height * 0.5) + yDraw;
13703 arrow[1].y = hl + hs;
13706 dc.SetPen(*pyelo_pen);
13708 dc.SetPen(*pblue_pen);
13709 dc.DrawLines(3, arrow);
13711 if (ts < 0.65 || ts > 0.85)
13713 hl = (int)(height * 0.75) + yDraw;
13715 arrow[1].y = hl + hs;
13718 dc.SetPen(*pyelo_pen);
13720 dc.SetPen(*pblue_pen);
13721 dc.DrawLines(3, arrow);
13725 double nowlev_converted = nowlev;
13729 int unit_c = TCDataFactory::findunit(pmsd->unit);
13733 TCDataFactory::known_units[unit_c].conv_factor;
13736 nowlev_converted =
toUsrHeight(nowlev_converted);
13740 s.Printf(
"%3.1f", nowlev_converted);
13744 dc.GetTextExtent(s, &wx1, NULL);
13746 dc.DrawText(s, r.x - (wx1 / 2), yDraw + height);
13761void ChartCanvas::RebuildCurrentSelectList(LLBBox &BBox) {
13764 pSelectTC->DeleteAllSelectableTypePoints(SELTYPE_CURRENTPOINT);
13766 for (
int i = 1; i <
ptcmgr->Get_max_IDX() + 1; i++) {
13772 if (((type ==
'c') || (type ==
'C')) && (!pIDX->
b_skipTooDeep)) {
13773 if ((BBox.Contains(lat, lon))) {
13775 pSelectTC->AddSelectablePoint(lat, lon, pIDX, SELTYPE_CURRENTPOINT);
13781void ChartCanvas::DrawAllCurrentsInBBox(
ocpnDC &dc, LLBBox &BBox) {
13784 float tcvalue, dir;
13788 double lon_last = 0.;
13789 double lat_last = 0.;
13791 double marge = 0.2;
13792 bool cur_time = !gTimeSource.IsValid();
13794 double true_scale_display = floor(VPoint.
chart_scale / 100.) * 100.;
13795 bDrawCurrentValues = true_scale_display < g_Show_Target_Name_Scale;
13797 wxPen *pblack_pen = wxThePenList->FindOrCreatePen(GetGlobalColor(
"UINFD"), 1,
13799 wxPen *porange_pen = wxThePenList->FindOrCreatePen(
13800 GetGlobalColor(cur_time ?
"UINFO" :
"UINFB"), 1, wxPENSTYLE_SOLID);
13801 wxBrush *porange_brush = wxTheBrushList->FindOrCreateBrush(
13802 GetGlobalColor(cur_time ?
"UINFO" :
"UINFB"), wxBRUSHSTYLE_SOLID);
13803 wxBrush *pgray_brush = wxTheBrushList->FindOrCreateBrush(
13804 GetGlobalColor(
"UIBDR"), wxBRUSHSTYLE_SOLID);
13805 wxBrush *pblack_brush = wxTheBrushList->FindOrCreateBrush(
13806 GetGlobalColor(
"UINFD"), wxBRUSHSTYLE_SOLID);
13808 double skew_angle = GetVPRotation();
13810 wxFont *dFont = FontMgr::Get().
GetFont(_(
"CurrentValue"));
13811 dc.SetTextForeground(FontMgr::Get().GetFontColor(_(
"CurrentValue")));
13812 int font_size = wxMax(10, dFont->GetPointSize());
13815 font_size, dFont->GetFamily(), dFont->GetStyle(), dFont->GetWeight(),
13816 false, dFont->GetFaceName());
13818 float scale_factor = 1.0;
13824 sdc.GetTextExtent(
"M", NULL, &height, NULL, NULL, pTCFont);
13826 float nominal_icon_size_pixels = 15;
13827 float pix_factor = (1 * height) / nominal_icon_size_pixels;
13829 scale_factor *= pix_factor;
13836 scale_factor *= user_scale_factor;
13838 scale_factor *= GetContentScaleFactor();
13841 for (
int i = 1; i <
ptcmgr->Get_max_IDX() + 1; i++) {
13847 if (((type ==
'c') || (type ==
'C')) && (1 )) {
13848 if (!pIDX->
b_skipTooDeep && (BBox.ContainsMarge(lat, lon, marge))) {
13853 int dd = (int)(5.0 * scale_factor + 0.5);
13864 pblack_pen->SetWidth(wxMax(2, (
int)(scale_factor + 0.5)));
13865 dc.SetPen(*pblack_pen);
13866 dc.SetBrush(*porange_brush);
13867 dc.DrawPolygon(4, d);
13870 dc.SetBrush(*pblack_brush);
13871 dc.DrawCircle(r.x, r.y, (
int)(2 * scale_factor));
13875 if (!
ptcmgr->GetTideOrCurrent15(0, i, tcvalue, dir, bnew_val))
13889 double a1 = fabs(tcvalue) * 10.;
13891 a1 = wxMax(1.0, a1);
13892 double a2 = log10(a1);
13894 float cscale = scale_factor * a2 * 0.3;
13896 porange_pen->SetWidth(wxMax(2, (
int)(scale_factor + 0.5)));
13897 dc.SetPen(*porange_pen);
13898 DrawArrow(dc, pixxc, pixyc, dir - 90 + (skew_angle * 180. / PI),
13902 if (bDrawCurrentValues) {
13903 dc.SetFont(*pTCFont);
13904 tcvalue = toUsrSpeed(tcvalue);
13905 snprintf(sbuf, 19,
"%3.1f", fabs(tcvalue));
13906 dc.DrawText(wxString(sbuf, wxConvUTF8), pixxc, pixyc);
13932 if (!pvIDX)
return;
13937 if (pCwin && pCwin->IsShown()) {
13945 pCwin->RequestUserAttention(wxUSER_ATTENTION_INFO);
13960 pCwin =
new TCWin(
this, x, y, pvIDX);
13978#define NUM_CURRENT_ARROW_POINTS 9
13979static wxPoint CurrentArrowArray[NUM_CURRENT_ARROW_POINTS] = {
13980 wxPoint(0, 0), wxPoint(0, -10), wxPoint(55, -10),
13981 wxPoint(55, -25), wxPoint(100, 0), wxPoint(55, 25),
13982 wxPoint(55, 10), wxPoint(0, 10), wxPoint(0, 0)};
13984void ChartCanvas::DrawArrow(
ocpnDC &dc,
int x,
int y,
double rot_angle,
13986 if (
scale > 1e-2) {
13987 float sin_rot = sin(rot_angle * PI / 180.);
13988 float cos_rot = cos(rot_angle * PI / 180.);
13992 float xt = CurrentArrowArray[0].x;
13993 float yt = CurrentArrowArray[0].y;
13995 float xp = (xt * cos_rot) - (yt * sin_rot);
13996 float yp = (xt * sin_rot) + (yt * cos_rot);
13997 int x1 = (int)(xp *
scale);
13998 int y1 = (int)(yp *
scale);
14001 for (
int ip = 1; ip < NUM_CURRENT_ARROW_POINTS; ip++) {
14002 xt = CurrentArrowArray[ip].x;
14003 yt = CurrentArrowArray[ip].y;
14005 float xp = (xt * cos_rot) - (yt * sin_rot);
14006 float yp = (xt * sin_rot) + (yt * cos_rot);
14007 int x2 = (int)(xp *
scale);
14008 int y2 = (int)(yp *
scale);
14010 dc.
DrawLine(x1 + x, y1 + y, x2 + x, y2 + y);
14018wxString ChartCanvas::FindValidUploadPort() {
14021 if (!g_uploadConnection.IsEmpty() &&
14022 g_uploadConnection.StartsWith(
"Serial")) {
14023 port = g_uploadConnection;
14030 if ((cp->direction != PortDirection::kInput) && cp->type == SERIAL)
14031 port <<
"Serial:" << cp->serial_port;
14037void ShowAISTargetQueryDialog(wxWindow *win,
int mmsi) {
14040 if (NULL == g_pais_query_dialog_active) {
14041 int pos_x = g_ais_query_dialog_x;
14042 int pos_y = g_ais_query_dialog_y;
14044 if (g_pais_query_dialog_active) {
14045 g_pais_query_dialog_active->Destroy();
14051 g_pais_query_dialog_active->
Create(win, -1, _(
"AIS Target Query"),
14052 wxPoint(pos_x, pos_y));
14054 g_pais_query_dialog_active->SetAutoCentre(g_btouch);
14055 g_pais_query_dialog_active->SetAutoSize(g_bresponsive);
14056 g_pais_query_dialog_active->SetMMSI(mmsi);
14057 g_pais_query_dialog_active->UpdateText();
14058 wxSize sz = g_pais_query_dialog_active->GetSize();
14060 bool b_reset_pos =
false;
14065 RECT frame_title_rect;
14066 frame_title_rect.left = pos_x;
14067 frame_title_rect.top = pos_y;
14068 frame_title_rect.right = pos_x + sz.x;
14069 frame_title_rect.bottom = pos_y + 30;
14071 if (NULL == MonitorFromRect(&frame_title_rect, MONITOR_DEFAULTTONULL))
14072 b_reset_pos =
true;
14077 wxRect window_title_rect;
14078 window_title_rect.x = pos_x;
14079 window_title_rect.y = pos_y;
14080 window_title_rect.width = sz.x;
14081 window_title_rect.height = 30;
14083 wxRect ClientRect = wxGetClientDisplayRect();
14084 ClientRect.Deflate(
14086 if (!ClientRect.Intersects(window_title_rect)) b_reset_pos =
true;
14090 if (b_reset_pos) g_pais_query_dialog_active->Move(50, 200);
14093 g_pais_query_dialog_active->SetMMSI(mmsi);
14094 g_pais_query_dialog_active->UpdateText();
14097 g_pais_query_dialog_active->Show();
14100void ChartCanvas::ToggleCanvasQuiltMode() {
14101 bool cur_mode = GetQuiltMode();
14103 if (!GetQuiltMode())
14104 SetQuiltMode(
true);
14105 else if (GetQuiltMode()) {
14106 SetQuiltMode(
false);
14107 g_sticky_chart = GetQuiltReferenceChartIndex();
14110 if (cur_mode != GetQuiltMode()) {
14111 SetupCanvasQuiltMode();
14120 if (ps52plib) ps52plib->GenerateStateHash();
14122 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
14123 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
14126void ChartCanvas::DoCanvasStackDelta(
int direction) {
14127 if (!GetQuiltMode()) {
14128 int current_stack_index = GetpCurrentStack()->CurrentStackEntry;
14129 if ((current_stack_index + direction) >= GetpCurrentStack()->nEntry)
return;
14130 if ((current_stack_index + direction) < 0)
return;
14132 if (m_bpersistent_quilt ) {
14134 GetpCurrentStack()->GetDBIndex(current_stack_index + direction);
14136 if (IsChartQuiltableRef(new_dbIndex)) {
14137 ToggleCanvasQuiltMode();
14138 SelectQuiltRefdbChart(new_dbIndex);
14139 m_bpersistent_quilt =
false;
14142 SelectChartFromStack(current_stack_index + direction);
14145 std::vector<int> piano_chart_index_array =
14146 GetQuiltExtendedStackdbIndexArray();
14147 int refdb = GetQuiltRefChartdbIndex();
14150 int current_index = -1;
14151 for (
unsigned int i = 0; i < piano_chart_index_array.size(); i++) {
14152 if (refdb == piano_chart_index_array[i]) {
14157 if (current_index == -1)
return;
14160 int target_family = ctet.GetChartFamily();
14162 int new_index = -1;
14163 int check_index = current_index + direction;
14164 bool found =
false;
14165 int check_dbIndex = -1;
14166 int new_dbIndex = -1;
14170 (
unsigned int)check_index < piano_chart_index_array.size() &&
14171 (check_index >= 0)) {
14172 check_dbIndex = piano_chart_index_array[check_index];
14174 if (target_family == cte.GetChartFamily()) {
14176 new_index = check_index;
14177 new_dbIndex = check_dbIndex;
14181 check_index += direction;
14184 if (!found)
return;
14186 if (!IsChartQuiltableRef(new_dbIndex)) {
14187 ToggleCanvasQuiltMode();
14188 SelectdbChart(new_dbIndex);
14189 m_bpersistent_quilt =
true;
14191 SelectQuiltRefChart(new_index);
14196 top_frame::Get()->UpdateGlobalMenuItems();
14197 SetQuiltChartHiLiteIndex(-1);
14208void ChartCanvas::OnToolLeftClick(wxCommandEvent &event) {
14211 switch (event.GetId()) {
14223 DoCanvasStackDelta(1);
14228 DoCanvasStackDelta(-1);
14238 ShowCurrents(!GetbShowCurrent());
14245 ShowTides(!GetbShowTide());
14252 if (0 == m_routeState) {
14259 androidSetRouteAnnunciator(m_routeState == 1);
14265 SetAISCanvasDisplayStyle(-1);
14277void ChartCanvas::SetShowAIS(
bool show) {
14279 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
14280 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
14283void ChartCanvas::SetAttenAIS(
bool show) {
14284 m_bShowAISScaled = show;
14285 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
14286 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
14289void ChartCanvas::SetAISCanvasDisplayStyle(
int StyleIndx) {
14292 bool bShowAIS_Array[3] = {
true,
true,
false};
14293 bool bShowScaled_Array[3] = {
false,
true,
true};
14294 wxString ToolShortHelp_Array[3] = {_(
"Show all AIS Targets"),
14295 _(
"Attenuate less critical AIS targets"),
14296 _(
"Hide AIS Targets")};
14297 wxString iconName_Array[3] = {
"AIS",
"AIS_Suppressed",
"AIS_Disabled"};
14299 int AIS_Toolbar_Switch = 0;
14300 if (StyleIndx == -1) {
14302 for (
int i = 1; i < ArraySize; i++) {
14303 if ((bShowAIS_Array[i] == m_bShowAIS) &&
14304 (bShowScaled_Array[i] == m_bShowAISScaled))
14305 AIS_Toolbar_Switch = i;
14307 AIS_Toolbar_Switch++;
14308 if ((!g_bAllowShowScaled) && (AIS_Toolbar_Switch == 1))
14309 AIS_Toolbar_Switch++;
14312 AIS_Toolbar_Switch = StyleIndx;
14315 if (AIS_Toolbar_Switch >= ArraySize) AIS_Toolbar_Switch = 0;
14317 int AIS_Toolbar_Switch_Next =
14318 AIS_Toolbar_Switch + 1;
14319 if ((!g_bAllowShowScaled) && (AIS_Toolbar_Switch_Next == 1))
14320 AIS_Toolbar_Switch_Next++;
14321 if (AIS_Toolbar_Switch_Next >= ArraySize)
14322 AIS_Toolbar_Switch_Next = 0;
14325 m_bShowAIS = bShowAIS_Array[AIS_Toolbar_Switch];
14326 m_bShowAISScaled = bShowScaled_Array[AIS_Toolbar_Switch];
14329void ChartCanvas::TouchAISToolActive() {}
14331void ChartCanvas::UpdateAISTBTool() {}
14339void ChartCanvas::UpdateGPSCompassStatusBox(
bool b_force_new) {
14341 bool b_update =
false;
14342 int cc1_edge_comp = 2;
14343 wxRect rect = m_Compass->
GetRect();
14344 wxSize parent_size = GetSize();
14346 parent_size *= m_displayScale;
14350 wxPoint compass_pt(parent_size.x - rect.width - cc1_edge_comp,
14351 g_StyleManager->GetCurrentStyle()->GetCompassYOffset());
14352 wxRect compass_rect(compass_pt, rect.GetSize());
14354 m_Compass->Move(compass_pt);
14356 if (m_Compass && m_Compass->IsShown())
14357 m_Compass->UpdateStatus(b_force_new | b_update);
14359 double scaler = g_Platform->GetCompassScaleFactor(g_GUIScaleFactor);
14360 scaler = wxMax(scaler, 1.0);
14361 wxPoint note_point = wxPoint(
14362 parent_size.x - (scaler * 20 * wxWindow::GetCharWidth()), compass_rect.y);
14363 if (m_notification_button) {
14364 m_notification_button->Move(note_point);
14365 m_notification_button->UpdateStatus();
14368 if (b_force_new | b_update) Refresh();
14371void ChartCanvas::SelectChartFromStack(
int index,
bool bDir,
14372 ChartTypeEnum New_Type,
14373 ChartFamilyEnum New_Family) {
14374 if (!GetpCurrentStack())
return;
14377 if (index < GetpCurrentStack()->nEntry) {
14380 pTentative_Chart =
ChartData->OpenStackChartConditional(
14381 GetpCurrentStack(), index, bDir, New_Type, New_Family);
14383 if (pTentative_Chart) {
14384 if (m_singleChart) m_singleChart->Deactivate();
14386 m_singleChart = pTentative_Chart;
14387 m_singleChart->Activate();
14389 GetpCurrentStack()->CurrentStackEntry =
ChartData->GetStackEntry(
14390 GetpCurrentStack(), m_singleChart->GetFullPath());
14403 double best_scale_ppm = GetBestVPScale(m_singleChart);
14404 double rotation = GetVPRotation();
14405 double oldskew = GetVPSkew();
14406 double newskew = m_singleChart->GetChartSkew() * PI / 180.0;
14408 if (!g_bskew_comp && (GetUpMode() == NORTH_UP_MODE)) {
14409 if (fabs(oldskew) > 0.0001) rotation = 0.0;
14410 if (fabs(newskew) > 0.0001) rotation = newskew;
14413 SetViewPoint(zLat, zLon, best_scale_ppm, newskew, rotation);
14415 UpdateGPSCompassStatusBox(
true);
14419 int idx = GetpCurrentStack()->GetCurrentEntrydbIndex();
14420 if (idx < 0)
return;
14422 std::vector<int> piano_active_chart_index_array;
14423 piano_active_chart_index_array.push_back(
14424 GetpCurrentStack()->GetCurrentEntrydbIndex());
14425 m_Piano->SetActiveKeyArray(piano_active_chart_index_array);
14428void ChartCanvas::SelectdbChart(
int dbindex) {
14429 if (!GetpCurrentStack())
return;
14432 if (dbindex >= 0) {
14435 pTentative_Chart =
ChartData->OpenChartFromDB(dbindex, FULL_INIT);
14437 if (pTentative_Chart) {
14438 if (m_singleChart) m_singleChart->Deactivate();
14440 m_singleChart = pTentative_Chart;
14441 m_singleChart->Activate();
14443 GetpCurrentStack()->CurrentStackEntry =
ChartData->GetStackEntry(
14444 GetpCurrentStack(), m_singleChart->GetFullPath());
14457 double best_scale_ppm = GetBestVPScale(m_singleChart);
14461 m_singleChart->GetChartSkew() * PI / 180., GetVPRotation());
14471void ChartCanvas::selectCanvasChartDisplay(
int type,
int family) {
14474 if (!GetQuiltMode()) {
14475 if (GetpCurrentStack()) {
14476 int stack_index = -1;
14477 for (
int i = 0; i < GetpCurrentStack()->nEntry; i++) {
14478 int check_dbIndex = GetpCurrentStack()->GetDBIndex(i);
14479 if (check_dbIndex < 0)
continue;
14481 ChartData->GetChartTableEntry(check_dbIndex);
14482 if (type == cte.GetChartType()) {
14485 }
else if (family == cte.GetChartFamily()) {
14491 if (stack_index >= 0) {
14492 SelectChartFromStack(stack_index);
14496 int sel_dbIndex = -1;
14497 std::vector<int> piano_chart_index_array =
14498 GetQuiltExtendedStackdbIndexArray();
14499 for (
unsigned int i = 0; i < piano_chart_index_array.size(); i++) {
14500 int check_dbIndex = piano_chart_index_array[i];
14502 if (type == cte.GetChartType()) {
14503 if (IsChartQuiltableRef(check_dbIndex)) {
14504 sel_dbIndex = check_dbIndex;
14507 }
else if (family == cte.GetChartFamily()) {
14508 if (IsChartQuiltableRef(check_dbIndex)) {
14509 sel_dbIndex = check_dbIndex;
14515 if (sel_dbIndex >= 0) {
14516 SelectQuiltRefdbChart(sel_dbIndex,
false);
14518 AdjustQuiltRefChart();
14525 SetQuiltChartHiLiteIndex(-1);
14530bool ChartCanvas::IsTileOverlayIndexInYesShow(
int index) {
14531 return std::find(m_tile_yesshow_index_array.begin(),
14532 m_tile_yesshow_index_array.end(),
14533 index) != m_tile_yesshow_index_array.end();
14536bool ChartCanvas::IsTileOverlayIndexInNoShow(
int index) {
14537 return std::find(m_tile_noshow_index_array.begin(),
14538 m_tile_noshow_index_array.end(),
14539 index) != m_tile_noshow_index_array.end();
14542void ChartCanvas::AddTileOverlayIndexToNoShow(
int index) {
14543 if (std::find(m_tile_noshow_index_array.begin(),
14544 m_tile_noshow_index_array.end(),
14545 index) == m_tile_noshow_index_array.end()) {
14546 m_tile_noshow_index_array.push_back(index);
14556void ChartCanvas::HandlePianoClick(
14557 int selected_index,
const std::vector<int> &selected_dbIndex_array) {
14560 if (!m_pCurrentStack)
return;
14576 double distance = 25000;
14577 int closest_index = -1;
14578 for (
int chart_index : selected_dbIndex_array) {
14580 double chart_lat = (cte.GetLatMax() + cte.GetLatMin()) / 2;
14581 double chart_lon = (cte.GetLonMax() + cte.GetLonMin()) / 2;
14584 double test_distance = abs(m_vLat - chart_lat) + abs(m_vLon - chart_lon);
14585 if (test_distance < distance) {
14586 distance = test_distance;
14587 closest_index = chart_index;
14591 int selected_dbIndex = selected_dbIndex_array[0];
14592 if (closest_index >= 0) selected_dbIndex = closest_index;
14594 if (!GetQuiltMode()) {
14595 if (m_bpersistent_quilt ) {
14596 if (IsChartQuiltableRef(selected_dbIndex)) {
14597 ToggleCanvasQuiltMode();
14598 SelectQuiltRefdbChart(selected_dbIndex);
14599 m_bpersistent_quilt =
false;
14601 SelectChartFromStack(selected_index);
14604 SelectChartFromStack(selected_index);
14605 g_sticky_chart = selected_dbIndex;
14609 GetVP().SetProjectionType(m_singleChart->GetChartProjectionType());
14613 if (CHART_TYPE_MBTILES ==
ChartData->GetDBChartType(selected_dbIndex)) {
14614 bool bfound =
false;
14615 for (
unsigned int i = 0; i < m_tile_noshow_index_array.size(); i++) {
14616 if (m_tile_noshow_index_array[i] ==
14617 selected_dbIndex) {
14618 m_tile_noshow_index_array.erase(m_tile_noshow_index_array.begin() +
14625 m_tile_noshow_index_array.push_back(selected_dbIndex);
14629 if (!IsTileOverlayIndexInYesShow(selected_dbIndex))
14630 m_tile_yesshow_index_array.push_back(selected_dbIndex);
14634 if (IsChartQuiltableRef(selected_dbIndex)) {
14640 bool set_scale =
false;
14641 if (CHART_TYPE_S57 ==
ChartData->GetDBChartType(selected_dbIndex)) {
14642 if (
ChartData->GetDBChartScale(selected_dbIndex) < 5000) {
14648 SelectQuiltRefdbChart(selected_dbIndex,
true);
14650 SelectQuiltRefdbChart(selected_dbIndex,
false);
14655 ChartData->OpenChartFromDB(selected_dbIndex, FULL_INIT);
14657 double proposed_scale_onscreen =
14660 if (g_bPreserveScaleOnX) {
14661 proposed_scale_onscreen =
14662 wxMin(proposed_scale_onscreen,
14664 GetCanvasWidth()));
14666 proposed_scale_onscreen =
14667 wxMin(proposed_scale_onscreen,
14669 GetCanvasWidth()));
14671 proposed_scale_onscreen =
14672 wxMax(proposed_scale_onscreen,
14681 ToggleCanvasQuiltMode();
14682 SelectdbChart(selected_dbIndex);
14683 m_bpersistent_quilt =
true;
14688 SetQuiltChartHiLiteIndex(-1);
14690 top_frame::Get()->UpdateGlobalMenuItems();
14691 HideChartInfoWindow();
14696void ChartCanvas::HandlePianoRClick(
14697 int x,
int y,
int selected_index,
14698 const std::vector<int> &selected_dbIndex_array) {
14701 if (!GetpCurrentStack())
return;
14703 PianoPopupMenu(x, y, selected_index, selected_dbIndex_array);
14704 UpdateCanvasControlBar();
14706 SetQuiltChartHiLiteIndex(-1);
14709void ChartCanvas::HandlePianoRollover(
14710 int selected_index,
const std::vector<int> &selected_dbIndex_array,
14711 int n_charts,
int scale) {
14714 if (!GetpCurrentStack())
return;
14719 wxPoint key_location = m_Piano->GetKeyOrigin(selected_index);
14721 if (!GetQuiltMode()) {
14722 ShowChartInfoWindow(key_location.x, selected_dbIndex_array[0]);
14725 std::vector<int> piano_chart_index_array;
14726 if (m_Piano->GetPianoMode() == PIANO_MODE_LEGACY) {
14727 piano_chart_index_array = GetQuiltExtendedStackdbIndexArray();
14728 if ((GetpCurrentStack()->nEntry > 1) ||
14729 (piano_chart_index_array.size() >= 1)) {
14730 ShowChartInfoWindow(key_location.x, selected_dbIndex_array[0]);
14732 SetQuiltChartHiLiteIndexArray(selected_dbIndex_array);
14734 }
else if (GetpCurrentStack()->nEntry == 1) {
14736 ChartData->GetChartTableEntry(GetpCurrentStack()->GetDBIndex(0));
14737 if (CHART_TYPE_CM93COMP != cte.GetChartType()) {
14738 ShowChartInfoWindow(key_location.x, selected_dbIndex_array[0]);
14740 }
else if ((-1 == selected_index) &&
14741 (0 == selected_dbIndex_array.size())) {
14742 ShowChartInfoWindow(key_location.x, -1);
14746 piano_chart_index_array = GetQuiltFullScreendbIndexArray();
14748 if ((GetpCurrentStack()->nEntry > 1) ||
14749 (piano_chart_index_array.size() >= 1)) {
14751 ShowCompositeInfoWindow(key_location.x, n_charts,
scale,
14752 selected_dbIndex_array);
14753 else if (n_charts == 1)
14754 ShowChartInfoWindow(key_location.x, selected_dbIndex_array[0]);
14756 SetQuiltChartHiLiteIndexArray(selected_dbIndex_array);
14763void ChartCanvas::ClearPianoRollover() {
14764 ClearQuiltChartHiLiteIndexArray();
14765 ShowChartInfoWindow(0, -1);
14766 std::vector<int> vec;
14767 ShowCompositeInfoWindow(0, 0, 0, vec);
14771void ChartCanvas::UpdateCanvasControlBar() {
14772 if (m_pianoFrozen)
return;
14774 if (!GetpCurrentStack())
return;
14776 if (!g_bShowChartBar)
return;
14779 int sel_family = -1;
14781 std::vector<int> piano_chart_index_array;
14782 std::vector<int> empty_piano_chart_index_array;
14784 wxString old_hash = m_Piano->GetStoredHash();
14786 if (GetQuiltMode()) {
14787 m_Piano->SetKeyArray(GetQuiltExtendedStackdbIndexArray(),
14788 GetQuiltFullScreendbIndexArray());
14790 std::vector<int> piano_active_chart_index_array =
14791 GetQuiltCandidatedbIndexArray();
14792 m_Piano->SetActiveKeyArray(piano_active_chart_index_array);
14794 std::vector<int> piano_eclipsed_chart_index_array =
14795 GetQuiltEclipsedStackdbIndexArray();
14796 m_Piano->SetEclipsedIndexArray(piano_eclipsed_chart_index_array);
14798 m_Piano->SetNoshowIndexArray(m_quilt_noshow_index_array);
14799 m_Piano->AddNoshowIndexArray(m_tile_noshow_index_array);
14801 sel_type =
ChartData->GetDBChartType(GetQuiltReferenceChartIndex());
14802 sel_family =
ChartData->GetDBChartFamily(GetQuiltReferenceChartIndex());
14804 piano_chart_index_array =
ChartData->GetCSArray(GetpCurrentStack());
14805 m_Piano->SetKeyArray(piano_chart_index_array, piano_chart_index_array);
14808 if (m_singleChart) {
14809 sel_type = m_singleChart->GetChartType();
14810 sel_family = m_singleChart->GetChartFamily();
14815 std::vector<int> piano_skew_chart_index_array;
14816 std::vector<int> piano_tmerc_chart_index_array;
14817 std::vector<int> piano_poly_chart_index_array;
14819 for (
unsigned int ino = 0; ino < piano_chart_index_array.size(); ino++) {
14821 ChartData->GetChartTableEntry(piano_chart_index_array[ino]);
14822 double skew_norm = ctei.GetChartSkew();
14823 if (skew_norm > 180.) skew_norm -= 360.;
14825 if (ctei.GetChartProjectionType() == PROJECTION_TRANSVERSE_MERCATOR)
14826 piano_tmerc_chart_index_array.push_back(piano_chart_index_array[ino]);
14829 else if (ctei.GetChartProjectionType() == PROJECTION_POLYCONIC) {
14830 if (fabs(skew_norm) > 1.)
14831 piano_skew_chart_index_array.push_back(piano_chart_index_array[ino]);
14833 piano_poly_chart_index_array.push_back(piano_chart_index_array[ino]);
14834 }
else if (fabs(skew_norm) > 1.)
14835 piano_skew_chart_index_array.push_back(piano_chart_index_array[ino]);
14837 m_Piano->SetSkewIndexArray(piano_skew_chart_index_array);
14838 m_Piano->SetTmercIndexArray(piano_tmerc_chart_index_array);
14839 m_Piano->SetPolyIndexArray(piano_poly_chart_index_array);
14841 wxString new_hash = m_Piano->GenerateAndStoreNewHash();
14842 if (new_hash != old_hash) {
14843 m_Piano->FormatKeys();
14844 HideChartInfoWindow();
14845 m_Piano->ResetRollover();
14846 SetQuiltChartHiLiteIndex(-1);
14847 m_brepaint_piano =
true;
14853 for (
unsigned int ino = 0; ino < piano_chart_index_array.size(); ino++) {
14855 ChartData->GetChartTableEntry(piano_chart_index_array[ino]);
14856 ChartFamilyEnum e = (ChartFamilyEnum)ctei.GetChartFamily();
14857 ChartTypeEnum t = (ChartTypeEnum)ctei.GetChartType();
14858 if (e == CHART_FAMILY_RASTER) mask |= 1;
14859 if (e == CHART_FAMILY_VECTOR) {
14860 if (t == CHART_TYPE_CM93COMP)
14867 wxString s_indicated;
14868 if (sel_type == CHART_TYPE_CM93COMP)
14869 s_indicated =
"cm93";
14871 if (sel_family == CHART_FAMILY_RASTER)
14872 s_indicated =
"raster";
14873 else if (sel_family == CHART_FAMILY_VECTOR)
14874 s_indicated =
"vector";
14877 g_Platform->setChartTypeMaskSel(mask, s_indicated);
14880void ChartCanvas::FormatPianoKeys() { m_Piano->FormatKeys(); }
14882void ChartCanvas::PianoPopupMenu(
14883 int x,
int y,
int selected_index,
14884 const std::vector<int> &selected_dbIndex_array) {
14885 if (!GetpCurrentStack())
return;
14888 if (!GetQuiltMode())
return;
14890 m_piano_ctx_menu =
new wxMenu();
14892 if (m_Piano->GetPianoMode() == PIANO_MODE_COMPOSITE) {
14902 menu_selected_dbIndex = selected_dbIndex_array[0];
14903 menu_selected_index = selected_index;
14906 bool b_is_in_noshow =
false;
14907 for (
unsigned int i = 0; i < m_quilt_noshow_index_array.size(); i++) {
14908 if (m_quilt_noshow_index_array[i] ==
14909 menu_selected_dbIndex)
14911 b_is_in_noshow =
true;
14916 if (b_is_in_noshow) {
14917 m_piano_ctx_menu->Append(ID_PIANO_ENABLE_QUILT_CHART,
14918 _(
"Show This Chart"));
14919 Connect(ID_PIANO_ENABLE_QUILT_CHART, wxEVT_COMMAND_MENU_SELECTED,
14920 wxCommandEventHandler(ChartCanvas::OnPianoMenuEnableChart));
14921 }
else if (GetpCurrentStack()->nEntry > 1) {
14922 m_piano_ctx_menu->Append(ID_PIANO_DISABLE_QUILT_CHART,
14923 _(
"Hide This Chart"));
14924 Connect(ID_PIANO_DISABLE_QUILT_CHART, wxEVT_COMMAND_MENU_SELECTED,
14925 wxCommandEventHandler(ChartCanvas::OnPianoMenuDisableChart));
14929 wxPoint pos = wxPoint(x, y - 30);
14932 if (m_piano_ctx_menu->GetMenuItems().GetCount())
14933 PopupMenu(m_piano_ctx_menu, pos);
14935 delete m_piano_ctx_menu;
14936 m_piano_ctx_menu = NULL;
14938 HideChartInfoWindow();
14939 m_Piano->ResetRollover();
14941 SetQuiltChartHiLiteIndex(-1);
14942 ClearQuiltChartHiLiteIndexArray();
14947void ChartCanvas::OnPianoMenuEnableChart(wxCommandEvent &event) {
14948 for (
unsigned int i = 0; i < m_quilt_noshow_index_array.size(); i++) {
14949 if (m_quilt_noshow_index_array[i] ==
14950 menu_selected_dbIndex)
14952 m_quilt_noshow_index_array.erase(m_quilt_noshow_index_array.begin() + i);
14958void ChartCanvas::OnPianoMenuDisableChart(wxCommandEvent &event) {
14959 if (!GetpCurrentStack())
return;
14962 RemoveChartFromQuilt(menu_selected_dbIndex);
14966 if (menu_selected_dbIndex == GetQuiltRefChartdbIndex()) {
14967 int type =
ChartData->GetDBChartType(menu_selected_dbIndex);
14969 int i = menu_selected_index + 1;
14970 bool b_success =
false;
14971 while (i < GetpCurrentStack()->nEntry - 1) {
14972 int dbIndex = GetpCurrentStack()->GetDBIndex(i);
14973 if (type ==
ChartData->GetDBChartType(dbIndex)) {
14974 SelectQuiltRefChart(i);
14984 i = menu_selected_index - 1;
14986 int dbIndex = GetpCurrentStack()->GetDBIndex(i);
14987 if (type ==
ChartData->GetDBChartType(dbIndex)) {
14988 SelectQuiltRefChart(i);
14998void ChartCanvas::RemoveChartFromQuilt(
int dbIndex) {
15000 for (
unsigned int i = 0; i < m_quilt_noshow_index_array.size(); i++) {
15001 if (m_quilt_noshow_index_array[i] ==
15004 m_quilt_noshow_index_array.erase(m_quilt_noshow_index_array.begin() + i);
15009 m_quilt_noshow_index_array.push_back(dbIndex);
15012bool ChartCanvas::UpdateS52State() {
15013 bool retval =
false;
15016 ps52plib->SetShowS57Text(m_encShowText);
15017 ps52plib->SetDisplayCategory((DisCat)m_encDisplayCategory);
15018 ps52plib->m_bShowSoundg = m_encShowDepth;
15019 ps52plib->m_bShowAtonText = m_encShowBuoyLabels;
15020 ps52plib->m_bShowLdisText = m_encShowLightDesc;
15023 if (!m_encShowLights)
15024 ps52plib->AddObjNoshow(
"LIGHTS");
15026 ps52plib->RemoveObjNoshow(
"LIGHTS");
15027 ps52plib->SetLightsOff(!m_encShowLights);
15028 ps52plib->m_bExtendLightSectors =
true;
15031 ps52plib->SetAnchorOn(m_encShowAnchor);
15032 ps52plib->SetQualityOfData(m_encShowDataQual);
15038void ChartCanvas::SetShowENCDataQual(
bool show) {
15039 m_encShowDataQual = show;
15040 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
15041 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
15043 m_s52StateHash = 0;
15046void ChartCanvas::SetShowENCText(
bool show) {
15047 m_encShowText = show;
15048 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
15049 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
15051 m_s52StateHash = 0;
15054void ChartCanvas::SetENCDisplayCategory(
int category) {
15055 m_encDisplayCategory = category;
15056 m_s52StateHash = 0;
15059void ChartCanvas::SetShowENCDepth(
bool show) {
15060 m_encShowDepth = show;
15061 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
15062 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
15064 m_s52StateHash = 0;
15067void ChartCanvas::SetShowENCLightDesc(
bool show) {
15068 m_encShowLightDesc = show;
15069 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
15070 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
15072 m_s52StateHash = 0;
15075void ChartCanvas::SetShowENCBuoyLabels(
bool show) {
15076 m_encShowBuoyLabels = show;
15077 m_s52StateHash = 0;
15080void ChartCanvas::SetShowENCLights(
bool show) {
15081 m_encShowLights = show;
15082 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
15083 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
15085 m_s52StateHash = 0;
15088void ChartCanvas::SetShowENCAnchor(
bool show) {
15089 m_encShowAnchor = show;
15090 if (GetMUIBar() && GetMUIBar()->GetCanvasOptions())
15091 GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
15093 m_s52StateHash = 0;
15096wxRect ChartCanvas::GetMUIBarRect() {
15099 rv = m_muiBar->GetRect();
15105void ChartCanvas::RenderAlertMessage(wxDC &dc,
const ViewPort &vp) {
15106 if (!GetAlertString().IsEmpty()) {
15107 wxFont *pfont = wxTheFontList->FindOrCreateFont(
15108 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
15110 dc.SetFont(*pfont);
15111 dc.SetPen(*wxTRANSPARENT_PEN);
15113 dc.SetBrush(wxColour(243, 229, 47));
15115 dc.GetMultiLineTextExtent(GetAlertString(), &w, &h);
15119 wxRect sbr = GetScaleBarRect();
15120 int xp = sbr.x + sbr.width + 10;
15121 int yp = (sbr.y + sbr.height) - h;
15123 int wdraw = w + 10;
15124 dc.DrawRectangle(xp, yp, wdraw, h);
15125 dc.DrawLabel(GetAlertString(), wxRect(xp, yp, wdraw, h),
15126 wxALIGN_CENTRE_HORIZONTAL | wxALIGN_CENTRE_VERTICAL);
15136#define BRIGHT_XCALIB
15137#define __OPCPN_USEICC__
15140#ifdef __OPCPN_USEICC__
15141int CreateSimpleICCProfileFile(
const char *file_name,
double co_red,
15142 double co_green,
double co_blue);
15144wxString temp_file_name;
15148class ocpnCurtain:
public wxDialog
15150 DECLARE_CLASS( ocpnCurtain )
15151 DECLARE_EVENT_TABLE()
15154 ocpnCurtain( wxWindow *parent, wxPoint position, wxSize size,
long wstyle );
15156 bool ProcessEvent(wxEvent& event);
15160IMPLEMENT_CLASS ( ocpnCurtain, wxDialog )
15162BEGIN_EVENT_TABLE(ocpnCurtain, wxDialog)
15165ocpnCurtain::ocpnCurtain( wxWindow *parent, wxPoint position, wxSize size,
long wstyle )
15167 wxDialog::Create( parent, -1,
"ocpnCurtain", position, size, wxNO_BORDER | wxSTAY_ON_TOP );
15170ocpnCurtain::~ocpnCurtain()
15174bool ocpnCurtain::ProcessEvent(wxEvent& event)
15176 GetParent()->GetEventHandler()->SetEvtHandlerEnabled(
true);
15177 return GetParent()->GetEventHandler()->ProcessEvent(event);
15182#include <windows.h>
15185typedef BOOL(WINAPI *SetDeviceGammaRamp_ptr_type)(HDC hDC, LPVOID lpRampTable);
15186typedef BOOL(WINAPI *GetDeviceGammaRamp_ptr_type)(HDC hDC, LPVOID lpRampTable);
15187SetDeviceGammaRamp_ptr_type
15188 g_pSetDeviceGammaRamp;
15189GetDeviceGammaRamp_ptr_type g_pGetDeviceGammaRamp;
15191WORD *g_pSavedGammaMap;
15195int InitScreenBrightness() {
15198 if (top_frame::Get()->GetWxGlCanvas() && g_bopengl) {
15202 if (NULL == hGDI32DLL) {
15203 hGDI32DLL = LoadLibrary(TEXT(
"gdi32.dll"));
15205 if (NULL != hGDI32DLL) {
15207 g_pSetDeviceGammaRamp = (SetDeviceGammaRamp_ptr_type)GetProcAddress(
15208 hGDI32DLL,
"SetDeviceGammaRamp");
15209 g_pGetDeviceGammaRamp = (GetDeviceGammaRamp_ptr_type)GetProcAddress(
15210 hGDI32DLL,
"GetDeviceGammaRamp");
15213 if ((NULL == g_pSetDeviceGammaRamp) ||
15214 (NULL == g_pGetDeviceGammaRamp)) {
15215 FreeLibrary(hGDI32DLL);
15224 if (!g_pSavedGammaMap) {
15225 g_pSavedGammaMap = (WORD *)malloc(3 * 256 *
sizeof(WORD));
15228 bbr = g_pGetDeviceGammaRamp(
15229 hDC, g_pSavedGammaMap);
15230 ReleaseDC(NULL, hDC);
15235 wxRegKey *pRegKey =
new wxRegKey(
15236 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows "
15237 "NT\\CurrentVersion\\ICM");
15238 if (!pRegKey->Exists()) pRegKey->Create();
15239 pRegKey->SetValue(
"GdiIcmGammaRange", 256);
15241 g_brightness_init =
true;
15247 if (NULL == g_pcurtain) {
15248 if (top_frame::Get()->CanSetTransparent()) {
15250 g_pcurtain =
new wxDialog(top_frame::Get()->GetPrimaryCanvasWindow(),
15251 -1,
"", wxPoint(0, 0), ::wxGetDisplaySize(),
15252 wxNO_BORDER | wxTRANSPARENT_WINDOW |
15253 wxSTAY_ON_TOP | wxDIALOG_NO_PARENT);
15260 g_pcurtain->Hide();
15262 HWND hWnd = GetHwndOf(g_pcurtain);
15263 SetWindowLong(hWnd, GWL_EXSTYLE,
15264 GetWindowLong(hWnd, GWL_EXSTYLE) | ~WS_EX_APPWINDOW);
15265 g_pcurtain->SetBackgroundColour(wxColour(0, 0, 0));
15266 g_pcurtain->SetTransparent(0);
15268 g_pcurtain->Maximize();
15269 g_pcurtain->Show();
15272 g_pcurtain->Enable();
15273 g_pcurtain->Disable();
15275 top_frame::Get()->Disable();
15276 top_frame::Get()->Enable();
15280 g_brightness_init =
true;
15286 wxString cmd(
"xcalib -version");
15288 wxArrayString output;
15289 long r = wxExecute(cmd, output);
15292 " External application \"xcalib\" not found. Screen brightness "
15295 g_brightness_init =
true;
15300int RestoreScreenBrightness() {
15303 if (g_pSavedGammaMap) {
15304 HDC hDC = GetDC(NULL);
15305 g_pSetDeviceGammaRamp(hDC,
15307 ReleaseDC(NULL, hDC);
15309 free(g_pSavedGammaMap);
15310 g_pSavedGammaMap = NULL;
15314 g_pcurtain->Close();
15315 g_pcurtain->Destroy();
15319 g_brightness_init =
false;
15324#ifdef BRIGHT_XCALIB
15325 if (g_brightness_init) {
15327 cmd =
"xcalib -clear";
15328 wxExecute(cmd, wxEXEC_ASYNC);
15329 g_brightness_init =
false;
15339int SetScreenBrightness(
int brightness) {
15346 if (top_frame::Get()->GetWxGlCanvas() && g_bopengl) {
15348 g_pcurtain->Close();
15349 g_pcurtain->Destroy();
15353 InitScreenBrightness();
15355 if (NULL == hGDI32DLL) {
15357 wchar_t wdll_name[80];
15358 MultiByteToWideChar(0, 0,
"gdi32.dll", -1, wdll_name, 80);
15359 LPCWSTR cstr = wdll_name;
15361 hGDI32DLL = LoadLibrary(cstr);
15363 if (NULL != hGDI32DLL) {
15365 g_pSetDeviceGammaRamp = (SetDeviceGammaRamp_ptr_type)GetProcAddress(
15366 hGDI32DLL,
"SetDeviceGammaRamp");
15367 g_pGetDeviceGammaRamp = (GetDeviceGammaRamp_ptr_type)GetProcAddress(
15368 hGDI32DLL,
"GetDeviceGammaRamp");
15371 if ((NULL == g_pSetDeviceGammaRamp) ||
15372 (NULL == g_pGetDeviceGammaRamp)) {
15373 FreeLibrary(hGDI32DLL);
15380 HDC hDC = GetDC(NULL);
15391 int increment = brightness * 256 / 100;
15394 WORD GammaTable[3][256];
15397 for (
int i = 0; i < 256; i++) {
15398 GammaTable[0][i] = r_gamma_mult * (WORD)table_val;
15399 GammaTable[1][i] = g_gamma_mult * (WORD)table_val;
15400 GammaTable[2][i] = b_gamma_mult * (WORD)table_val;
15402 table_val += increment;
15404 if (table_val > 65535) table_val = 65535;
15407 g_pSetDeviceGammaRamp(hDC, GammaTable);
15408 ReleaseDC(NULL, hDC);
15415 if (g_pSavedGammaMap) {
15416 HDC hDC = GetDC(NULL);
15417 g_pSetDeviceGammaRamp(hDC,
15419 ReleaseDC(NULL, hDC);
15422 if (brightness < 100) {
15423 if (NULL == g_pcurtain) InitScreenBrightness();
15426 int sbrite = wxMax(1, brightness);
15427 sbrite = wxMin(100, sbrite);
15429 g_pcurtain->SetTransparent((100 - sbrite) * 256 / 100);
15433 g_pcurtain->Close();
15434 g_pcurtain->Destroy();
15444#ifdef BRIGHT_XCALIB
15446 if (!g_brightness_init) {
15447 last_brightness = 100;
15448 g_brightness_init =
true;
15449 temp_file_name = wxFileName::CreateTempFileName(
"");
15450 InitScreenBrightness();
15453#ifdef __OPCPN_USEICC__
15456 if (!CreateSimpleICCProfileFile(
15457 (
const char *)temp_file_name.fn_str(), brightness * r_gamma_mult,
15458 brightness * g_gamma_mult, brightness * b_gamma_mult)) {
15459 wxString cmd(
"xcalib ");
15460 cmd += temp_file_name;
15462 wxExecute(cmd, wxEXEC_ASYNC);
15471 if (brightness > last_brightness) {
15473 cmd =
"xcalib -clear";
15474 wxExecute(cmd, wxEXEC_ASYNC);
15476 ::wxMilliSleep(10);
15478 int brite_adj = wxMax(1, brightness);
15479 cmd.Printf(
"xcalib -co %2d -a", brite_adj);
15480 wxExecute(cmd, wxEXEC_ASYNC);
15482 int brite_adj = wxMax(1, brightness);
15483 int factor = (brite_adj * 100) / last_brightness;
15484 factor = wxMax(1, factor);
15486 cmd.Printf(
"xcalib -co %2d -a", factor);
15487 wxExecute(cmd, wxEXEC_ASYNC);
15492 last_brightness = brightness;
15499#ifdef __OPCPN_USEICC__
15501#define MLUT_TAG 0x6d4c5554L
15502#define VCGT_TAG 0x76636774L
15504int GetIntEndian(
unsigned char *s) {
15509 p = (
unsigned char *)&ret;
15512 for (i =
sizeof(
int) - 1; i > -1; --i) *p++ = s[i];
15514 for (i = 0; i < (int)
sizeof(
int); ++i) *p++ = s[i];
15519unsigned short GetShortEndian(
unsigned char *s) {
15520 unsigned short ret;
15524 p = (
unsigned char *)&ret;
15527 for (i =
sizeof(
unsigned short) - 1; i > -1; --i) *p++ = s[i];
15529 for (i = 0; i < (int)
sizeof(
unsigned short); ++i) *p++ = s[i];
15535int CreateSimpleICCProfileFile(
const char *file_name,
double co_red,
15536 double co_green,
double co_blue) {
15540 fp = fopen(file_name,
"wb");
15541 if (!fp)
return -1;
15547 for (
int i = 0; i < 128; i++) header[i] = 0;
15549 fwrite(header, 128, 1, fp);
15553 int numTags = GetIntEndian((
unsigned char *)&numTags0);
15554 fwrite(&numTags, 1, 4, fp);
15556 int tagName0 = VCGT_TAG;
15557 int tagName = GetIntEndian((
unsigned char *)&tagName0);
15558 fwrite(&tagName, 1, 4, fp);
15560 int tagOffset0 = 128 + 4 *
sizeof(int);
15561 int tagOffset = GetIntEndian((
unsigned char *)&tagOffset0);
15562 fwrite(&tagOffset, 1, 4, fp);
15565 int tagSize = GetIntEndian((
unsigned char *)&tagSize0);
15566 fwrite(&tagSize, 1, 4, fp);
15568 fwrite(&tagName, 1, 4, fp);
15570 fwrite(&tagName, 1, 4, fp);
15575 int gammatype0 = 0;
15576 int gammatype = GetIntEndian((
unsigned char *)&gammatype0);
15577 fwrite(&gammatype, 1, 4, fp);
15579 int numChannels0 = 3;
15580 unsigned short numChannels = GetShortEndian((
unsigned char *)&numChannels0);
15581 fwrite(&numChannels, 1, 2, fp);
15583 int numEntries0 = 256;
15584 unsigned short numEntries = GetShortEndian((
unsigned char *)&numEntries0);
15585 fwrite(&numEntries, 1, 2, fp);
15587 int entrySize0 = 1;
15588 unsigned short entrySize = GetShortEndian((
unsigned char *)&entrySize0);
15589 fwrite(&entrySize, 1, 2, fp);
15591 unsigned char ramp[256];
15594 for (
int i = 0; i < 256; i++) ramp[i] = i * co_red / 100.;
15595 fwrite(ramp, 256, 1, fp);
15598 for (
int i = 0; i < 256; i++) ramp[i] = i * co_green / 100.;
15599 fwrite(ramp, 256, 1, fp);
15602 for (
int i = 0; i < 256; i++) ramp[i] = i * co_blue / 100.;
15603 fwrite(ramp, 256, 1, fp);
Select * pSelectAIS
Global instance.
AisDecoder * g_pAIS
Global instance.
Class AisDecoder and helpers.
Global state for AIS decoder.
Class AISTargetAlertDialog and helpers.
Class AISTargetQueryDialog.
std::unique_ptr< HostApi > GetHostApi()
HostApi factory,.
Chart canvas configuration state
Class CanvasOptions and helpers – Canvas options Window/Dialog.
ChartDB * ChartData
Global instance.
Charts database management
ChartGroupArray * g_pGroupArray
Global instance.
ChartCanvas * g_focusCanvas
Global instance.
arrayofCanvasPtr g_canvasArray
Global instance.
ChartCanvas * g_overlayCanvas
Global instance.
Generic Chart canvas base.
ChartCanvas * g_focusCanvas
Global instance.
ChartCanvas * g_overlayCanvas
Global instance.
Dialog for displaying a list of AIS targets.
Dialog for querying detailed information about an AIS target.
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &caption=_("Object Query"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=AIS_TARGET_QUERY_STYLE)
Creation.
Minimal ChartCAnvas interface with very little dependencies.
Handles context menu events for the chart canvas.
A custom panel for displaying chart information.
Base class for BSB (Maptech/NOS) format nautical charts.
Base class for all chart types.
ChartCanvas - Main chart display and interaction component.
void CloseTideDialog()
Close any open tide dialog.
bool GetCanvasPointPixVP(ViewPort &vp, double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates rounded to nearest integer using specified vie...
void OnPaint(wxPaintEvent &event)
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
void DoMovement(long dt)
Performs a step of smooth movement animation on the chart canvas.
void ShowSingleTideDialog(int x, int y, void *pvIDX)
Display tide/current dialog with single-instance management.
obs::EventVar json_msg
Notified with message targeting all plugins.
void GetDoubleCanvasPointPixVP(ViewPort &vp, double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision,...
double m_cursor_lat
The latitude in degrees corresponding to the most recently processed cursor position.
double GetCanvasScaleFactor()
Return the number of logical pixels per meter for the screen.
double GetPixPerMM()
Get the number of logical pixels per millimeter on the screen.
void SetDisplaySizeMM(double size)
Set the width of the screen in millimeters.
int PrepareContextSelections(double lat, double lon)
bool MouseEventSetup(wxMouseEvent &event, bool b_handle_dclick=true)
bool PanCanvas(double dx, double dy)
Pans (moves) the canvas by the specified physical pixels in x and y directions.
void ZoomCanvasSimple(double factor)
Perform an immediate zoom operation without smooth transitions.
bool SetVPScale(double sc, bool b_refresh=true)
Sets the viewport scale while maintaining the center point.
float GetVPScale() override
Return ViewPort scale factor, in physical pixels per meter.
double m_cursor_lon
The longitude in degrees corresponding to the most recently processed cursor position.
void GetCanvasPixPoint(double x, double y, double &lat, double &lon)
Convert canvas pixel coordinates (physical pixels) to latitude/longitude.
bool IsTideDialogOpen() const
void ZoomCanvas(double factor, bool can_zoom_to_cursor=true, bool stoptimer=true)
Perform a smooth zoom operation on the chart canvas by the specified factor.
void DrawTCWindow(int x, int y, void *pIDX)
Legacy tide dialog creation method.
void GetDoubleCanvasPointPix(double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision.
bool SetViewPoint(double lat, double lon)
Centers the view on a specific lat/lon position.
bool MouseEventProcessCanvas(wxMouseEvent &event)
Processes mouse events for core chart panning and zooming operations.
Represents a user-defined collection of logically related charts.
Represents an MBTiles format chart.
Wrapper class for plugin-based charts.
wxFont * FindOrCreateFont(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline=false, const wxString &facename=wxEmptyString, wxFontEncoding encoding=wxFONTENCODING_DEFAULT)
Creates or finds a matching font in the font cache.
wxColour GetFontColor(const wxString &TextElement) const
Gets the text color for a UI element.
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Get a font object for a UI element.
Locked down interface published in 5.14.0.
Modal dialog displaying hotkeys.
Represents an index entry for tidal and current data.
char IDX_type
Entry type identifier "TCtcIUu".
char IDX_station_name[MAXNAMELEN]
Name of the tidal or current station.
int station_tz_offset
Offset in seconds to convert from harmonic data (epochs) to the station time zone.
double IDX_lat
Latitude of the station (in degrees, +North)
double IDX_lon
Longitude of the station (in degrees, +East)
bool b_skipTooDeep
Flag to skip processing if depth exceeds limit.
Station_Data * pref_sta_data
Pointer to the reference station data.
int IDX_rec_num
Record number for multiple entries with same name.
Modern User Interface Control Bar for OpenCPN.
Dialog for displaying and editing waypoint properties.
An iterator class for OCPNRegion.
A wrapper class for wxRegion with additional functionality.
Data for a loaded plugin, including dl-loaded library.
int m_cap_flag
PlugIn Capabilities descriptor.
PluginLoader is a backend module without any direct GUI functionality.
const ArrayOfPlugIns * GetPlugInArray()
Return list of currently loaded plugins.
bool Compose(const ViewPort &vp)
Represents a waypoint or mark within the navigation system.
wxString m_MarkDescription
Description text for the waypoint.
LLBBox m_wpBBox
Bounding box for the waypoint.
bool m_bRPIsBeingEdited
Flag indicating if this waypoint is currently being edited.
wxRect CurrentRect_in_DC
Current rectangle occupied by the waypoint in the display.
wxString m_GUID
Globally Unique Identifier for the waypoint.
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
bool m_bIsActive
Flag indicating if this waypoint is active for navigation.
bool m_bIsInRoute
Flag indicating if this waypoint is part of a route.
double m_seg_len
Length of the leg from previous waypoint to this waypoint in nautical miles.
bool m_bShowName
Flag indicating if the waypoint name should be shown.
bool m_bBlink
Flag indicating if the waypoint should blink when displayed.
bool m_bPtIsSelected
Flag indicating if this waypoint is currently selected.
bool m_bIsVisible
Flag indicating if the waypoint should be drawn on the chart.
bool m_bIsInLayer
Flag indicating if the waypoint belongs to a layer.
int m_LayerID
Layer identifier if the waypoint belongs to a layer.
Represents a navigational route in the navigation system.
bool m_bRtIsSelected
Flag indicating whether this route is currently selected in the UI.
RoutePointList * pRoutePointList
Ordered list of waypoints (RoutePoints) that make up this route.
double m_route_length
Total length of the route in nautical miles, calculated using rhumb line (Mercator) distances.
bool m_bRtIsActive
Flag indicating whether this route is currently active for navigation.
int m_width
Width of the route line in pixels when rendered on the chart.
wxString m_RouteNameString
User-assigned name for the route.
wxString m_GUID
Globally unique identifier for this route.
bool m_bIsInLayer
Flag indicating whether this route belongs to a layer.
int m_lastMousePointIndex
Index of the most recently interacted with route point.
bool m_bIsBeingEdited
Flag indicating that the route is currently being edited by the user.
bool m_NextLegGreatCircle
Flag indicating whether the next leg should be calculated using great circle navigation or rhumb line...
wxArrayPtrVoid * GetRouteArrayContaining(RoutePoint *pWP)
Find all routes that contain the given waypoint.
bool ActivateNextPoint(Route *pr, bool skipped)
Activates the next waypoint in a route when the current waypoint is reached.
bool DeleteRoute(Route *pRoute)
Dialog for displaying query results of S57 objects.
IDX_entry * GetCurrentIDX() const
Represents a single point in a track.
wxDateTime GetCreateTime(void)
Retrieves the creation timestamp of a track point as a wxDateTime object.
void SetCreateTime(wxDateTime dt)
Sets the creation timestamp for a track point.
Represents a track, which is a series of connected track points.
ViewPort - Core geographic projection and coordinate transformation engine.
void SetBoxes()
Computes the bounding box coordinates for the current viewport.
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
double ref_scale
The nominal scale of the "reference chart" for this view.
int pix_height
Height of the viewport in physical pixels.
double rotation
Rotation angle of the viewport in radians.
void SetPixelScale(double scale)
Set the physical to logical pixel ratio for the display.
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.
double tilt
Tilt angle for perspective view in radians.
double skew
Angular distortion (shear transform) applied to the viewport in radians.
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
double clon
Center longitude of the viewport in degrees.
double clat
Center latitude of the viewport in degrees.
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).
bool AddRoutePoint(RoutePoint *prp)
Add a point to list which owns it.
bool RemoveRoutePoint(RoutePoint *prp)
Remove a routepoint from list if present, deallocate it all cases.
Encapsulates persistent canvas configuration.
double iLat
Latitude of the center of the chart, in degrees.
bool bShowOutlines
Display chart outlines.
bool bShowDepthUnits
Display depth unit indicators.
double iLon
Longitude of the center of the chart, in degrees.
double iRotation
Initial rotation angle in radians.
bool bCourseUp
Orient display to course up.
bool bQuilt
Enable chart quilting.
bool bFollow
Enable vessel following mode.
double iScale
Initial chart scale factor.
bool bShowENCText
Display ENC text elements.
bool bShowAIS
Display AIS targets.
bool bShowGrid
Display coordinate grid.
bool bShowCurrents
Display current information.
bool bShowTides
Display tide information.
bool bLookahead
Enable lookahead mode.
bool bHeadUp
Orient display to heading up.
bool bAttenAIS
Enable AIS target attenuation.
Represents a composite CM93 chart covering multiple scales.
Stores emboss effect data for textures.
OpenGL chart rendering canvas.
void Notify() override
Notify all listeners, no data supplied.
Represents a compass display in the OpenCPN navigation system.
wxRect GetRect(void) const
Return the coordinates of the compass widget, in physical pixels relative to the canvas window.
wxRect GetLogicalRect(void) const
Return the coordinates of the compass widget, in logical pixels.
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.
Represents an S57 format electronic navigational chart in OpenCPN.
Class cm93chart and helpers – CM93 chart state.
Global variables reflecting command line options and arguments.
APConsole * console
Global instance.
Primary navigation console display for route and vessel tracking.
bool g_bsmoothpanzoom
Controls how the chart panning and zooming smoothing is done during user interactions.
bool g_bRollover
enable/disable mouse rollover GUI effects
double g_COGAvg
Debug only usage.
int g_COGAvgSec
COG average period for Course Up Mode (sec)
int g_iDistanceFormat
User-selected distance (horizontal) unit format for display and input.
double g_display_size_mm
Physical display width (mm)
std::vector< ConnectionParams * > & TheConnectionParams()
Return global list of connections.
PopUpDSlide * pPopupDetailSlider
Global instance.
Chart display details slider.
std::vector< OCPN_MonitorInfo > g_monitor_info
Information about the monitors connected to the system.
OpenGL chart rendering canvas.
glTextureManager * g_glTextureManager
Global instance.
OpenGL texture container.
GoToPositionDialog * pGoToPositionDialog
Global instance.
GSHHS Chart Object (Global Self-consistent, Hierarchical, High-resolution Shoreline) Derived from htt...
Hooks into gui available in model.
size_t g_current_monitor
Current monitor displaying main application frame.
double g_current_monitor_dip_px_ratio
ratio to convert between DIP and physical pixels.
bool g_b_overzoom_x
Allow high overzoom.
Miscellaneous globals primarely used by gui layer, not persisted in configuration file.
Hotheys help dialog (the '?' button).
GUI constant definitions.
Read and write KML Format.
MarkInfoDlg * g_pMarkInfoDialog
global instance
Waypoint properties maintenance dialog.
MUI (Modern User Interface) Control bar.
Multiplexer class and helpers.
double AnchorDistFix(double const d, double const AnchorPointMinDist, double const AnchorPointMaxDist)
Return constrained value of d so that AnchorPointMinDist < abs(d) < AnchorPointMaxDist.
MySQL based storage for routes, tracks, etc.
wxString getUsrHeightUnit(int unit)
Get the abbreviation for the preferred height unit.
double toUsrHeight(double m_height, int unit)
Convert height from meters to preferred height units.
double fromUsrDistance(double usr_distance, int unit, int default_val)
Convert distance from user units to nautical miles.
double toUsrDistance(double nm_distance, int unit)
Convert a distance from nautical miles (NMi) to user display units.
wxString FormatDistanceAdaptive(double distance)
Format a distance (given in nautical miles) using the current distance preference,...
Navigation Utility Functions without GUI dependencies.
User notifications manager.
Notification Manager GUI.
OCPN_AUIManager * g_pauimgr
Global instance.
Optimized wxBitmap Object.
ChartFamilyEnumPI
Enumeration of chart families (broad categories).
#define OVERLAY_LEGACY
Overlay rendering priorities determine the layering order of plugin graphics.
#define OVERLAY_OVER_UI
Highest priority for overlays above all UI elements.
#define OVERLAY_OVER_EMBOSS
Priority for overlays above embossed chart features.
#define OVERLAY_OVER_SHIPS
Priority for overlays that should appear above ship symbols.
int GetCanvasCount()
Gets total number of chart canvases.
PI_DisCat GetENCDisplayCategory(int CanvasIndex)
Gets current ENC display category.
int GetCanvasIndexUnderMouse()
Gets index of chart canvas under mouse cursor.
int GetChartbarHeight()
Gets height of chart bar in pixels.
double OCPN_GetWinDIPScaleFactor()
Gets Windows-specific DPI scaling factor.
Miscellaneous utilities, many of which string related.
Layer to use wxDC or opengl.
options * g_options
Global instance.
bool bGPSValid
Indicate whether the Global Navigation Satellite System (GNSS) has a valid position.
double gHdt
True heading in degrees (0-359.99).
double gLat
Vessel's current latitude in decimal degrees.
double gCog
Course over ground in degrees (0-359.99).
double gSog
Speed over ground in knots.
double gLon
Vessel's current longitude in decimal degrees.
Position, course, speed, etc.
Tools to send data to plugins.
PlugInManager * g_pi_manager
Global instance.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
Purpose: Track and Trackpoint drawing stuff.
RoutePropDlgImpl * pRoutePropDialog
Global instance.
RoutePoint * pAnchorWatchPoint2
Global instance.
Routeman * g_pRouteMan
Global instance.
RouteList * pRouteList
Global instance.
RoutePoint * pAnchorWatchPoint1
Global instance.
float g_ChartScaleFactorExp
Global instance.
RouteManagerDialog * pRouteManagerDialog
Global instance.
S57QueryDialog * g_pObjectQueryDialog
Global instance.
S57 object query result window.
Select * pSelect
Global instance.
Select * pSelectTC
Global instance.
Selected route, segment, waypoint, etc.
A single, selected generic item.
SENCThreadManager * g_SencThreadManager
Global instance.
ShapeBaseChartSet gShapeBasemap
global instance
Represents an entry in the chart table, containing information about a single chart.
Tide and currents window.
TCMgr * ptcmgr
Global instance.
Tide and Current Manager @TODO Add original author copyright.
ThumbWin * pthumbwin
Global instance.
Timer identification constants.
Abstract gFrame/MyFrame interface.
ActiveTrack * g_pActiveTrack
global instance
std::vector< Track * > g_TrackList
Global instance.
Recorded track abstraction.
Track and Trackpoint drawing stuff.
TrackPropDlg * pTrackPropDialog
Global instance.
Framework for Undo features.