OpenCPN Partial API docs
Loading...
Searching...
No Matches
select.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2013 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <wx/list.h>
25#include <wx/gdicmn.h>
26
27#include "model/base_platform.h"
28#include "model/georef.h"
29#include "model/nav_object_database.h"
30#include "model/route.h"
31#include "model/select.h"
32#include "model/track.h"
33
34#include "vector2D.h"
35
38
39Select::Select() {
40 pSelectList = new SelectableItemList;
41 pixelRadius = g_BasePlatform->GetSelectRadiusPix();
42}
43
44Select::~Select() {
45 for (SelectItem *si : *pSelectList) delete si;
46 pSelectList->clear();
47 delete pSelectList;
48}
49
50bool Select::IsSelectableRoutePointValid(RoutePoint *pRoutePoint) {
51 SelectItem *pFindSel;
52
53 // Iterate on the select list
54 for (SelectItem *pFindSel : *pSelectList) {
55 if (pFindSel->m_seltype == SELTYPE_ROUTEPOINT &&
56 (RoutePoint *)pFindSel->m_pData1 == pRoutePoint)
57 return true;
58 }
59 return false;
60}
61
62bool Select::AddSelectableRoutePoint(float slat, float slon,
63 RoutePoint *pRoutePointAdd) {
64 SelectItem *pSelItem = new SelectItem;
65 pSelItem->m_slat = slat;
66 pSelItem->m_slon = slon;
67 pSelItem->m_seltype = SELTYPE_ROUTEPOINT;
68 pSelItem->m_bIsSelected = false;
69 pSelItem->m_pData1 = pRoutePointAdd;
70
71 if (pRoutePointAdd->m_bIsInLayer)
72 pSelectList->push_back(pSelItem);
73 else
74 pSelectList->insert(pSelectList->begin(), pSelItem);
75
76 pRoutePointAdd->SetSelectNode(pSelItem);
77
78 return true;
79}
80
81bool Select::AddSelectableRouteSegment(float slat1, float slon1, float slat2,
82 float slon2, RoutePoint *pRoutePointAdd1,
83 RoutePoint *pRoutePointAdd2,
84 Route *pRoute) {
85 SelectItem *pSelItem = new SelectItem;
86 pSelItem->m_slat = slat1;
87 pSelItem->m_slon = slon1;
88 pSelItem->m_slat2 = slat2;
89 pSelItem->m_slon2 = slon2;
90 pSelItem->m_seltype = SELTYPE_ROUTESEGMENT;
91 pSelItem->m_bIsSelected = false;
92 pSelItem->m_pData1 = pRoutePointAdd1;
93 pSelItem->m_pData2 = pRoutePointAdd2;
94 pSelItem->m_pData3 = pRoute;
95
96 if (pRoute->m_bIsInLayer)
97 pSelectList->push_back(pSelItem);
98 else
99 pSelectList->insert(pSelectList->begin(), pSelItem);
100
101 return true;
102}
103
104bool Select::DeleteAllSelectableRouteSegments(Route *pr) {
105 SelectItem *pFindSel;
106
107 // Iterate on the select list
108 auto node = pSelectList->begin();
109
110 while (node != pSelectList->end()) {
111 pFindSel = *node;
112 if (pFindSel->m_seltype == SELTYPE_ROUTESEGMENT &&
113 (Route *)pFindSel->m_pData3 == pr) {
114 delete pFindSel;
115 auto d = node;
116 ++node;
117 auto pos = std::find(pSelectList->begin(), pSelectList->end(), *d);
118 if (pos != pSelectList->end()) pSelectList->erase(pos);
119 } else {
120 ++node; // FIXME (leamas) proper erase idiom
121 }
122 }
123
124 return true;
125}
126
127bool Select::DeleteAllSelectableRoutePoints(Route *pr) {
128 SelectItem *pFindSel;
129
130 // Iterate on the select list
131 auto node = pSelectList->begin();
132
133 while (node != pSelectList->end()) {
134 pFindSel = *node;
135 if (pFindSel->m_seltype == SELTYPE_ROUTEPOINT) {
136 RoutePoint *ps = (RoutePoint *)pFindSel->m_pData1;
137 // inner loop iterates on the route's point list
138 bool is_restarted = false;
139 for (RoutePoint *prp : *pr->pRoutePointList) {
140 if (prp == ps) {
141 auto pos =
142 std::find(pSelectList->begin(), pSelectList->end(), pFindSel);
143 if (pos != pSelectList->end()) pSelectList->erase(pos);
144 delete pFindSel;
145 prp->SetSelectNode(nullptr);
146 node = pSelectList->begin();
147 is_restarted = true;
148 break;
149 }
150 }
151 if (!is_restarted) ++node; // FIXME (leamas) proper erase idiom
152 } else {
153 ++node;
154 }
155 }
156 return true;
157}
158
159bool Select::AddAllSelectableRoutePoints(Route *pr) {
160 if (pr->pRoutePointList->size()) {
161 for (RoutePoint *prp : *pr->pRoutePointList) {
162 AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
163 }
164 return true;
165 } else
166 return false;
167}
168
169bool Select::AddAllSelectableRouteSegments(Route *pr) {
170 wxPoint rpt, rptn;
171 float slat1, slon1, slat2, slon2;
172
173 if (pr->pRoutePointList->size()) {
174 auto it = pr->pRoutePointList->begin();
175 RoutePoint *prp0 = *it;
176 slat1 = prp0->m_lat;
177 slon1 = prp0->m_lon;
178 for (++it; it != pr->pRoutePointList->end(); ++it) {
179 RoutePoint *prp = *it;
180 slat2 = prp->m_lat;
181 slon2 = prp->m_lon;
182
183 AddSelectableRouteSegment(slat1, slon1, slat2, slon2, prp0, prp, pr);
184
185 slat1 = slat2;
186 slon1 = slon2;
187 prp0 = prp;
188 }
189 return true;
190 } else
191 return false;
192}
193
194bool Select::AddAllSelectableTrackSegments(Track *pr) {
195 wxPoint rpt, rptn;
196 float slat1, slon1, slat2, slon2;
197
198 if (pr->GetnPoints()) {
199 TrackPoint *prp0 = pr->GetPoint(0);
200 slat1 = prp0->m_lat;
201 slon1 = prp0->m_lon;
202
203 for (int i = 1; i < pr->GetnPoints(); i++) {
204 TrackPoint *prp = pr->GetPoint(i);
205 slat2 = prp->m_lat;
206 slon2 = prp->m_lon;
207
208 AddSelectableTrackSegment(slat1, slon1, slat2, slon2, prp0, prp, pr);
209
210 slat1 = slat2;
211 slon1 = slon2;
212 prp0 = prp;
213 }
214 return true;
215 } else
216 return false;
217}
218
219bool Select::UpdateSelectableRouteSegments(RoutePoint *prp) {
220 bool ret = false;
221 for (SelectItem *pFindSel : *pSelectList) {
222 if (pFindSel->m_seltype == SELTYPE_ROUTESEGMENT) {
223 if (pFindSel->m_pData1 == prp) {
224 pFindSel->m_slat = prp->m_lat;
225 pFindSel->m_slon = prp->m_lon;
226 ret = true;
227 } else if (pFindSel->m_pData2 == prp) {
228 pFindSel->m_slat2 = prp->m_lat;
229 pFindSel->m_slon2 = prp->m_lon;
230 ret = true;
231 }
232 }
233 }
234
235 return ret;
236}
237
238SelectItem *Select::AddSelectablePoint(float slat, float slon,
239 const void *pdata, int fseltype) {
240 SelectItem *pSelItem = new SelectItem;
241 if (pSelItem) {
242 pSelItem->m_slat = slat;
243 pSelItem->m_slon = slon;
244 pSelItem->m_seltype = fseltype;
245 pSelItem->m_bIsSelected = false;
246 pSelItem->m_pData1 = pdata;
247
248 pSelectList->push_back(pSelItem);
249 }
250
251 return pSelItem;
252}
253
254/*
255bool Select::DeleteAllPoints( void )
256{
257 pSelectList->DeleteContents( true );
258 pSelectList->Clear();
259 return true;
260}
261*/
262
263bool Select::DeleteSelectablePoint(void *pdata, int SeltypeToDelete) {
264 SelectItem *pFindSel;
265
266 if (NULL != pdata) {
267 // Iterate on the list
268 auto node = pSelectList->begin();
269
270 while (node != pSelectList->end()) {
271 pFindSel = *node;
272 if (pFindSel->m_seltype == SeltypeToDelete) {
273 if (pdata == pFindSel->m_pData1) {
274 auto pos =
275 std::find(pSelectList->begin(), pSelectList->end(), pFindSel);
276 if (pos != pSelectList->end()) pSelectList->erase(pos);
277 delete pFindSel;
278 if (SELTYPE_ROUTEPOINT == SeltypeToDelete) {
279 RoutePoint *prp = (RoutePoint *)pdata;
280 prp->SetSelectNode(NULL);
281 }
282
283 return true;
284 }
285 }
286 ++node;
287 }
288 }
289 return false;
290}
291
292bool Select::DeleteAllSelectableTypePoints(int SeltypeToDelete) {
293 SelectItem *pFindSel;
294
295 // Iterate on the list
296 auto node = pSelectList->begin();
297
298 while (node != pSelectList->end()) {
299 pFindSel = *node;
300 if (pFindSel->m_seltype == SeltypeToDelete) {
301 auto pos = std::find(pSelectList->begin(), pSelectList->end(), pFindSel);
302 if (pos != pSelectList->end()) pSelectList->erase(pos);
303 if (SELTYPE_ROUTEPOINT == SeltypeToDelete) {
304 RoutePoint *prp = (RoutePoint *)pFindSel->m_pData1;
305 prp->SetSelectNode(NULL);
306 }
307 delete pFindSel;
308 node = pSelectList->begin();
309 } else {
310 ++node;
311 }
312 }
313 return true;
314}
315
316bool Select::DeleteSelectableRoutePoint(RoutePoint *prp) {
317 if (!prp) return false;
318 auto *pFindSel = (SelectItem *)prp->GetSelectNode();
319 if (pFindSel) {
320 auto pos = std::find(pSelectList->begin(), pSelectList->end(), pFindSel);
321 if (pos != pSelectList->end()) pSelectList->erase(pos);
322 delete pFindSel;
323 prp->SetSelectNode(nullptr);
324 return true;
325 } else {
326 return DeleteSelectablePoint(prp, SELTYPE_ROUTEPOINT);
327 }
328}
329
330bool Select::ModifySelectablePoint(float lat, float lon, void *data,
331 int SeltypeToModify) {
332 SelectItem *pFindSel;
333
334 // Iterate on the list
335 for (SelectItem *pFindSel : *pSelectList) {
336 if (pFindSel->m_seltype == SeltypeToModify) {
337 if (data == pFindSel->m_pData1) {
338 pFindSel->m_slat = lat;
339 pFindSel->m_slon = lon;
340 return true;
341 }
342 }
343 }
344 return false;
345}
346
347bool Select::AddSelectableTrackSegment(float slat1, float slon1, float slat2,
348 float slon2, TrackPoint *pTrackPointAdd1,
349 TrackPoint *pTrackPointAdd2,
350 Track *pTrack) {
351 SelectItem *pSelItem = new SelectItem;
352 pSelItem->m_slat = slat1;
353 pSelItem->m_slon = slon1;
354 pSelItem->m_slat2 = slat2;
355 pSelItem->m_slon2 = slon2;
356 pSelItem->m_seltype = SELTYPE_TRACKSEGMENT;
357 pSelItem->m_bIsSelected = false;
358 pSelItem->m_pData1 = pTrackPointAdd1;
359 pSelItem->m_pData2 = pTrackPointAdd2;
360 pSelItem->m_pData3 = pTrack;
361
362 if (pTrack->m_bIsInLayer)
363 pSelectList->push_back(pSelItem);
364 else
365 pSelectList->insert(pSelectList->begin(), pSelItem);
366
367 return true;
368}
369
370bool Select::DeleteAllSelectableTrackSegments(Track *pt) {
371 SelectItem *pFindSel;
372
373 // Iterate on the select list
374 auto node = pSelectList->begin();
375
376 while (node != pSelectList->end()) {
377 pFindSel = *node;
378 if (pFindSel->m_seltype == SELTYPE_TRACKSEGMENT &&
379 (Track *)pFindSel->m_pData3 == pt) {
380 delete pFindSel;
381 auto d = node;
382 ++node;
383 auto pos = std::find(pSelectList->begin(), pSelectList->end(), *d);
384 if (pos != pSelectList->end()) pSelectList->erase(pos);
385 } else {
386 ++node;
387 }
388 }
389 return true;
390}
391
392bool Select::DeletePointSelectableTrackSegments(TrackPoint *pt) {
393 SelectItem *pFindSel;
394
395 // Iterate on the select list
396 auto node = pSelectList->begin();
397
398 while (node != pSelectList->end()) {
399 pFindSel = *node;
400 if (pFindSel->m_seltype == SELTYPE_TRACKSEGMENT &&
401 ((TrackPoint *)pFindSel->m_pData1 == pt ||
402 (TrackPoint *)pFindSel->m_pData2 == pt)) {
403 delete pFindSel;
404 auto d = node;
405 ++node;
406 auto pos = std::find(pSelectList->begin(), pSelectList->end(), *d);
407 if (pos != pSelectList->end()) pSelectList->erase(pos);
408 } else {
409 ++node;
410 }
411 }
412 return true;
413}
414
415bool Select::IsSegmentSelected(float a, float b, float c, float d, float slat,
416 float slon) {
417 double adder = 0.;
418
419 // Track segments for some reason can have longitude values > 180.
420 // Therefore, we normalize all the lat/lon values here.
421 if (a > 90.0) a -= 180.0;
422 if (b > 90.0) b -= 180.0;
423 if (c > 180.0) c -= 360.0;
424 if (d > 180.0) d -= 360.0;
425 if (slat > 90.0) slat -= 180.0;
426 if (slon > 180.0) slon -= 360.0;
427
428 if ((c * d) < 0.) {
429 // Arrange for points to be increasing longitude, c to d
430 double dist, brg;
431 DistanceBearingMercator(a, c, b, d, &brg, &dist);
432 if (brg < 180.) // swap points?
433 {
434 double tmp;
435 tmp = c;
436 c = d;
437 d = tmp;
438 tmp = a;
439 a = b;
440 b = tmp;
441 }
442 if (d < 0.) // idl?
443 {
444 d += 360.;
445 if (slon < 0.) adder = 360.;
446 }
447 }
448
449 // As a course test, use segment bounding box test
450 if ((slat >= (fmin(a, b) - selectRadius)) &&
451 (slat <= (fmax(a, b) + selectRadius)) &&
452 ((slon + adder) >= (fmin(c, d) - selectRadius)) &&
453 ((slon + adder) <= (fmax(c, d) + selectRadius))) {
454 // Use vectors to do hit test....
455 vector2D va, vb, vn;
456
457 // Assuming a Mercator projection
458 double ap, cp;
459 toSM(a, c, 0., 0., &cp, &ap);
460 double bp, dp;
461 toSM(b, d, 0., 0., &dp, &bp);
462 double slatp, slonp;
463 toSM(slat, slon + adder, 0., 0., &slonp, &slatp);
464
465 va.x = slonp - cp;
466 va.y = slatp - ap;
467 vb.x = dp - cp;
468 vb.y = bp - ap;
469
470 double delta = vGetLengthOfNormal(&va, &vb, &vn);
471 if (fabs(delta) < (selectRadius * 1852 * 60)) return true;
472 }
473 return false;
474}
475
476void Select::CalcSelectRadius(SelectCtx &ctx) {
477 selectRadius = pixelRadius / (ctx.scale * 1852 * 60);
478}
479
480SelectItem *Select::FindSelection(SelectCtx &ctx, float slat, float slon,
481 int fseltype) {
482 float a, b, c, d;
483 SelectItem *pFindSel;
484
485 CalcSelectRadius(ctx);
486
487 // Iterate on the list
488 for (SelectItem *si : *pSelectList) {
489 pFindSel = si;
490 if (pFindSel->m_seltype == fseltype) {
491 switch (fseltype) {
492 case SELTYPE_ROUTEPOINT:
493 case SELTYPE_TIDEPOINT:
494 case SELTYPE_CURRENTPOINT:
495 case SELTYPE_AISTARGET:
496 if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
497 (fabs(slon - pFindSel->m_slon) < selectRadius)) {
498 if (fseltype == SELTYPE_ROUTEPOINT) {
499 if (((RoutePoint *)pFindSel->m_pData1)
500 ->IsVisibleSelectable(ctx.chart_scale))
501 goto find_ok;
502 } else {
503 goto find_ok;
504 }
505 }
506 break;
507 case SELTYPE_ROUTESEGMENT:
508 case SELTYPE_TRACKSEGMENT: {
509 a = pFindSel->m_slat;
510 b = pFindSel->m_slat2;
511 c = pFindSel->m_slon;
512 d = pFindSel->m_slon2;
513
514 if (IsSegmentSelected(a, b, c, d, slat, slon)) goto find_ok;
515 break;
516 }
517 default:
518 break;
519 }
520 }
521 }
522
523 return NULL;
524find_ok:
525 return pFindSel;
526}
527
528bool Select::IsSelectableSegmentSelected(SelectCtx &ctx, float slat, float slon,
529 SelectItem *pFindSel) {
530 bool valid = false;
531 for (SelectItem *si : *pSelectList) {
532 if (pFindSel == si) {
533 valid = true;
534 break;
535 }
536 }
537
538 if (valid == false) {
539 // not in the list anymore
540 return false;
541 }
542 CalcSelectRadius(ctx);
543
544 float a = pFindSel->m_slat;
545 float b = pFindSel->m_slat2;
546 float c = pFindSel->m_slon;
547 float d = pFindSel->m_slon2;
548
549 return IsSegmentSelected(a, b, c, d, slat, slon);
550}
551
552static bool is_selectable_wp(SelectCtx ctx, RoutePoint *wp) {
553 if (ctx.show_nav_objects) return true;
554
555 if (wp->m_bIsActive) return true;
556
557 Route *rte;
558 rte = FindRouteContainingWaypoint(wp);
559 if (rte && rte->IsActive()) return true;
560
561 return false;
562}
563
564SelectableItemList Select::FindSelectionList(SelectCtx &ctx, float slat,
565 float slon, int fseltype) {
566 float a, b, c, d;
567 SelectItem *pFindSel;
568 SelectableItemList ret_list;
569
570 CalcSelectRadius(ctx);
571
572 // Iterate on the list
573 for (SelectItem *si : *pSelectList) {
574 pFindSel = si;
575 if (pFindSel->m_seltype == fseltype) {
576 switch (fseltype) {
577 case SELTYPE_ROUTEPOINT:
578 if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
579 (fabs(slon - pFindSel->m_slon) < selectRadius))
580 if (is_selectable_wp(ctx, (RoutePoint *)pFindSel->m_pData1))
581 if (((RoutePoint *)pFindSel->m_pData1)
582 ->IsVisibleSelectable(ctx.chart_scale))
583 ret_list.push_back(pFindSel);
584 break;
585 case SELTYPE_TIDEPOINT:
586 case SELTYPE_CURRENTPOINT:
587 case SELTYPE_AISTARGET:
588 case SELTYPE_DRAGHANDLE:
589 if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
590 (fabs(slon - pFindSel->m_slon) < selectRadius)) {
591 if (is_selectable_wp(ctx, (RoutePoint *)pFindSel->m_pData1))
592 ret_list.push_back(pFindSel);
593 }
594 break;
595 case SELTYPE_ROUTESEGMENT:
596 case SELTYPE_TRACKSEGMENT: {
597 a = pFindSel->m_slat;
598 b = pFindSel->m_slat2;
599 c = pFindSel->m_slon;
600 d = pFindSel->m_slon2;
601
602 if (IsSegmentSelected(a, b, c, d, slat, slon)) {
603 if (ctx.show_nav_objects ||
604 (fseltype == SELTYPE_ROUTESEGMENT &&
605 ((Route *)pFindSel->m_pData3)->m_bRtIsActive)) {
606 ret_list.push_back(pFindSel);
607 }
608 }
609
610 break;
611 }
612 default:
613 break;
614 }
615 }
616 }
617
618 return ret_list;
619}
BasePlatform * g_BasePlatform
points to g_platform, handles brain-dead MS linker.
Basic platform specific support utilities without GUI deps.
Represents a waypoint or mark within the navigation system.
Definition route_point.h:71
bool m_bIsActive
Flag indicating if this waypoint is active for navigation.
bool m_bIsInLayer
Flag indicating if the waypoint belongs to a layer.
Represents a navigational route in the navigation system.
Definition route.h:99
RoutePointList * pRoutePointList
Ordered list of waypoints (RoutePoints) that make up this route.
Definition route.h:336
bool m_bIsInLayer
Flag indicating whether this route belongs to a layer.
Definition route.h:278
Represents a single point in a track.
Definition track.h:59
Represents a track, which is a series of connected track points.
Definition track.h:117
OpenCPN Georef utility.
Route abstraction.
Select * pSelect
Global instance.
Definition select.cpp:36
Select * pSelectTC
Global instance.
Definition select.cpp:37
Selected route, segment, waypoint, etc.
Recorded track abstraction.