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