From eea9900d8510b2cc48c75466009b13da78d7a200 Mon Sep 17 00:00:00 2001 From: Theeko Date: Thu, 15 Mar 2018 00:49:16 +0100 Subject: [PATCH 01/11] Change comfort model and scoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Of course, this is very empirical, and based on my proper sailing experience but changes include: * Course over wind score is changed to a normal distribution center on 30°, as maximum incomfort is felt when sailing on the wind (30-40°) * Wind and waves scores are changed to an exponential function --- src/RouteMapOverlay.cpp | 89 +++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/src/RouteMapOverlay.cpp b/src/RouteMapOverlay.cpp index cfaf4a68..f1ba406d 100644 --- a/src/RouteMapOverlay.cpp +++ b/src/RouteMapOverlay.cpp @@ -495,57 +495,78 @@ void RouteMapOverlay::RenderPolarChangeMarks(Position *pos, wrDC &dc, PlugIn_Vie } /* Customization ComfortDisplay - * ----------------------------------------------------- - * The idea is to display the weather route with different - * colors giving an idea of the sailing comfort: - * Green = Light conditions, relax and enjoy - * Orange = Can be tough, stay focus - * Red = Strong conditions, heavy sailors, be prepared + * ---------------------------------------------------------------------------- + * The idea is to display on the weather route different colors giving an idea + * of the sailing comfort along the trip: + * Green = Light conditions, relax and enjoy + * Orange = Can be tough, stay focus + * Red = Strong conditions, heavy sailors, be prepared */ int RouteMapOverlay::sailingConditionLevel(PlotData plot) { - /* Method to calculate a indicator between 1 and 3 - * of the sailing conditions based on sea conditions - * such as waves, wind, current. - */ - - double level_calc = 0.0; - - /* Define coefficient to weight relative impact - * of the parameter on sailing comfort + /* Method to calculate a indicator between 1 and 3 of the sailing conditions + * based on wind, wind course and waves. * - * Definitions: - * C - Sea Current Direction over ground - * VC - Velocity of Current - * W - Wind Direction - * WG - Wind direction over ground - * VWG - Velocity of wind over ground - * WA - Angle of wind relative to true north - * VW - velocity of wind over water + * All these calculations are empirical and just made from experience and how + * people feel sailing comfort which is a highly subjective value... */ - double coef_WV = 1.0; - double coef_W = 1.0; + double level_calc = 0.0; - // Define MAX constants. Over this value, the ratio - // is more than 1 which means it is very impacting - // the sailing comfort. - double MAX_WV = 30; // No more than 30knt, please - double MAX_W = 50; // No more than 50° app. wind + // Define mximum constants. Over this value, sailing comfort is very impacted + // (coef > 1) and automatically displayed in red. + // Definitions: + // AW - Apparent Wind Direction from the boat (0 = upwind) + // VW - Velocity of wind over water + //WVHT - Swell (if available) + double MAX_WV = 27; // Vigilant over 27knts/7B + double MAX_AW = 30; // Upwind + double MAX_WVHT = 5; // No more than 5m waves + // Use a exp function for wind as difficulty sailing comfort + // is exponentially degraded with wind speed. + // Over 30knts, it starts to be difficult double WV = plot.VW; - double W = plot.W; + double WV_normal = exp((1.32*WV-MAX_WV)/MAX_WV)-exp(-1); + if (WV_normal > 1) + WV_normal = 1.0; + + // Use a normal distribution to set the maximum difficulty at 30° upwind, + // and reduce when we go downwind. Also, take into account that sailing + // on the wind becomes much more difficult when wind speed increases. + double AW = heading_resolve(plot.B-plot.W); + double AW_normal = ((1/((0.67*MAX_AW)*sqrt(2*M_PI))) * \ + exp(-(pow(AW-MAX_AW, 2))/(2*pow(0.67*MAX_AW,2))) * \ + 75.19) * WV_normal; + if (AW_normal > 1) + AW_normal = 1.0; + // If available, add swell conditions in comfort model. + // Use same exponential function for swell as sailing + // comfort exponentially decrease with swell height. + double WVHT = plot.WVHT; + double WVHT_normal = 0.0; + if (WVHT > 0) + WVHT_normal = exp((1.32*WVHT-MAX_WVHT)/MAX_WVHT)-exp(-1); + if (WVHT_normal > 1) + WVHT_normal = 1; - level_calc = (coef_WV * WV/MAX_WV + coef_W * MAX_W/W) / (coef_WV + coef_W); + // Calculate score + // Use an OR function X,Y E [0,1], f(X,Y) = 1-(1-X)(1-Y) + double WV_coef = 1.0; + double AW_coef = 0.6; + double WVHT_coef = 0.5; + level_calc = 1 - (1 - WV_coef * WV_normal) * \ + (1 - AW_coef * AW_normal) * \ + (1 - WVHT_coef * WVHT_normal); if (level_calc <= 0.5) // Light conditions, enjoy ;-) return 1; - if (level_calc > 0.5 && level_calc <= 1) + if (level_calc > 0.5 && level_calc < 1) // Can be tough return 2; - if (level_calc > 1) + if (level_calc >= 1) // Strong conditions return 3; return 0; From 76afa4915f417c0fa099ec502d1d3a73cfa6f848 Mon Sep 17 00:00:00 2001 From: Theeko Date: Sat, 17 Mar 2018 00:42:13 +0100 Subject: [PATCH 02/11] FIX error with cache for wind barbs on route I think caching wind barbs on route creates more problems than it solves in saving few CPU. With boat polar change or minor time modification, route was updated but not the related wind barbs... --- src/RouteMapOverlay.cpp | 58 ++++++++++++++++------------------------- src/RouteMapOverlay.h | 3 --- 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/src/RouteMapOverlay.cpp b/src/RouteMapOverlay.cpp index f1ba406d..59fb4c7b 100644 --- a/src/RouteMapOverlay.cpp +++ b/src/RouteMapOverlay.cpp @@ -62,7 +62,7 @@ RouteMapOverlay::RouteMapOverlay() : m_UpdateOverlay(true), m_bEndRouteVisible(false), m_Thread(NULL), last_cursor_lat(0), last_cursor_lon(0), last_cursor_position(NULL), destination_position(NULL), last_destination_position(NULL), - m_bUpdated(false), m_overlaylist(0), wind_barb_cache_origin_size(0), current_cache_origin_size(0), wind_barb_route_cache_origin_size(0) + m_bUpdated(false), m_overlaylist(0), current_cache_origin_size(0) { } @@ -710,13 +710,6 @@ void RouteMapOverlay::RenderWindBarbsOnRoute(wrDC &dc, PlugIn_ViewPort &vp) if (origin.size() < 2) return; - // if not, then check if wind barbs have to be - // calculated or are stored in cache line buffer. - bool toCompute = origin.size() != wind_barb_route_cache_origin_size || - vp.view_scale_ppm != wind_barb_route_cache_scale || - vp.m_projection_type != wind_barb_route_cache_projection || - vp.m_projection_type != PI_PROJECTION_MERCATOR; - // Create a specific viewport at position (0,0) // to draw the winds barbs, and then translate it PlugIn_ViewPort nvp = vp; @@ -729,37 +722,30 @@ void RouteMapOverlay::RenderWindBarbsOnRoute(wrDC &dc, PlugIn_ViewPort &vp) // calculate wind barbs along the route by looping // over [GetPlotData(false)] list which contains lat, // lon, wind info for each points, only if needed. - if (toCompute) + std::list plot = GetPlotData(false); + std::list::iterator it; + for (it = plot.begin(); it != plot.end(); it++) { - wind_barb_route_cache_origin_size = origin.size(); - wind_barb_route_cache_scale = vp.view_scale_ppm; - wind_barb_route_cache_projection = vp.m_projection_type; + wxPoint p; + GetCanvasPixLL(&nvp, &p, it->lat, it->lon); - std::list plot = GetPlotData(false); - std::list::iterator it; - for (it = plot.begin(); it != plot.end(); it++) - { - wxPoint p; - GetCanvasPixLL(&nvp, &p, it->lat, it->lon); - - double VW = it->VW; - double W = it->W; - - // Calculate the offset to put the head - // of the arrow on the route (and not the - // middle of the arrow) for readability. - int xOffset, yOffset; - xOffset = (int)(0.5 * 35 * sin(deg2rad(W))); - yOffset = (int)(0.5 * 35 * cos(deg2rad(W))); - - // Draw barbs - g_barbsOnRoute_LineBufferOverlay.pushWindArrowWithBarbs( - wind_barb_route_cache, p.x + xOffset, p.y - yOffset, VW, - deg2rad(W) + vp.rotation, it->lat < 0 - ); - } - wind_barb_route_cache.Finalize(); + double VW = it->VW; + double W = it->W; + + // Calculate the offset to put the head + // of the arrow on the route (and not the + // middle of the arrow) for readability. + int xOffset, yOffset; + xOffset = (int)(0.5 * 35 * sin(deg2rad(W))); + yOffset = (int)(0.5 * 35 * cos(deg2rad(W))); + + // Draw barbs + g_barbsOnRoute_LineBufferOverlay.pushWindArrowWithBarbs( + wind_barb_route_cache, p.x + xOffset, p.y - yOffset, VW, + deg2rad(W) + vp.rotation, it->lat < 0 + ); } + wind_barb_route_cache.Finalize(); // Draw the wind barbs wxPoint point; diff --git a/src/RouteMapOverlay.h b/src/RouteMapOverlay.h index 2b06dfc2..3712cf1a 100644 --- a/src/RouteMapOverlay.h +++ b/src/RouteMapOverlay.h @@ -130,9 +130,6 @@ class RouteMapOverlay : public RouteMap // Customization WindBarbsOnRoute LineBuffer wind_barb_route_cache; - double wind_barb_route_cache_scale; - size_t wind_barb_route_cache_origin_size; - int wind_barb_route_cache_projection; LineBuffer current_cache; double current_cache_scale; From 4a26ddd3f766af4bb8cf3a5aa899e85d2688033f Mon Sep 17 00:00:00 2001 From: Theeko Date: Sat, 17 Mar 2018 20:15:52 +0100 Subject: [PATCH 03/11] ADD cursor bar on the plot to show where we are in time --- WeatherRouting.fbp | 12 ++++++------ src/PlotDialog.cpp | 24 +++++++++++++++++++++++- src/PlotDialog.h | 2 ++ src/WeatherRoutingUI.cpp | 5 +++++ src/WeatherRoutingUI.h | 1 + 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/WeatherRouting.fbp b/WeatherRouting.fbp index 818e900f..720ecda9 100644 --- a/WeatherRouting.fbp +++ b/WeatherRouting.fbp @@ -27,7 +27,7 @@ UI 0 0 - + 0 wxAUI_MGR_DEFAULT @@ -10779,7 +10779,7 @@ 0 - + -1,210 1 m_PlotWindow 1 @@ -10821,7 +10821,7 @@ OnSizePlot - + OnUpdateUI @@ -11489,7 +11489,7 @@ 0 - + 130,-1 1 m_stMousePosition1 1 @@ -22915,11 +22915,11 @@ - + 5 wxALL|wxEXPAND 0 - + 1 1 1 diff --git a/src/PlotDialog.cpp b/src/PlotDialog.cpp index 277a0de6..9813e7c9 100644 --- a/src/PlotDialog.cpp +++ b/src/PlotDialog.cpp @@ -50,6 +50,7 @@ PlotDialog::PlotDialog( WeatherRouting &weatherrouting ) #endif m_WeatherRouting(weatherrouting) { + } PlotDialog::~PlotDialog() @@ -232,10 +233,26 @@ void PlotDialog::OnPaintPlot(wxPaintEvent& event) lx = x, ly = y; } } + + // Cursor Customization + // ---------------------------------------------------------------- + // Draw a cursor on the graph to show + // the current time position by the GRIB file + wxDateTime gribTime = m_WeatherRouting.m_ConfigurationDialog.m_GribTimelineTime; + double cursorTime = (gribTime - m_StartTime).GetSeconds().ToDouble(); + int x_cursor = w * (scale * ((cursorTime - m_mintime) / (m_maxtime - m_mintime) - \ + position) + position); + + wxColor orange(255, 165, 0); + wxPen cursorPen(orange, 3, wxPENSTYLE_DOT); + dc.SetPen(cursorPen); + dc.DrawLine(x_cursor, 0, x_cursor, h); + + // ---------------------------------------------------------------- dc.SetTextForeground(*wxBLACK); dc.SetPen(wxPen(*wxBLACK, 1, wxPENSTYLE_DOT)); - + const double steps = 10; bool grid = true; for(double i=1/steps; i<1-1/steps; i+=1/steps) { @@ -283,3 +300,8 @@ void PlotDialog::SetRouteMapOverlay(RouteMapOverlay *routemapoverlay) GetScale(); m_PlotWindow->Refresh(); } + +void PlotDialog::OnUpdateUI(wxUpdateUIEvent &event) +{ + SetRouteMapOverlay(m_WeatherRouting.FirstCurrentRouteMap()); +} diff --git a/src/PlotDialog.h b/src/PlotDialog.h index 4107649e..473df0ad 100644 --- a/src/PlotDialog.h +++ b/src/PlotDialog.h @@ -57,6 +57,7 @@ class PlotDialog : public PlotDialogBase void OnUpdatePlot( wxScrollEvent& event ) { m_PlotWindow->Refresh(); } void OnUpdatePlotVariable( wxCommandEvent& event ) { GetScale(); m_PlotWindow->Refresh(); } void OnUpdateRoute( wxCommandEvent& event ); + void OnUpdateUI( wxUpdateUIEvent& event ); private: @@ -72,6 +73,7 @@ class PlotDialog : public PlotDialogBase std::list m_PlotData; WeatherRouting &m_WeatherRouting; + }; #endif diff --git a/src/WeatherRoutingUI.cpp b/src/WeatherRoutingUI.cpp index e36f47a4..57b4f421 100644 --- a/src/WeatherRoutingUI.cpp +++ b/src/WeatherRoutingUI.cpp @@ -1362,6 +1362,8 @@ PlotDialogBase::PlotDialogBase( wxWindow* parent, wxWindowID id, const wxString& m_PlotWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); m_PlotWindow->SetScrollRate( 5, 5 ); + m_PlotWindow->SetMinSize( wxSize( -1,210 ) ); + fgSizer3->Add( m_PlotWindow, 1, wxEXPAND | wxALL, 5 ); wxFlexGridSizer* fgSizer12; @@ -1418,6 +1420,7 @@ PlotDialogBase::PlotDialogBase( wxWindow* parent, wxWindowID id, const wxString& m_stMousePosition1 = new wxStaticText( this, wxID_ANY, _(" N/A "), wxDefaultPosition, wxDefaultSize, 0 ); m_stMousePosition1->Wrap( -1 ); m_stMousePosition1->SetForegroundColour( wxColour( 251, 2, 7 ) ); + m_stMousePosition1->SetMinSize( wxSize( 130,-1 ) ); fgSizer78->Add( m_stMousePosition1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); @@ -1513,6 +1516,7 @@ PlotDialogBase::PlotDialogBase( wxWindow* parent, wxWindowID id, const wxString& m_PlotWindow->Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( PlotDialogBase::OnMouseEventsPlot ), NULL, this ); m_PlotWindow->Connect( wxEVT_PAINT, wxPaintEventHandler( PlotDialogBase::OnPaintPlot ), NULL, this ); m_PlotWindow->Connect( wxEVT_SIZE, wxSizeEventHandler( PlotDialogBase::OnSizePlot ), NULL, this ); + m_PlotWindow->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( PlotDialogBase::OnUpdateUI ), NULL, this ); m_sPosition->Connect( wxEVT_SCROLL_TOP, wxScrollEventHandler( PlotDialogBase::OnUpdatePlot ), NULL, this ); m_sPosition->Connect( wxEVT_SCROLL_BOTTOM, wxScrollEventHandler( PlotDialogBase::OnUpdatePlot ), NULL, this ); m_sPosition->Connect( wxEVT_SCROLL_LINEUP, wxScrollEventHandler( PlotDialogBase::OnUpdatePlot ), NULL, this ); @@ -1555,6 +1559,7 @@ PlotDialogBase::~PlotDialogBase() m_PlotWindow->Disconnect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( PlotDialogBase::OnMouseEventsPlot ), NULL, this ); m_PlotWindow->Disconnect( wxEVT_PAINT, wxPaintEventHandler( PlotDialogBase::OnPaintPlot ), NULL, this ); m_PlotWindow->Disconnect( wxEVT_SIZE, wxSizeEventHandler( PlotDialogBase::OnSizePlot ), NULL, this ); + m_PlotWindow->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( PlotDialogBase::OnUpdateUI ), NULL, this ); m_sPosition->Disconnect( wxEVT_SCROLL_TOP, wxScrollEventHandler( PlotDialogBase::OnUpdatePlot ), NULL, this ); m_sPosition->Disconnect( wxEVT_SCROLL_BOTTOM, wxScrollEventHandler( PlotDialogBase::OnUpdatePlot ), NULL, this ); m_sPosition->Disconnect( wxEVT_SCROLL_LINEUP, wxScrollEventHandler( PlotDialogBase::OnUpdatePlot ), NULL, this ); diff --git a/src/WeatherRoutingUI.h b/src/WeatherRoutingUI.h index 1cb0396e..d34bf5d3 100644 --- a/src/WeatherRoutingUI.h +++ b/src/WeatherRoutingUI.h @@ -342,6 +342,7 @@ class PlotDialogBase : public wxDialog virtual void OnMouseEventsPlot( wxMouseEvent& event ) { event.Skip(); } virtual void OnPaintPlot( wxPaintEvent& event ) { event.Skip(); } virtual void OnSizePlot( wxSizeEvent& event ) { event.Skip(); } + virtual void OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); } virtual void OnUpdatePlot( wxScrollEvent& event ) { event.Skip(); } virtual void OnUpdatePlotVariable( wxCommandEvent& event ) { event.Skip(); } virtual void OnUpdateRoute( wxCommandEvent& event ) { event.Skip(); } From 71dcf008b158f3fefddcb96e3a5b8835a0f8fbed Mon Sep 17 00:00:00 2001 From: Theeko Date: Sun, 18 Mar 2018 01:21:54 +0100 Subject: [PATCH 04/11] Fix cursor --- src/PlotDialog.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/PlotDialog.cpp b/src/PlotDialog.cpp index 9813e7c9..76ba3172 100644 --- a/src/PlotDialog.cpp +++ b/src/PlotDialog.cpp @@ -240,14 +240,16 @@ void PlotDialog::OnPaintPlot(wxPaintEvent& event) // the current time position by the GRIB file wxDateTime gribTime = m_WeatherRouting.m_ConfigurationDialog.m_GribTimelineTime; double cursorTime = (gribTime - m_StartTime).GetSeconds().ToDouble(); - int x_cursor = w * (scale * ((cursorTime - m_mintime) / (m_maxtime - m_mintime) - \ - position) + position); - - wxColor orange(255, 165, 0); - wxPen cursorPen(orange, 3, wxPENSTYLE_DOT); - dc.SetPen(cursorPen); - dc.DrawLine(x_cursor, 0, x_cursor, h); - + if (cursorTime <= m_maxtime || cursorTime >= m_mintime) + { + int x_cursor = w * (scale * ((cursorTime - m_mintime) / (m_maxtime - m_mintime) - \ + position) + position); + + wxColor orange(255, 165, 0); + wxPen cursorPen(orange, 3, wxPENSTYLE_DOT); + dc.SetPen(cursorPen); + dc.DrawLine(x_cursor, 0, x_cursor, h); + } // ---------------------------------------------------------------- dc.SetTextForeground(*wxBLACK); From f0be797ee7f0e69381c9e48afda9c557b03b8f95 Mon Sep 17 00:00:00 2001 From: Theeko Date: Sun, 18 Mar 2018 01:28:43 +0100 Subject: [PATCH 05/11] Delete "Variable" label on plot which are useless --- WeatherRouting.fbp | 251 +-------------------------------------- src/WeatherRoutingUI.cpp | 20 +--- src/WeatherRoutingUI.h | 3 - 3 files changed, 2 insertions(+), 272 deletions(-) diff --git a/WeatherRouting.fbp b/WeatherRouting.fbp index 818e900f..3ad3edd0 100644 --- a/WeatherRouting.fbp +++ b/WeatherRouting.fbp @@ -11269,7 +11269,7 @@ wxEXPAND 1 - 3 + 2 wxBOTH @@ -11280,89 +11280,6 @@ none 0 0 - - 5 - wxALIGN_CENTER_VERTICAL|wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - 255,0,0 - 1 - - 0 - 0 - wxID_ANY - Variable - - 0 - - - 0 - - 1 - m_staticText140 - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - - - -1 - - - - - - - - - - - - - - - - - - - - - - - - - 5 wxALL @@ -11534,89 +11451,6 @@ - - 5 - wxALIGN_CENTER_VERTICAL|wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - 0,255,0 - 1 - - 0 - 0 - wxID_ANY - Variable - - 0 - - - 0 - - 1 - m_staticText1401 - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - - - -1 - - - - - - - - - - - - - - - - - - - - - - - - - 5 wxALL @@ -11788,89 +11622,6 @@ - - 5 - wxALIGN_CENTER_VERTICAL|wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - 0,0,255 - 1 - - 0 - 0 - wxID_ANY - Variable - - 0 - - - 0 - - 1 - m_staticText14011 - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - - - -1 - - - - - - - - - - - - - - - - - - - - - - - - - 5 wxALL diff --git a/src/WeatherRoutingUI.cpp b/src/WeatherRoutingUI.cpp index e36f47a4..61e66439 100644 --- a/src/WeatherRoutingUI.cpp +++ b/src/WeatherRoutingUI.cpp @@ -1399,16 +1399,10 @@ PlotDialogBase::PlotDialogBase( wxWindow* parent, wxWindowID id, const wxString& fgSizer97->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); wxFlexGridSizer* fgSizer78; - fgSizer78 = new wxFlexGridSizer( 0, 3, 0, 0 ); + fgSizer78 = new wxFlexGridSizer( 0, 2, 0, 0 ); fgSizer78->SetFlexibleDirection( wxBOTH ); fgSizer78->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - m_staticText140 = new wxStaticText( this, wxID_ANY, _("Variable"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText140->Wrap( -1 ); - m_staticText140->SetForegroundColour( wxColour( 255, 0, 0 ) ); - - fgSizer78->Add( m_staticText140, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - wxString m_cVariable1Choices[] = { _("Speed Over Ground (SOG)"), _("Course Over Ground (COG)"), _("Speed Over Water (SOW)"), _("Course Over Water (COW)"), _("Wind Velocity"), _("Wind Direction"), _("Wind Course"), _("Wind Velocity Ground"), _("Wind Direction Ground"), _("Wind Course Ground"), _("Apparent Wind Speed (AWS)"), _("Apparent Wind Angle (AWA)"), _("Wind Gust"), _("Current Velocity"), _("Current Direction"), _("Sig Wave Height"), _("Tacks") }; int m_cVariable1NChoices = sizeof( m_cVariable1Choices ) / sizeof( wxString ); m_cVariable1 = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_cVariable1NChoices, m_cVariable1Choices, 0 ); @@ -1421,12 +1415,6 @@ PlotDialogBase::PlotDialogBase( wxWindow* parent, wxWindowID id, const wxString& fgSizer78->Add( m_stMousePosition1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_staticText1401 = new wxStaticText( this, wxID_ANY, _("Variable"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText1401->Wrap( -1 ); - m_staticText1401->SetForegroundColour( wxColour( 0, 255, 0 ) ); - - fgSizer78->Add( m_staticText1401, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - wxString m_cVariable2Choices[] = { _("Speed Over Ground (SOG)"), _("Course Over Ground (COG)"), _("Speed Over Water (SOW)"), _("Course Over Water (COW)"), _("Wind Velocity"), _("Wind Direction"), _("Wind Course"), _("Wind Velocity Ground"), _("Wind Direction Ground"), _("Wind Course Ground"), _("Apparent Wind Speed (AWS)"), _("Apparent Wind Angle (AWA)"), _("Wind Gust"), _("Current Velocity"), _("Current Direction"), _("Sig Wave Height"), _("Tacks") }; int m_cVariable2NChoices = sizeof( m_cVariable2Choices ) / sizeof( wxString ); m_cVariable2 = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_cVariable2NChoices, m_cVariable2Choices, 0 ); @@ -1439,12 +1427,6 @@ PlotDialogBase::PlotDialogBase( wxWindow* parent, wxWindowID id, const wxString& fgSizer78->Add( m_stMousePosition2, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_staticText14011 = new wxStaticText( this, wxID_ANY, _("Variable"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText14011->Wrap( -1 ); - m_staticText14011->SetForegroundColour( wxColour( 0, 0, 255 ) ); - - fgSizer78->Add( m_staticText14011, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - wxString m_cVariable3Choices[] = { _("Speed Over Ground (SOG)"), _("Course Over Ground (COG)"), _("Speed Over Water (SOW)"), _("Course Over Water (COW)"), _("Wind Velocity"), _("Wind Direction"), _("Wind Course"), _("Wind Velocity Ground"), _("Wind Direction Ground"), _("Wind Course Ground"), _("Apparent Wind Speed (AWS)"), _("Apparent Wind Angle (AWA)"), _("Wind Gust"), _("Current Velocity"), _("Current Direction"), _("Sig Wave Height"), _("Tacks") }; int m_cVariable3NChoices = sizeof( m_cVariable3Choices ) / sizeof( wxString ); m_cVariable3 = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_cVariable3NChoices, m_cVariable3Choices, 0 ); diff --git a/src/WeatherRoutingUI.h b/src/WeatherRoutingUI.h index 1cb0396e..410e3720 100644 --- a/src/WeatherRoutingUI.h +++ b/src/WeatherRoutingUI.h @@ -325,13 +325,10 @@ class PlotDialogBase : public wxDialog wxSlider* m_sPosition; wxStaticText* m_staticText139; wxSlider* m_sScale; - wxStaticText* m_staticText140; wxChoice* m_cVariable1; wxStaticText* m_stMousePosition1; - wxStaticText* m_staticText1401; wxChoice* m_cVariable2; wxStaticText* m_stMousePosition2; - wxStaticText* m_staticText14011; wxChoice* m_cVariable3; wxStaticText* m_stMousePosition3; wxRadioButton* m_rbCurrentRoute; From 43f8b8841de680c6eda0cb7e1f21920afd343b21 Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Mon, 19 Mar 2018 09:55:10 +0100 Subject: [PATCH 06/11] Fix crash in edit polar dialog when clicking on "Remove" if there is no selected item in the list. --- src/EditPolarDialog.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/EditPolarDialog.cpp b/src/EditPolarDialog.cpp index 4947205f..09a4b2e4 100644 --- a/src/EditPolarDialog.cpp +++ b/src/EditPolarDialog.cpp @@ -81,7 +81,10 @@ void EditPolarDialog::OnAddTrueWindAngle( wxCommandEvent& event ) void EditPolarDialog::OnRemoveTrueWindAngle( wxCommandEvent& event ) { - GetPolar()->RemoveDegreeStep(m_lTrueWindAngles->GetSelection()); + int sel = m_lTrueWindAngles->GetSelection(); + if (sel == -1) + return; + GetPolar()->RemoveDegreeStep(sel); RebuildTrueWindAngles(); RebuildGrid(); } @@ -99,7 +102,10 @@ void EditPolarDialog::OnAddTrueWindSpeed( wxCommandEvent& event ) void EditPolarDialog::OnRemoveTrueWindSpeed( wxCommandEvent& event ) { - GetPolar()->RemoveWindSpeed(m_lTrueWindSpeeds->GetSelection()); + int sel = m_lTrueWindSpeeds->GetSelection(); + if (sel == -1) + return; + GetPolar()->RemoveWindSpeed(sel); RebuildTrueWindSpeeds(); RebuildGrid(); } From de41d1793f04506a736c14dba7ade36f12707b9d Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Mon, 19 Mar 2018 22:32:12 +0100 Subject: [PATCH 07/11] Fix crash when editing polars caused by spurious paint events. For some reason, some spurious paint events are generated by the BoatDialog at the same time the user tries to edit or modify the polar. The code in the paint event handler accesses the polar data in an unprotected way, and reads uninitialized data at some point. This commit fixes this issue by blocking the paint event handlers while the EditPolarDialog is shown. --- src/BoatDialog.cpp | 12 +++++++++++- src/BoatDialog.h | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/BoatDialog.cpp b/src/BoatDialog.cpp index f9025acc..4b3d53bc 100644 --- a/src/BoatDialog.cpp +++ b/src/BoatDialog.cpp @@ -58,7 +58,7 @@ BoatDialog::BoatDialog(WeatherRouting &weatherrouting) #else : BoatDialogBase(&weatherrouting, wxID_ANY, _("Boat"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxSTAY_ON_TOP ), #endif - m_WeatherRouting(weatherrouting), m_PlotScale(0), m_CrossOverRegenerate(false), m_CrossOverGenerationThread(NULL) + m_WeatherRouting(weatherrouting), m_PlotScale(0), m_CrossOverRegenerate(false), m_CrossOverGenerationThread(NULL), m_EditingPolar(false) { // for small screens: don't let boat dialog be larger than screen int w,h; @@ -209,6 +209,9 @@ void BoatDialog::OnMouseEventsPolarPlot( wxMouseEvent& event ) void BoatDialog::OnPaintPlot(wxPaintEvent& event) { + if (m_EditingPolar) + return; + wxWindow *window = dynamic_cast(event.GetEventObject()); if(!window) return; @@ -495,6 +498,9 @@ static int CalcPolarPoints(wxPoint p0, wxPoint p1) void BoatDialog::OnPaintCrossOverChart(wxPaintEvent& event) { + if (m_EditingPolar) + return; + wxWindow *window = dynamic_cast(event.GetEventObject()); if(!window) return; @@ -815,6 +821,8 @@ void BoatDialog::OnEditPolar( wxCommandEvent& event ) if(i == -1) return; + m_EditingPolar = true; + EditPolarDialog dlg(this); dlg.SetPolarIndex(i); @@ -832,6 +840,8 @@ void BoatDialog::OnEditPolar( wxCommandEvent& event ) wxICON_ERROR | wxOK ); } + m_EditingPolar = false; + GenerateCrossOverChart(); RefreshPlots(); } diff --git a/src/BoatDialog.h b/src/BoatDialog.h index 638bf2ce..41c79c3d 100644 --- a/src/BoatDialog.h +++ b/src/BoatDialog.h @@ -90,6 +90,8 @@ class BoatDialog : public BoatDialogBase bool m_CrossOverRegenerate; CrossOverGenerationThread *m_CrossOverGenerationThread; + + bool m_EditingPolar; }; #endif From 2b1b18363205000c4bc429783060c0ec45d6fb6e Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Wed, 21 Mar 2018 15:23:36 +0100 Subject: [PATCH 08/11] Fix example configurations and polars These got broken when the example polars were moved into the Example/ subdirectory, as the boats were not modified and the initial configuration copy logic didn't take subdirectories into account. --- data/boats/Boat-Climatology.xml | 7 ++++--- data/boats/Boat-Test-Power.xml | 7 ++++--- data/boats/Boat-Test.xml | 7 ++++--- src/WeatherRouting.cpp | 24 ++++++++++++++++++------ 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/data/boats/Boat-Climatology.xml b/data/boats/Boat-Climatology.xml index ed1837af..60d9eafb 100644 --- a/data/boats/Boat-Climatology.xml +++ b/data/boats/Boat-Climatology.xml @@ -1,6 +1,7 @@ - - - + + + + diff --git a/data/boats/Boat-Test-Power.xml b/data/boats/Boat-Test-Power.xml index 1fa8cc29..a1ba0737 100644 --- a/data/boats/Boat-Test-Power.xml +++ b/data/boats/Boat-Test-Power.xml @@ -1,6 +1,7 @@ - - - + + + + diff --git a/data/boats/Boat-Test.xml b/data/boats/Boat-Test.xml index dd80bca6..91545706 100644 --- a/data/boats/Boat-Test.xml +++ b/data/boats/Boat-Test.xml @@ -1,6 +1,7 @@ - - - + + + + diff --git a/src/WeatherRouting.cpp b/src/WeatherRouting.cpp index 3e1348f6..1c6562f1 100644 --- a/src/WeatherRouting.cpp +++ b/src/WeatherRouting.cpp @@ -154,7 +154,7 @@ WeatherRouting::WeatherRouting(wxWindow *parent, weather_routing_pi &plugin) /* ensure the directories exist */ wxFileName fn; - fn.Mkdir(weather_routing_pi::StandardPath()); + fn.Mkdir(weather_routing_pi::StandardPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL); fn.Mkdir(boatsdir); fn.Mkdir(polarsdir); @@ -333,11 +333,23 @@ WeatherRouting::~WeatherRouting( ) void WeatherRouting::CopyDataFiles(wxString from, wxString to) { - wxArrayString fns; - wxDir::GetAllFiles(from, &fns); - for (unsigned int i = 0; i < fns.Count(); i++) { - wxFileName f(fns.Item(i)); - wxCopyFile(fns.Item(i), to + wxFileName::GetPathSeparator() + f.GetFullName()); + if (from[from.Len() - 1] != '\\' && from[from.Len() - 1] != '/') from += wxFILE_SEP_PATH; + if (to[to.Len() - 1] != '\\' && to[to.Len() - 1] != '/') to += wxFILE_SEP_PATH; + + if (!wxDirExists(to)) + wxFileName::Mkdir(to, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL); + + wxDir dir(from); + wxString next = wxEmptyString; + bool b = dir.GetFirst(&next); + while (b) { + const wxString fileFrom = from + next; + const wxString fileTo = to + next; + if (wxDirExists(fileFrom)) + CopyDataFiles(fileFrom, fileTo); + else + wxCopyFile(fileFrom, fileTo); + b = dir.GetNext(&next); } } From 84130d2f1de95dc12bb621085f3392fddd6185ba Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Wed, 21 Mar 2018 15:53:20 +0100 Subject: [PATCH 09/11] Handle polar filenames containing the "/" character under Windows. --- src/Boat.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Boat.cpp b/src/Boat.cpp index c6274ca6..f4d4ae58 100644 --- a/src/Boat.cpp +++ b/src/Boat.cpp @@ -75,6 +75,7 @@ wxString Boat::OpenXML(wxString filename, bool shortcut) Polar polar; //(wxString::FromUTF8(e->Attribute("Name"))); polar.FileName = wxString::FromUTF8(e->Attribute("FileName")); + polar.FileName.Replace (_T("/"), wxFileName::GetPathSeparator()); if(!wxFileName::FileExists(polar.FileName)) polar.FileName = weather_routing_pi::StandardPath() + _T("polars") + wxFileName::GetPathSeparator() + polar.FileName; From 67bc80f2ae73c93501296698d24da6e7cb14b693 Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Wed, 21 Mar 2018 18:25:21 +0100 Subject: [PATCH 10/11] Revert "Fix crash when editing polars caused by spurious paint events." This reverts commit de41d1793f04506a736c14dba7ade36f12707b9d. --- src/BoatDialog.cpp | 12 +----------- src/BoatDialog.h | 2 -- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/BoatDialog.cpp b/src/BoatDialog.cpp index 4b3d53bc..f9025acc 100644 --- a/src/BoatDialog.cpp +++ b/src/BoatDialog.cpp @@ -58,7 +58,7 @@ BoatDialog::BoatDialog(WeatherRouting &weatherrouting) #else : BoatDialogBase(&weatherrouting, wxID_ANY, _("Boat"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxSTAY_ON_TOP ), #endif - m_WeatherRouting(weatherrouting), m_PlotScale(0), m_CrossOverRegenerate(false), m_CrossOverGenerationThread(NULL), m_EditingPolar(false) + m_WeatherRouting(weatherrouting), m_PlotScale(0), m_CrossOverRegenerate(false), m_CrossOverGenerationThread(NULL) { // for small screens: don't let boat dialog be larger than screen int w,h; @@ -209,9 +209,6 @@ void BoatDialog::OnMouseEventsPolarPlot( wxMouseEvent& event ) void BoatDialog::OnPaintPlot(wxPaintEvent& event) { - if (m_EditingPolar) - return; - wxWindow *window = dynamic_cast(event.GetEventObject()); if(!window) return; @@ -498,9 +495,6 @@ static int CalcPolarPoints(wxPoint p0, wxPoint p1) void BoatDialog::OnPaintCrossOverChart(wxPaintEvent& event) { - if (m_EditingPolar) - return; - wxWindow *window = dynamic_cast(event.GetEventObject()); if(!window) return; @@ -821,8 +815,6 @@ void BoatDialog::OnEditPolar( wxCommandEvent& event ) if(i == -1) return; - m_EditingPolar = true; - EditPolarDialog dlg(this); dlg.SetPolarIndex(i); @@ -840,8 +832,6 @@ void BoatDialog::OnEditPolar( wxCommandEvent& event ) wxICON_ERROR | wxOK ); } - m_EditingPolar = false; - GenerateCrossOverChart(); RefreshPlots(); } diff --git a/src/BoatDialog.h b/src/BoatDialog.h index 41c79c3d..638bf2ce 100644 --- a/src/BoatDialog.h +++ b/src/BoatDialog.h @@ -90,8 +90,6 @@ class BoatDialog : public BoatDialogBase bool m_CrossOverRegenerate; CrossOverGenerationThread *m_CrossOverGenerationThread; - - bool m_EditingPolar; }; #endif From beddd7f044af08517c6196fa02edb4fd87a13719 Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Wed, 21 Mar 2018 18:25:28 +0100 Subject: [PATCH 11/11] Recalculate VMG when polar changes preventing a crash due to uninitialized data Do recalculate the VMG when: - a new wind speed is added - the EditPolar dialog is closed by clicking Save, in order to take into account the new cell values. Not doing this causes the invalid VMG data to be referenced later, for example by the paint events of the BoatDialog (when drawing the polar or the crossover chart), causing infinite loops and crashes. --- src/Polar.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Polar.cpp b/src/Polar.cpp index a0fa5ca0..923b993a 100644 --- a/src/Polar.cpp +++ b/src/Polar.cpp @@ -361,6 +361,10 @@ bool Polar::Save(const wxString &filename) fputs("\n", f); } fclose(f); + + for(unsigned int VWi = 0; VWi < wind_speeds.size(); VWi++) + CalculateVMG(VWi); + return true; } @@ -969,6 +973,7 @@ void Polar::AddWindSpeed(double tws) wind_speeds.insert(wind_speeds.begin()+VWi, SailingWindSpeed(tws)); for(unsigned int Wi = 0; Wi < degree_steps.size(); Wi++) wind_speeds[VWi].speeds.push_back(0); + CalculateVMG(VWi); } void Polar::RemoveWindSpeed(int index)