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