35#include "dialog_alert.h"
37#include "color_handler.h"
39#include "model/comm_n0183_output.h"
40#include "model/georef.h"
42#include "model/multiplexer.h"
43#include "n0183_ctx_factory.h"
45#include "model/own_ship.h"
46#include "model/routeman.h"
48#include "route_point_gui.h"
49#include "glChartCanvas.h"
51#include "model/route.h"
54extern wxColour g_colourTrackLineColour;
57extern wxColor GetDimColor(wxColor c);
58extern bool g_bHighliteTracks;
62extern int s_arrow_icon[];
64static void TestLongitude(
double lon,
double min,
double max,
bool &lonl,
66 double clon = (min + max) / 2;
67 if (min - lon > 180) lon += 360;
75 }
else if (lon > max) {
84 if (m_route.pRoutePointList->empty())
return;
88 LLBBox test_box = m_route.GetBBox();
89 if (box.IntersectOut(test_box))
92 int width = g_route_line_width;
93 if (m_route.m_width != WIDTH_UNDEFINED) width = m_route.m_width;
95 if (m_route.m_bVisible && m_route.m_bRtIsSelected) {
96 wxPen spen = *g_pRouteMan->GetSelectedRoutePen();
99 dc.SetBrush(*g_pRouteMan->GetSelectedRouteBrush());
100 }
else if (m_route.m_bVisible) {
101 wxPenStyle style = wxPENSTYLE_SOLID;
103 if (m_route.m_style != wxPENSTYLE_INVALID) style = m_route.m_style;
104 if (m_route.m_Colour == wxEmptyString) {
105 col = g_pRouteMan->GetRoutePen()->GetColour();
107 for (
unsigned int i = 0; i <
sizeof(::GpxxColorNames) /
sizeof(wxString);
109 if (m_route.m_Colour == ::GpxxColorNames[i]) {
110 col = ::GpxxColors[i];
115 dc.SetPen(*wxThePenList->FindOrCreatePen(col, width, style));
116 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(col, wxBRUSHSTYLE_SOLID));
119 if (m_route.m_bVisible && m_route.m_bRtIsActive) {
120 wxPen spen = *g_pRouteMan->GetActiveRoutePen();
121 spen.SetWidth(width);
123 dc.SetBrush(*g_pRouteMan->GetActiveRouteBrush());
127 if (m_route.m_bVisible) DrawPointWhich(dc, canvas, 1, &rpt1);
129 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
131 node = node->GetNext();
133 if (m_route.m_bVisible || prp1->IsShared())
140 bool draw_arrow = !(prp2->m_bIsActive && g_bAllowShipToActive);
142 if (m_route.m_bVisible || prp2->IsShared())
146 if (m_route.m_bVisible) {
148 bool b_2_on = vp.GetBBox().Contains(prp2->m_lat, prp2->m_lon);
149 bool b_1_on = vp.GetBBox().Contains(prp1->m_lat, prp1->m_lon);
152 if (b_1_on && b_2_on)
153 RenderSegment(dc, rpt1.x, rpt1.y, rpt2.x, rpt2.y, vp, draw_arrow,
154 m_route.m_hiliteWidth);
160 double pix_full_circle = WGS84_semimajor_axis_meters * mercator_k0 * 2 *
163 pow((
double)(rpt1.x - rpt2.x), 2) + pow((
double)(rpt1.y - rpt2.y), 2);
166 if (b_1_on && !b_2_on) {
168 adder = (int)pix_full_circle;
170 adder = -(int)pix_full_circle;
172 dtest = pow((
double)(rpt1.x - (rpt2.x + adder)), 2) +
173 pow((
double)(rpt1.y - rpt2.y), 2);
175 if (dp < dtest) adder = 0;
177 RenderSegment(dc, rpt1.x, rpt1.y, rpt2.x + adder, rpt2.y, vp,
178 draw_arrow, m_route.m_hiliteWidth);
179 }
else if (!b_1_on) {
181 adder = (int)pix_full_circle;
183 adder = -(int)pix_full_circle;
185 float rxd = rpt2.x - (rpt1.x + adder);
186 float ryd = rpt1.y - rpt2.y;
187 dtest = rxd * rxd + ryd * ryd;
189 if (dp < dtest) adder = 0;
191 RenderSegment(dc, rpt1.x + adder, rpt1.y, rpt2.x, rpt2.y, vp,
192 draw_arrow, m_route.m_hiliteWidth);
199 node = node->GetNext();
203void RouteGui::RenderSegment(
ocpnDC &dc,
int xa,
int ya,
int xb,
int yb,
204 ViewPort &vp,
bool bdraw_arrow,
int hilite_width) {
207 dc.GetSize(&sx, &sy);
210 wxRect r(0, 0, sx, sy);
211 wxRect s(xa, ya, 1, 1);
212 wxRect t(xb, yb, 1, 1);
214 if (!r.Intersects(s))
return;
227 cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, 0, sx, 0, sy)) {
228 wxPen psave = dc.GetPen();
230 wxColour y = GetGlobalColor(_T (
"YELO1" ));
231 wxColour hilt(y.Red(), y.Green(), y.Blue(), 128);
233 wxPen HiPen(hilt, hilite_width, wxPENSTYLE_SOLID);
236 dc.StrokeLine(x0, y0, x1, y1);
239 dc.StrokeLine(x0, y0, x1, y1);
243 cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, 0, sx, 0, sy))
244 dc.StrokeLine(x0, y0, x1, y1);
250 double theta = atan2((
double)(yb - ya), (
double)(xb - xa));
255 icon_scale_factor = fmin(icon_scale_factor, 1.5);
256 icon_scale_factor = fmax(icon_scale_factor, .10);
260 double nom_arrow_size = 20.;
261 double max_arrow_to_leg = .20;
262 double lpp = sqrt(pow((
double)(xa - xb), 2) + pow((
double)(ya - yb), 2));
264 double icon_size = icon_scale_factor * nom_arrow_size;
265 if (icon_size > (lpp * max_arrow_to_leg))
266 icon_scale_factor = (lpp * max_arrow_to_leg) / nom_arrow_size;
268 for (
int i = 0; i < 7; i++) {
270 double pxa = (double)(s_arrow_icon[j]);
271 double pya = (double)(s_arrow_icon[j + 1]);
273 pya *= icon_scale_factor;
274 pxa *= icon_scale_factor;
276 double px = (pxa * sin(theta)) + (pya * cos(theta));
277 double py = (pya * sin(theta)) - (pxa * cos(theta));
279 icon[i].x = (int)(px) + xb;
280 icon[i].y = (int)(py) + yb;
282 wxPen savePen = dc.GetPen();
283 dc.SetPen(*wxTRANSPARENT_PEN);
284 dc.StrokePolygon(6, &icon[0], 0, 0);
289void RouteGui::RenderSegmentArrowsGL(
ocpnDC &dc,
int xa,
int ya,
int xb,
int yb,
294 icon_scale_factor = fmin(icon_scale_factor, 1.5);
295 icon_scale_factor = fmax(icon_scale_factor, .10);
299 float nom_arrow_size = 20.;
300 float max_arrow_to_leg = (float).20;
301 float lpp = sqrtf(powf((
float)(xa - xb), 2) + powf((
float)(ya - yb), 2));
303 float icon_size = icon_scale_factor * nom_arrow_size;
304 if (icon_size > (lpp * max_arrow_to_leg))
305 icon_scale_factor = (lpp * max_arrow_to_leg) / nom_arrow_size;
307 float theta = atan2f((
float)yb - ya, (
float)xb - xa);
313 pts[0].x = s_arrow_icon[0];
314 pts[0].y = s_arrow_icon[1];
315 pts[1].x = s_arrow_icon[2];
316 pts[1].y = s_arrow_icon[3];
317 pts[2].x = s_arrow_icon[6];
318 pts[2].y = s_arrow_icon[7];
320 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
323 pts[0].x = s_arrow_icon[2];
324 pts[0].y = s_arrow_icon[3];
325 pts[1].x = s_arrow_icon[4];
326 pts[1].y = s_arrow_icon[5];
327 pts[2].x = s_arrow_icon[6];
328 pts[2].y = s_arrow_icon[7];
329 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
332 pts[0].x = s_arrow_icon[0];
333 pts[0].y = -s_arrow_icon[1];
334 pts[1].x = s_arrow_icon[2];
335 pts[1].y = -s_arrow_icon[3];
336 pts[2].x = s_arrow_icon[6];
337 pts[2].y = -s_arrow_icon[7];
338 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
341 pts[0].x = s_arrow_icon[2];
342 pts[0].y = -s_arrow_icon[3];
343 pts[1].x = s_arrow_icon[4];
344 pts[1].y = -s_arrow_icon[5];
345 pts[2].x = s_arrow_icon[6];
346 pts[2].y = -s_arrow_icon[7];
347 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
354 if (iPoint <= m_route.GetnPoints())
355 RoutePointGui(*m_route.GetPoint(iPoint)).Draw(dc, canvas, rpn);
359 wxPoint *rp2,
ViewPort &vp,
bool bdraw_arrow) {
360 if (m_route.m_bRtIsSelected)
361 dc.SetPen(*g_pRouteMan->GetSelectedRoutePen());
362 else if (m_route.m_bRtIsActive)
363 dc.SetPen(*g_pRouteMan->GetActiveRoutePen());
365 dc.SetPen(*g_pRouteMan->GetRoutePen());
367 RenderSegment(dc, rp1->x, rp1->y, rp2->x, rp2->y, vp, bdraw_arrow);
372 if (m_route.pRoutePointList->empty())
return;
374 if (!vp.GetBBox().IntersectOut(m_route.GetBBox()) && m_route.m_bVisible)
375 DrawGLRouteLines(vp, canvas, dc);
378 for (wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst(); node;
379 node = node->GetNext()) {
386 if (vp.GetBBox().ContainsMarge(prp->m_lat, prp->m_lon, .5)) {
387 if (m_route.m_bVisible || prp->IsShared())
398 if (m_route.m_hiliteWidth) {
399 wxColour y = GetGlobalColor(_T (
"YELO1" ));
400 wxColour hilt(y.Red(), y.Green(), y.Blue(), 128);
402 wxPen HiPen(hilt, m_route.m_hiliteWidth, wxPENSTYLE_SOLID);
406 DrawGLLines(vp, &dc, canvas);
412 int width = g_pRouteMan->GetRoutePen()->GetWidth();
413 if (m_route.m_width != wxPENSTYLE_INVALID) width = m_route.m_width;
415 if (m_route.m_bRtIsActive) {
416 col = g_pRouteMan->GetActiveRoutePen()->GetColour();
417 }
else if (m_route.m_bRtIsSelected) {
418 col = g_pRouteMan->GetSelectedRoutePen()->GetColour();
420 if (m_route.m_Colour == wxEmptyString) {
421 col = g_pRouteMan->GetRoutePen()->GetColour();
423 for (
unsigned int i = 0; i <
sizeof(::GpxxColorNames) /
sizeof(wxString);
425 if (m_route.m_Colour == ::GpxxColorNames[i]) {
426 col = ::GpxxColors[i];
433 wxPenStyle style = wxPENSTYLE_SOLID;
434 if (m_route.m_style != wxPENSTYLE_INVALID) style = m_route.m_style;
435 wxPen p = *wxThePenList->FindOrCreatePen(col, width, style);
436 if (glChartCanvas::dash_map.find(style) != glChartCanvas::dash_map.end()) {
437 p.SetDashes(2, &glChartCanvas::dash_map[style][0]);
440 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(col, wxBRUSHSTYLE_SOLID));
442 glLineWidth(wxMax(g_GLMinSymbolLineWidth, width));
446 DrawGLLines(vp, &dc, canvas);
448 glDisable(GL_LINE_STIPPLE);
451 dc.SetPen(*wxThePenList->FindOrCreatePen(col, 1, wxPENSTYLE_SOLID));
453 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
458 if (node != m_route.pRoutePointList->GetFirst()) {
459 if (!prp->m_bIsActive || !g_bAllowShipToActive)
460 RenderSegmentArrowsGL(dc, rpt1.x, rpt1.y, rpt2.x, rpt2.y, vp);
463 node = node->GetNext();
470 float pix_full_circle =
471 WGS84_semimajor_axis_meters * mercator_k0 * 2 * PI * vp.
view_scale_ppm;
473 bool r1valid =
false;
475 wxPoint2DDouble lastpoint;
477 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
482 if (m_route.GetnPoints() == 1 && dc) {
484 dc->
DrawLine(r1.m_x, r1.m_y, r1.m_x + 2, r1.m_y + 2);
489 LLBBox bbox = vp.GetBBox();
493 for (node = node->GetNext(); node; node = node->GetNext()) {
495 prp2 = node->GetData();
498 prp2->m_pos_on_screen =
false;
502 if (std::isnan(r2.m_x)) {
512 bool lat1l = prp1->m_lat < bbox.GetMinLat(),
513 lat2l = prp2->m_lat < bbox.GetMinLat();
514 bool lat1r = prp1->m_lat > bbox.GetMaxLat(),
515 lat2r = prp2->m_lat > bbox.GetMaxLat();
516 if ((lat1l && lat2l) || (lat1r && lat2r)) {
518 prp1->m_pos_on_screen =
false;
523 if (!vp.ContainsIDL()) {
524 bool lon1l, lon1r, lon2l, lon2r;
525 TestLongitude(prp1->m_lon, bbox.GetMinLon(), bbox.GetMaxLon(), lon1l,
527 TestLongitude(prp2->m_lon, bbox.GetMinLon(), bbox.GetMaxLon(), lon2l,
529 if ((lon1l && lon2l) || (lon1r && lon2r)) {
531 prp1->m_pos_on_screen =
false;
538 if (std::isnan(r1.m_x))
continue;
549 if ((vp.m_projection_type == PROJECTION_MERCATOR ||
550 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR)) {
551 float olon = vp.
clon > 0 ? vp.
clon - 180 : vp.
clon + 180;
553 if (prp1->m_lon < prp2->m_lon) {
554 if (prp2->m_lon - prp1->m_lon < 180) {
555 if (olon > prp1->m_lon && olon < prp2->m_lon)
556 adder = pix_full_circle;
557 }
else if (olon < prp1->m_lon || olon > prp2->m_lon)
558 adder = -pix_full_circle;
559 }
else if (prp1->m_lon - prp2->m_lon < 180) {
560 if (olon < prp1->m_lon && olon > prp2->m_lon)
561 adder = -pix_full_circle;
562 }
else if (olon > prp1->m_lon || olon < prp2->m_lon)
563 adder = pix_full_circle;
568 float adderc = cos(vp.
rotation) * adder,
570 dc->
DrawLine(r1.m_x, r1.m_y, r2.m_x + adderc, r2.m_y + adders);
571 dc->
DrawLine(r1.m_x - adderc, r1.m_y - adders, r2.m_x, r2.m_y);
573 dc->
DrawLine(r1.m_x, r1.m_y, r2.m_x, r2.m_y);
585void RouteGui::CalculateDCRect(wxDC &dc_route,
ChartCanvas *canvas,
587 dc_route.ResetBoundingBox();
588 dc_route.DestroyClippingRegion();
596 if (m_route.m_bVisible) {
597 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
600 bool blink_save = prp2->m_bBlink;
601 prp2->m_bBlink =
false;
602 ocpnDC odc_route(dc_route);
603 odc_route.SetVP(canvas->GetVP());
605 prp2->m_bBlink = blink_save;
607 wxRect r = prp2->CurrentRect_in_DC;
609 r.Inflate(m_route.m_hiliteWidth, m_route.m_hiliteWidth);
611 update_rect.Union(r);
612 node = node->GetNext();
616 *prect = update_rect;
619int RouteGui::SendToGPS(
const wxString &com_name,
bool bsend_waypoints,
624 ::wxBeginBusyCursor();
625 result = SendRouteToGPS_N0183(&m_route, com_name, bsend_waypoints, *g_pMUX,
631 msg = _(
"Route Transmitted.");
633 if (result == ERR_GARMIN_INITIALIZE)
634 msg = _(
"Error on Route Upload. Garmin GPS not connected");
636 msg = _(
"Error on Route Upload. Please check logfiles...");
638 OCPNMessageBox(NULL, msg, _(
"OpenCPN Info"), wxOK | wxICON_INFORMATION);
640 return (result == 0);
644bool RouteGui::OnDelete(wxWindow *parent,
const int count) {
645 std::string title = _(
"Route Delete").ToStdString();
646 std::string action = _(
"Delete").ToStdString();
649 wxString str = wxString::Format(
650 _(
"Are you sure you want to delete %d routes?"), count);
653 msg = _(
"Are you sure you want to delete this route?").ToStdString();
659 return result == wxID_YES;
A modal message dialog with a cancel and confirmation button.
void SetMessage(const std::string &msg)
Set alert message.
int ShowModal() override
Show dialog and return response.
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
void GetDoubleCanvasPointPix(double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision.
Dialog for sending routes/waypoints to a GPS device.
Represents the view port for chart display in OpenCPN.
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
double rotation
Rotation angle of the viewport in radians.
double clon
Center longitude of the viewport in degrees.
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.
General purpose GUI support.