OpenCPN Partial API docs
Loading...
Searching...
No Matches
route_gui.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2022 by David Register *
3 * Copyright (C) 2022 Alec Leamas *
4 * Copyright (C) 2025 NoCodeHummel *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20 **************************************************************************/
21
27#include <string>
28
29#include <wx/colour.h>
30#include <wx/gdicmn.h>
31#include <wx/pen.h>
32#include <wx/string.h>
33#include <wx/utils.h>
34
35#include "dialog_alert.h"
36
37#include "color_handler.h"
38#include "chartbase.h"
39#include "model/comm_n0183_output.h"
40#include "model/georef.h"
41#include "gui_lib.h"
42#include "model/multiplexer.h"
43#include "n0183_ctx_factory.h"
44#include "navutil.h"
45#include "model/own_ship.h"
46#include "model/routeman.h"
47#include "route_gui.h"
48#include "route_point_gui.h"
49#include "glChartCanvas.h"
50#include "line_clip.h"
51#include "model/route.h"
52
53extern Routeman *g_pRouteMan;
54extern wxColour g_colourTrackLineColour;
55extern Multiplexer *g_pMUX;
56
57extern wxColor GetDimColor(wxColor c);
58extern bool g_bHighliteTracks;
59
60extern ocpnGLOptions g_GLOptions;
61
62extern int s_arrow_icon[];
63
64static void TestLongitude(double lon, double min, double max, bool &lonl,
65 bool &lonr) {
66 double clon = (min + max) / 2;
67 if (min - lon > 180) lon += 360;
68
69 lonl = lonr = false;
70 if (lon < min) {
71 if (lon < clon - 180)
72 lonr = true;
73 else
74 lonl = true;
75 } else if (lon > max) {
76 if (lon > clon + 180)
77 lonl = true;
78 else
79 lonr = true;
80 }
81}
82
83void RouteGui::Draw(ocpnDC &dc, ChartCanvas *canvas, const LLBBox &box) {
84 if (m_route.pRoutePointList->empty()) return;
85
86 ViewPort vp = canvas->GetVP();
87
88 LLBBox test_box = m_route.GetBBox();
89 if (box.IntersectOut(test_box)) // Route is wholly outside window
90 return;
91
92 int width = g_route_line_width;
93 if (m_route.m_width != WIDTH_UNDEFINED) width = m_route.m_width;
94
95 if (m_route.m_bVisible && m_route.m_bRtIsSelected) {
96 wxPen spen = *g_pRouteMan->GetSelectedRoutePen();
97 spen.SetWidth(width);
98 dc.SetPen(spen);
99 dc.SetBrush(*g_pRouteMan->GetSelectedRouteBrush());
100 } else if (m_route.m_bVisible) {
101 wxPenStyle style = wxPENSTYLE_SOLID;
102 wxColour col;
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();
106 } else {
107 for (unsigned int i = 0; i < sizeof(::GpxxColorNames) / sizeof(wxString);
108 i++) {
109 if (m_route.m_Colour == ::GpxxColorNames[i]) {
110 col = ::GpxxColors[i];
111 break;
112 }
113 }
114 }
115 dc.SetPen(*wxThePenList->FindOrCreatePen(col, width, style));
116 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(col, wxBRUSHSTYLE_SOLID));
117 }
118
119 if (m_route.m_bVisible && m_route.m_bRtIsActive) {
120 wxPen spen = *g_pRouteMan->GetActiveRoutePen();
121 spen.SetWidth(width);
122 dc.SetPen(spen);
123 dc.SetBrush(*g_pRouteMan->GetActiveRouteBrush());
124 }
125
126 wxPoint rpt1, rpt2;
127 if (m_route.m_bVisible) DrawPointWhich(dc, canvas, 1, &rpt1);
128
129 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
130 RoutePoint *prp1 = node->GetData();
131 node = node->GetNext();
132
133 if (m_route.m_bVisible || prp1->IsShared())
134 RoutePointGui(*prp1).Draw(dc, canvas,
135 NULL); // prp1->Draw(dc, canvas, NULL);
136
137 while (node) {
138 RoutePoint *prp2 = node->GetData();
139
140 bool draw_arrow = !(prp2->m_bIsActive && g_bAllowShipToActive);
141
142 if (m_route.m_bVisible || prp2->IsShared())
143 RoutePointGui(*prp2).Draw(dc, canvas,
144 &rpt2); // prp2->Draw(dc, canvas, &rpt2);
145
146 if (m_route.m_bVisible) {
147 // Handle offscreen points
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);
150
151 // Simple case
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); // with arrows
155
156 // In the cases where one point is on, and one off
157 // we must decide which way to go in longitude
158 // Arbitrarily, we will go the shortest way
159
160 double pix_full_circle = WGS84_semimajor_axis_meters * mercator_k0 * 2 *
161 PI * vp.view_scale_ppm;
162 double dp =
163 pow((double)(rpt1.x - rpt2.x), 2) + pow((double)(rpt1.y - rpt2.y), 2);
164 double dtest;
165 int adder;
166 if (b_1_on && !b_2_on) {
167 if (rpt2.x < rpt1.x)
168 adder = (int)pix_full_circle;
169 else
170 adder = -(int)pix_full_circle;
171
172 dtest = pow((double)(rpt1.x - (rpt2.x + adder)), 2) +
173 pow((double)(rpt1.y - rpt2.y), 2);
174
175 if (dp < dtest) adder = 0;
176
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) {
180 if (rpt1.x < rpt2.x)
181 adder = (int)pix_full_circle;
182 else
183 adder = -(int)pix_full_circle;
184
185 float rxd = rpt2.x - (rpt1.x + adder);
186 float ryd = rpt1.y - rpt2.y;
187 dtest = rxd * rxd + ryd * ryd;
188
189 if (dp < dtest) adder = 0;
190
191 RenderSegment(dc, rpt1.x + adder, rpt1.y, rpt2.x, rpt2.y, vp,
192 draw_arrow, m_route.m_hiliteWidth);
193 }
194 }
195
196 rpt1 = rpt2;
197 prp1 = prp2;
198
199 node = node->GetNext();
200 }
201}
202
203void RouteGui::RenderSegment(ocpnDC &dc, int xa, int ya, int xb, int yb,
204 ViewPort &vp, bool bdraw_arrow, int hilite_width) {
205 // Get the dc boundary
206 int sx, sy;
207 dc.GetSize(&sx, &sy);
208
209 // Try to exit early if the segment is nowhere near the screen
210 wxRect r(0, 0, sx, sy);
211 wxRect s(xa, ya, 1, 1);
212 wxRect t(xb, yb, 1, 1);
213 s.Union(t);
214 if (!r.Intersects(s)) return;
215
216 // Clip the line segment to the dc boundary
217 int x0 = xa;
218 int y0 = ya;
219 int x1 = xb;
220 int y1 = yb;
221
222 // If hilite is desired, use a Native Graphics context to render alpha
223 // colours That is, if wxGraphicsContext is available.....
224
225 if (hilite_width) {
226 if (Visible ==
227 cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, 0, sx, 0, sy)) {
228 wxPen psave = dc.GetPen();
229
230 wxColour y = GetGlobalColor(_T ( "YELO1" ));
231 wxColour hilt(y.Red(), y.Green(), y.Blue(), 128);
232
233 wxPen HiPen(hilt, hilite_width, wxPENSTYLE_SOLID);
234
235 dc.SetPen(HiPen);
236 dc.StrokeLine(x0, y0, x1, y1);
237
238 dc.SetPen(psave);
239 dc.StrokeLine(x0, y0, x1, y1);
240 }
241 } else {
242 if (Visible ==
243 cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, 0, sx, 0, sy))
244 dc.StrokeLine(x0, y0, x1, y1);
245 }
246
247 if (bdraw_arrow) {
248 // Draw a direction arrow
249
250 double theta = atan2((double)(yb - ya), (double)(xb - xa));
251 theta -= PI / 2.;
252
253 wxPoint icon[10];
254 double icon_scale_factor = 100 * vp.view_scale_ppm;
255 icon_scale_factor = fmin(icon_scale_factor, 1.5); // Sets the max size
256 icon_scale_factor = fmax(icon_scale_factor, .10);
257
258 // Get the absolute line length
259 // and constrain the arrow to be no more than xx% of the line length
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));
263
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;
267
268 for (int i = 0; i < 7; i++) {
269 int j = i * 2;
270 double pxa = (double)(s_arrow_icon[j]);
271 double pya = (double)(s_arrow_icon[j + 1]);
272
273 pya *= icon_scale_factor;
274 pxa *= icon_scale_factor;
275
276 double px = (pxa * sin(theta)) + (pya * cos(theta));
277 double py = (pya * sin(theta)) - (pxa * cos(theta));
278
279 icon[i].x = (int)(px) + xb;
280 icon[i].y = (int)(py) + yb;
281 }
282 wxPen savePen = dc.GetPen();
283 dc.SetPen(*wxTRANSPARENT_PEN);
284 dc.StrokePolygon(6, &icon[0], 0, 0);
285 dc.SetPen(savePen);
286 }
287}
288
289void RouteGui::RenderSegmentArrowsGL(ocpnDC &dc, int xa, int ya, int xb, int yb,
290 ViewPort &vp) {
291#ifdef ocpnUSE_GL
292 // Draw a direction arrow
293 float icon_scale_factor = 100 * vp.view_scale_ppm;
294 icon_scale_factor = fmin(icon_scale_factor, 1.5); // Sets the max size
295 icon_scale_factor = fmax(icon_scale_factor, .10);
296
297 // Get the absolute line length
298 // and constrain the arrow to be no more than xx% of the line length
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));
302
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;
306
307 float theta = atan2f((float)yb - ya, (float)xb - xa);
308 theta -= (float)PI;
309
310 // icon_scale_factor = 5;
311 wxPoint pts[3];
312 // 0
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];
319
320 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
321
322 // 1
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);
330
331 // 2
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);
339
340 // 3
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);
348
349#endif
350}
351
352void RouteGui::DrawPointWhich(ocpnDC &dc, ChartCanvas *canvas, int iPoint,
353 wxPoint *rpn) {
354 if (iPoint <= m_route.GetnPoints())
355 RoutePointGui(*m_route.GetPoint(iPoint)).Draw(dc, canvas, rpn);
356}
357
358void RouteGui::DrawSegment(ocpnDC &dc, ChartCanvas *canvas, wxPoint *rp1,
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());
364 else
365 dc.SetPen(*g_pRouteMan->GetRoutePen());
366
367 RenderSegment(dc, rp1->x, rp1->y, rp2->x, rp2->y, vp, bdraw_arrow);
368}
369
370void RouteGui::DrawGL(ViewPort &vp, ChartCanvas *canvas, ocpnDC &dc) {
371#ifdef ocpnUSE_GL
372 if (m_route.pRoutePointList->empty()) return;
373
374 if (!vp.GetBBox().IntersectOut(m_route.GetBBox()) && m_route.m_bVisible)
375 DrawGLRouteLines(vp, canvas, dc);
376
377 /* Route points */
378 for (wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst(); node;
379 node = node->GetNext()) {
380 RoutePoint *prp = node->GetData();
381 // Inflate the bounding box a bit to ensure full drawing in accelerated pan
382 // mode.
383 // TODO this is a little extravagant, assumming a mark is always a large
384 // fixed lat/lon extent.
385 // Maybe better to use the mark's drawn box, once it is known.
386 if (vp.GetBBox().ContainsMarge(prp->m_lat, prp->m_lon, .5)) {
387 if (m_route.m_bVisible || prp->IsShared())
388 RoutePointGui(*prp).DrawGL(vp, canvas, dc);
389 }
390 }
391#endif
392}
393
394void RouteGui::DrawGLRouteLines(ViewPort &vp, ChartCanvas *canvas, ocpnDC &dc) {
395#ifdef ocpnUSE_GL
396 // Hiliting first
397 // Being special case to draw something for a 1 point route....
398 if (m_route.m_hiliteWidth) {
399 wxColour y = GetGlobalColor(_T ( "YELO1" ));
400 wxColour hilt(y.Red(), y.Green(), y.Blue(), 128);
401
402 wxPen HiPen(hilt, m_route.m_hiliteWidth, wxPENSTYLE_SOLID);
403
404 dc.SetPen(HiPen);
405
406 DrawGLLines(vp, &dc, canvas);
407 }
408
409 /* determine color and width */
410 wxColour col;
411
412 int width = g_pRouteMan->GetRoutePen()->GetWidth(); // g_route_line_width;
413 if (m_route.m_width != wxPENSTYLE_INVALID) width = m_route.m_width;
414
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();
419 } else {
420 if (m_route.m_Colour == wxEmptyString) {
421 col = g_pRouteMan->GetRoutePen()->GetColour();
422 } else {
423 for (unsigned int i = 0; i < sizeof(::GpxxColorNames) / sizeof(wxString);
424 i++) {
425 if (m_route.m_Colour == ::GpxxColorNames[i]) {
426 col = ::GpxxColors[i];
427 break;
428 }
429 }
430 }
431 }
432
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]);
438 }
439 dc.SetPen(p);
440 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(col, wxBRUSHSTYLE_SOLID));
441
442 glLineWidth(wxMax(g_GLMinSymbolLineWidth, width));
443
444 dc.SetGLStipple();
445
446 DrawGLLines(vp, &dc, canvas);
447
448 glDisable(GL_LINE_STIPPLE);
449
450 /* direction arrows.. could probably be further optimized for opengl */
451 dc.SetPen(*wxThePenList->FindOrCreatePen(col, 1, wxPENSTYLE_SOLID));
452
453 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
454 wxPoint rpt1, rpt2;
455 while (node) {
456 RoutePoint *prp = node->GetData();
457 canvas->GetCanvasPointPix(prp->m_lat, prp->m_lon, &rpt2);
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);
461 }
462 rpt1 = rpt2;
463 node = node->GetNext();
464 }
465#endif
466}
467
468void RouteGui::DrawGLLines(ViewPort &vp, ocpnDC *dc, ChartCanvas *canvas) {
469#ifdef ocpnUSE_GL
470 float pix_full_circle =
471 WGS84_semimajor_axis_meters * mercator_k0 * 2 * PI * vp.view_scale_ppm;
472
473 bool r1valid = false;
474 wxPoint2DDouble r1;
475 wxPoint2DDouble lastpoint;
476
477 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
478 RoutePoint *prp2 = node->GetData();
479 canvas->GetDoubleCanvasPointPix(prp2->m_lat, prp2->m_lon, &lastpoint);
480
481 // single point.. make sure it shows up for highlighting
482 if (m_route.GetnPoints() == 1 && dc) {
483 canvas->GetDoubleCanvasPointPix(prp2->m_lat, prp2->m_lon, &r1);
484 dc->DrawLine(r1.m_x, r1.m_y, r1.m_x + 2, r1.m_y + 2);
485 return;
486 }
487
488 // Handle offscreen points
489 LLBBox bbox = vp.GetBBox();
490
491 // dc is passed for thicker highlighted lines (performance not very important)
492
493 for (node = node->GetNext(); node; node = node->GetNext()) {
494 RoutePoint *prp1 = prp2;
495 prp2 = node->GetData();
496
497 // Provisional, to properly set status of last point in route
498 prp2->m_pos_on_screen = false;
499 {
500 wxPoint2DDouble r2;
501 canvas->GetDoubleCanvasPointPix(prp2->m_lat, prp2->m_lon, &r2);
502 if (std::isnan(r2.m_x)) {
503 r1valid = false;
504 continue;
505 }
506
507 lastpoint = r2; // For active track segment to ownship
508
509 // don't need to perform calculations or render segment
510 // if both points are past any edge of the vp
511 // TODO: use these optimizations for dc mode
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)) {
517 r1valid = false;
518 prp1->m_pos_on_screen = false;
519 continue;
520 }
521
522 // Possible optimization, not usable if vp crosses IDL (180 E)
523 if (!vp.ContainsIDL()) {
524 bool lon1l, lon1r, lon2l, lon2r;
525 TestLongitude(prp1->m_lon, bbox.GetMinLon(), bbox.GetMaxLon(), lon1l,
526 lon1r);
527 TestLongitude(prp2->m_lon, bbox.GetMinLon(), bbox.GetMaxLon(), lon2l,
528 lon2r);
529 if ((lon1l && lon2l) || (lon1r && lon2r)) {
530 r1valid = false;
531 prp1->m_pos_on_screen = false;
532 continue;
533 }
534 }
535
536 if (!r1valid) {
537 canvas->GetDoubleCanvasPointPix(prp1->m_lat, prp1->m_lon, &r1);
538 if (std::isnan(r1.m_x)) continue;
539 }
540
541 // we must decide which way to go in longitude
542 // for projections which wrap, in this case, we will render two lines
543 // (one may often be off screen which would be nice to fix but complicate
544 // things here anyway, in some cases both points are on screen, but the
545 // route wraps to either side so two lines are needed to draw this
546 // properly
547
548 double adder = 0;
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;
552
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;
564 }
565
566 if (dc)
567 if (adder) {
568 float adderc = cos(vp.rotation) * adder,
569 adders = sin(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);
572 } else
573 dc->DrawLine(r1.m_x, r1.m_y, r2.m_x, r2.m_y);
574 else {
575 }
576
577 r1 = r2;
578 r1valid = true;
579 }
580 }
581
582#endif
583}
584
585void RouteGui::CalculateDCRect(wxDC &dc_route, ChartCanvas *canvas,
586 wxRect *prect) {
587 dc_route.ResetBoundingBox();
588 dc_route.DestroyClippingRegion();
589
590 wxRect update_rect;
591
592 // Draw the route in skeleton form on the dc
593 // That is, draw only the route points, assuming that the segements will
594 // always be fully contained within the resulting rectangle.
595 // Can we prove this?
596 if (m_route.m_bVisible) {
597 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
598 while (node) {
599 RoutePoint *prp2 = node->GetData();
600 bool blink_save = prp2->m_bBlink;
601 prp2->m_bBlink = false;
602 ocpnDC odc_route(dc_route);
603 odc_route.SetVP(canvas->GetVP());
604 RoutePointGui(*prp2).Draw(odc_route, canvas, NULL);
605 prp2->m_bBlink = blink_save;
606
607 wxRect r = prp2->CurrentRect_in_DC;
608 // allow for large hilite circles at segment ends
609 r.Inflate(m_route.m_hiliteWidth, m_route.m_hiliteWidth);
610
611 update_rect.Union(r);
612 node = node->GetNext();
613 }
614 }
615
616 *prect = update_rect;
617}
618
619int RouteGui::SendToGPS(const wxString &com_name, bool bsend_waypoints,
620 SendToGpsDlg *dialog) {
621 int result = 0;
622
623 N0183DlgCtx dlg_ctx = GetDialogCtx(dialog);
624 ::wxBeginBusyCursor();
625 result = SendRouteToGPS_N0183(&m_route, com_name, bsend_waypoints, *g_pMUX,
626 dlg_ctx);
627 ::wxEndBusyCursor();
628
629 wxString msg;
630 if (0 == result)
631 msg = _("Route Transmitted.");
632 else {
633 if (result == ERR_GARMIN_INITIALIZE)
634 msg = _("Error on Route Upload. Garmin GPS not connected");
635 else
636 msg = _("Error on Route Upload. Please check logfiles...");
637 }
638 OCPNMessageBox(NULL, msg, _("OpenCPN Info"), wxOK | wxICON_INFORMATION);
639
640 return (result == 0);
641}
642
643// Delete the route.
644bool RouteGui::OnDelete(wxWindow *parent, const int count) {
645 std::string title = _("Route Delete").ToStdString();
646 std::string action = _("Delete").ToStdString();
647 std::string msg;
648 if (count > 1) {
649 wxString str = wxString::Format(
650 _("Are you sure you want to delete %d routes?"), count);
651 msg = str.c_str();
652 } else {
653 msg = _("Are you sure you want to delete this route?").ToStdString();
654 }
655
656 AlertDialog *dialog = new AlertDialog(parent, title, action);
657 dialog->SetMessage(msg);
658 int result = dialog->ShowModal();
659 return result == wxID_YES;
660}
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.
Chart display canvas.
Definition chcanv.h:135
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
Definition chcanv.cpp:4518
void GetDoubleCanvasPointPix(double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision.
Definition chcanv.cpp:4463
Dialog for sending routes/waypoints to a GPS device.
Represents the view port for chart display in OpenCPN.
Definition viewport.h:84
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:199
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:209
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:194
Device context class that can use either wxDC or OpenGL for drawing.
Definition ocpndc.h:64
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.
Definition ocpndc.cpp:476
General purpose GUI support.