]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/waypoint.cxx
Trying to bullet-proof the traffic code.
[flightgear.git] / src / Navaids / waypoint.cxx
1 // waypoint.cxx - waypoints that can occur in routes/procedures
2 // Written by James Turner, started 2009.
3 //
4 // Copyright (C) 2009  Curtis L. Olson
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // 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 Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "waypoint.hxx"
25
26 #include <simgear/structure/exception.hxx>
27
28 #include <Airports/airport.hxx>
29 #include <Airports/runways.hxx>
30 #include <Navaids/airways.hxx>
31
32 using std::string;
33
34 namespace flightgear
35 {
36
37 BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) :
38   Waypt(aOwner),
39   _pos(aPos),
40   _ident(aIdent)
41 {
42 }
43
44 BasicWaypt::BasicWaypt(RouteBase* aOwner) :
45   Waypt(aOwner)
46 {
47 }
48
49 void BasicWaypt::initFromProperties(SGPropertyNode_ptr aProp)
50 {
51   if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
52     throw sg_io_exception("missing lon/lat properties", 
53       "BasicWaypt::initFromProperties");
54   }
55
56   Waypt::initFromProperties(aProp);
57
58   _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), 
59     aProp->getDoubleValue("lat"));
60   _ident = aProp->getStringValue("ident");
61 }
62
63 void BasicWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
64 {
65   Waypt::writeToProperties(aProp);
66   
67   aProp->setStringValue("ident", _ident);
68   aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
69   aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
70 }
71
72 //////////////////////////////////////////////////////////////////////////////
73
74 NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner) :
75   Waypt(aOwner),
76   _navaid(aPos)
77 {
78   if (aPos->type() == FGPositioned::RUNWAY) {
79       SG_LOG(SG_NAVAID, SG_WARN, "sure you don't want to be building a runway waypt here?");
80   }
81 }
82
83 NavaidWaypoint::NavaidWaypoint(RouteBase* aOwner) :
84   Waypt(aOwner)
85 {
86 }
87
88
89 SGGeod NavaidWaypoint::position() const
90 {
91   return SGGeod::fromGeodFt(_navaid->geod(), _altitudeFt);
92 }  
93  
94 std::string NavaidWaypoint::ident() const
95 {
96   return _navaid->ident();
97 }
98
99 void NavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
100 {
101   if (!aProp->hasChild("ident")) {
102     throw sg_io_exception("missing navaid value", 
103       "NavaidWaypoint::initFromProperties");
104   }
105   
106   Waypt::initFromProperties(aProp);
107   
108   std::string idn(aProp->getStringValue("ident"));
109   SGGeod p;
110   if (aProp->hasChild("lon")) {
111     p = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
112   }
113   
114   // FIXME - resolve co-located DME, etc
115   // is it sufficent just to ignore DMEs, actually?
116   FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
117   if (!nav) {
118     throw sg_io_exception("unknown navaid ident:" + idn, 
119       "NavaidWaypoint::initFromProperties");
120   }
121   
122   _navaid = nav;
123 }
124
125 void NavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
126 {
127   Waypt::writeToProperties(aProp);
128   
129   aProp->setStringValue("ident", _navaid->ident());
130   // write lon/lat to disambiguate
131   aProp->setDoubleValue("lon", _navaid->geod().getLongitudeDeg());
132   aProp->setDoubleValue("lat", _navaid->geod().getLatitudeDeg());
133 }
134
135 OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner,
136   double aRadial, double aDistNm) :
137   NavaidWaypoint(aPos, aOwner),
138   _radial(aRadial),
139   _distanceNm(aDistNm)
140 {
141   init();
142 }
143
144 OffsetNavaidWaypoint::OffsetNavaidWaypoint(RouteBase* aOwner) :
145   NavaidWaypoint(aOwner)
146 {
147 }
148
149 void OffsetNavaidWaypoint::init()
150 {
151   SGGeod offset;
152   double az2;
153   SGGeodesy::direct(_navaid->geod(), _radial, _distanceNm * SG_NM_TO_METER, offset, az2);
154   _geod = SGGeod::fromGeodFt(offset, _altitudeFt);
155 }
156
157 void OffsetNavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
158 {
159   if (!aProp->hasChild("radial-deg") || !aProp->hasChild("distance-nm")) {
160     throw sg_io_exception("missing radial/offset distance",
161       "OffsetNavaidWaypoint::initFromProperties");
162   }
163   
164   NavaidWaypoint::initFromProperties(aProp);
165   _radial = aProp->getDoubleValue("radial-deg");
166   _distanceNm = aProp->getDoubleValue("distance-nm");
167   init();
168 }
169
170 void OffsetNavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
171 {
172   NavaidWaypoint::writeToProperties(aProp);
173   aProp->setDoubleValue("radial-deg", _radial);
174   aProp->setDoubleValue("distance-nm", _distanceNm);
175 }
176
177 /////////////////////////////////////////////////////////////////////////////
178
179 RunwayWaypt::RunwayWaypt(FGRunway* aPos, RouteBase* aOwner) :
180   Waypt(aOwner),
181   _runway(aPos)
182 {
183 }
184
185 RunwayWaypt::RunwayWaypt(RouteBase* aOwner) :
186   Waypt(aOwner)
187 {
188 }
189
190 SGGeod RunwayWaypt::position() const
191 {
192   return _runway->threshold();
193 }
194   
195 std::string RunwayWaypt::ident() const
196 {
197   return _runway->airport()->ident() + "-" + _runway->ident();
198 }
199
200 FGPositioned* RunwayWaypt::source() const
201 {
202   return _runway;
203 }
204   
205 double RunwayWaypt::headingRadialDeg() const
206 {
207   return _runway->headingDeg();
208 }
209
210 void RunwayWaypt::initFromProperties(SGPropertyNode_ptr aProp)
211 {
212   if (!aProp->hasChild("icao") || !aProp->hasChild("ident")) {
213     throw sg_io_exception("missing values: icao or ident", 
214       "RunwayWaypoint::initFromProperties");
215   }
216   
217   Waypt::initFromProperties(aProp);
218   std::string idn(aProp->getStringValue("ident"));
219   const FGAirport* apt = FGAirport::getByIdent(aProp->getStringValue("icao"));
220   _runway = apt->getRunwayByIdent(aProp->getStringValue("ident"));
221 }
222
223 void RunwayWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
224 {
225   Waypt::writeToProperties(aProp);
226   aProp->setStringValue("ident", _runway->ident());
227   aProp->setStringValue("icao", _runway->airport()->ident());
228 }
229
230 /////////////////////////////////////////////////////////////////////////////
231
232 Hold::Hold(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) :
233   BasicWaypt(aPos, aIdent, aOwner),
234   _righthanded(true),
235   _isDistance(false)
236 {
237   setFlag(WPT_DYNAMIC);
238 }
239
240 Hold::Hold(RouteBase* aOwner) :
241   BasicWaypt(aOwner),
242   _righthanded(true),
243   _isDistance(false)
244 {
245 }
246
247 void Hold::setHoldRadial(double aInboundRadial)
248 {
249   _bearing = aInboundRadial;
250 }
251
252 void Hold::setHoldDistance(double aDistanceNm)
253 {
254   _isDistance = true;
255   _holdTD = aDistanceNm;
256 }
257
258 void Hold::setHoldTime(double aTimeSec)
259 {
260   _isDistance = false;
261   _holdTD = aTimeSec;
262 }
263
264 void Hold::setRightHanded()
265 {
266   _righthanded = true;
267 }
268
269 void Hold::setLeftHanded()
270 {
271   _righthanded = false;
272 }
273   
274 void Hold::initFromProperties(SGPropertyNode_ptr aProp)
275 {
276   BasicWaypt::initFromProperties(aProp);
277   
278   if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
279     throw sg_io_exception("missing lon/lat properties", 
280       "Hold::initFromProperties");
281   }
282
283   _righthanded = aProp->getBoolValue("right-handed");
284   _isDistance = aProp->getBoolValue("is-distance");
285   _bearing = aProp->getDoubleValue("inbound-radial-deg");
286   _holdTD = aProp->getDoubleValue("td");
287 }
288
289 void Hold::writeToProperties(SGPropertyNode_ptr aProp) const
290 {
291   BasicWaypt::writeToProperties(aProp);
292
293   aProp->setBoolValue("right-handed", _righthanded);
294   aProp->setBoolValue("is-distance", _isDistance);
295   aProp->setDoubleValue("inbound-radial-deg", _bearing);
296   aProp->setDoubleValue("td", _holdTD);
297 }
298
299 /////////////////////////////////////////////////////////////////////////////
300
301 HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner, const string& aIdent, 
302   double aMagHdg) :
303   Waypt(aOwner),
304   _ident(aIdent),
305   _magHeading(aMagHdg)
306 {
307   setFlag(WPT_DYNAMIC);
308 }
309
310 HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner) :
311   Waypt(aOwner)
312 {
313 }
314
315 void HeadingToAltitude::initFromProperties(SGPropertyNode_ptr aProp)
316 {
317   if (!aProp->hasChild("heading-deg")) {
318     throw sg_io_exception("missing heading/alt properties", 
319       "HeadingToAltitude::initFromProperties");
320   }
321
322   Waypt::initFromProperties(aProp);
323   _magHeading = aProp->getDoubleValue("heading-deg");
324   _ident = aProp->getStringValue("ident");
325 }
326
327 void HeadingToAltitude::writeToProperties(SGPropertyNode_ptr aProp) const
328 {
329   Waypt::writeToProperties(aProp);
330   aProp->setStringValue("ident", _ident);
331   aProp->setDoubleValue("heading-deg", _magHeading);
332 }
333
334 /////////////////////////////////////////////////////////////////////////////
335
336 DMEIntercept::DMEIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos,
337     double aCourseDeg, double aDistanceNm) :
338   Waypt(aOwner),
339   _ident(aIdent),
340   _pos(aPos),
341   _magCourse(aCourseDeg),
342   _dmeDistanceNm(aDistanceNm)
343 {
344   setFlag(WPT_DYNAMIC);
345 }
346
347 DMEIntercept::DMEIntercept(RouteBase* aOwner) :
348   Waypt(aOwner)
349 {
350 }
351
352 void DMEIntercept::initFromProperties(SGPropertyNode_ptr aProp)
353 {
354   if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
355     throw sg_io_exception("missing lon/lat properties", 
356       "DMEIntercept::initFromProperties");
357   }
358
359   Waypt::initFromProperties(aProp);
360   _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
361   _ident = aProp->getStringValue("ident");
362 // check it's a real DME?
363   _magCourse = aProp->getDoubleValue("course-deg");
364   _dmeDistanceNm = aProp->getDoubleValue("dme-distance-nm");
365   
366 }
367
368 void DMEIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
369 {
370   Waypt::writeToProperties(aProp);
371   
372   aProp->setStringValue("ident", _ident);
373   aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
374   aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
375   aProp->setDoubleValue("course-deg", _magCourse);
376   aProp->setDoubleValue("dme-distance-nm", _dmeDistanceNm);
377 }
378
379 /////////////////////////////////////////////////////////////////////////////
380
381 RadialIntercept::RadialIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos,
382     double aCourseDeg, double aRadial) :
383   Waypt(aOwner),
384   _ident(aIdent),
385   _pos(aPos),
386   _magCourse(aCourseDeg),
387   _radial(aRadial)
388 {
389   setFlag(WPT_DYNAMIC);
390 }
391
392 RadialIntercept::RadialIntercept(RouteBase* aOwner) :
393   Waypt(aOwner)
394 {
395 }
396   
397 void RadialIntercept::initFromProperties(SGPropertyNode_ptr aProp)
398 {
399   if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
400     throw sg_io_exception("missing lon/lat properties", 
401       "RadialIntercept::initFromProperties");
402   }
403
404   Waypt::initFromProperties(aProp);
405   _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
406   _ident = aProp->getStringValue("ident");
407 // check it's a real VOR?
408   _magCourse = aProp->getDoubleValue("course-deg");
409   _radial = aProp->getDoubleValue("radial-deg");
410   
411 }
412
413 void RadialIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
414 {
415   Waypt::writeToProperties(aProp);
416   
417   aProp->setStringValue("ident", _ident);
418   aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
419   aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
420   aProp->setDoubleValue("course-deg", _magCourse);
421   aProp->setDoubleValue("radial-deg", _radial);
422 }
423
424 /////////////////////////////////////////////////////////////////////////////
425
426 ATCVectors::ATCVectors(RouteBase* aOwner, FGAirport* aFacility) :
427   Waypt(aOwner),
428   _facility(aFacility)
429 {
430   setFlag(WPT_DYNAMIC);
431 }
432
433 ATCVectors::~ATCVectors()
434 {
435 }
436
437 ATCVectors::ATCVectors(RouteBase* aOwner) :
438   Waypt(aOwner)
439 {
440 }
441
442 SGGeod ATCVectors::position() const
443 {
444   return _facility->geod();
445 }
446     
447 string ATCVectors::ident() const
448 {
449   return "VECTORS-" + _facility->ident();
450 }
451
452 void ATCVectors::initFromProperties(SGPropertyNode_ptr aProp)
453 {  
454   if (!aProp->hasChild("icao")) {
455     throw sg_io_exception("missing icao propertie", 
456       "ATCVectors::initFromProperties");
457   }
458
459   Waypt::initFromProperties(aProp);
460   _facility = FGAirport::getByIdent(aProp->getStringValue("icao"));
461 }
462
463 void ATCVectors::writeToProperties(SGPropertyNode_ptr aProp) const
464 {
465   Waypt::writeToProperties(aProp);
466   aProp->setStringValue("icao", _facility->ident());
467 }
468
469 /////////////////////////////////////////////////////////////////////////////
470
471 Discontinuity::Discontinuity(RouteBase* aOwner) :
472     Waypt(aOwner)
473 {
474     setFlag(WPT_DYNAMIC);
475     setFlag(WPT_GENERATED); // prevent drag, delete, etc
476 }
477
478 Discontinuity::~Discontinuity()
479 {
480 }
481
482 SGGeod Discontinuity::position() const
483 {
484     return SGGeod(); // deliberately invalid of course
485 }
486
487 string Discontinuity::ident() const
488 {
489     return "DISCONTINUITY";
490 }
491
492 void Discontinuity::initFromProperties(SGPropertyNode_ptr aProp)
493 {
494 }
495
496 void Discontinuity::writeToProperties(SGPropertyNode_ptr aProp) const
497 {
498     Waypt::writeToProperties(aProp);
499 }
500
501 /////////////////////////////////////////////////////////////////////////////
502
503 SGGeod Via::position() const
504 {
505     return _to->geod();
506 }
507
508 string Via::ident() const
509 {
510     return "VIA " + _airway + " TO " + _to->ident();
511 }
512
513 Via::Via(RouteBase *aOwner) :
514     Waypt(aOwner)
515 {
516 }
517
518 Via::Via(RouteBase *aOwner, const std::string &airwayName, FGPositioned *to) :
519     Waypt(aOwner),
520     _airway(airwayName),
521     _to(to)
522 {
523 }
524
525 Via::~Via()
526 {
527
528 }
529
530 void Via::initFromProperties(SGPropertyNode_ptr aProp)
531 {
532     if (!aProp->hasChild("airway") || !aProp->hasChild("to")) {
533         throw sg_io_exception("missing airway/to propertie",
534                               "Via::initFromProperties");
535     }
536
537     Waypt::initFromProperties(aProp);
538
539     _airway = aProp->getStringValue("airway");
540     Airway* way = Airway::findByIdent(_airway);
541     if (!way) {
542         throw sg_io_exception("unknown airway idnet: '" + _airway + "'",
543                               "Via::initFromProperties");
544     }
545
546     std::string idn(aProp->getStringValue("to"));
547     SGGeod p;
548     if (aProp->hasChild("lon")) {
549         p = SGGeod::fromDeg(aProp->getDoubleValue("lon"),
550                             aProp->getDoubleValue("lat"));
551     }
552
553     FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
554     if (!nav) {
555         throw sg_io_exception("unknown navaid ident:" + idn,
556                               "Via::initFromProperties");
557     }
558
559     _to = nav;
560 }
561
562 void Via::writeToProperties(SGPropertyNode_ptr aProp) const
563 {
564     Waypt::writeToProperties(aProp);
565     aProp->setStringValue("airway", _airway);
566     aProp->setStringValue("to", _to->ident());
567     // write lon/lat to disambiguate
568     aProp->setDoubleValue("lon", _to->geod().getLongitudeDeg());
569     aProp->setDoubleValue("lat", _to->geod().getLatitudeDeg());
570 }
571
572 WayptVec Via::expandToWaypoints(WayptRef aPreceeding) const
573 {
574     if (!aPreceeding) {
575         throw sg_exception("invalid preceeding waypoint");
576     }
577
578     Airway* way = Airway::findByIdent(_airway);
579     if (!way) {
580         throw sg_exception("invalid airway");
581     }
582
583     return way->via(aPreceeding, new NavaidWaypoint(_to, owner()));
584 }
585
586 } // of namespace