FlightGear next
positioned.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: (C) 2008 James Turner <james@flightgear.org>
3 * SPDX_FileComment: base class for objects which are spatially located in the simulated world
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "config.h"
8
9#include "positioned.hxx"
10
11#include <map>
12#include <set>
13#include <cstring> // strcmp
14#include <algorithm> // for sort
15#include <queue>
16#include <memory>
17
18
19#include <simgear/timing/timestamp.hxx>
20#include <simgear/debug/logstream.hxx>
21#include <simgear/structure/exception.hxx>
22#include <simgear/math/SGGeometry.hxx>
23#include <simgear/sg_inlines.h>
24
26
27using std::string;
28using namespace flightgear;
29
30static void validateSGGeod(const SGGeod& geod)
31{
32 if (SGMisc<double>::isNaN(geod.getLatitudeDeg()) ||
33 SGMisc<double>::isNaN(geod.getLongitudeDeg()))
34 {
35 throw sg_range_exception("position is invalid, NaNs");
36 }
37}
38
40{
41 if (filter->maxType() < filter->minType()) {
42 SG_LOG(SG_GENERAL, SG_WARN, "invalid positioned filter specified");
43 return false;
44 }
45
46 return true;
47}
48
50
52 Type ty,
53 const std::string& aIdent,
54 const SGGeod& aPos ):
55 mGuid(aGuid),
56 mType(ty),
57 mIdent(aIdent),
58 mPosition(aPos),
59 mCart(SGVec3d::fromGeod(mPosition))
60{
61
62}
63
67
68// Static method
70{
71 if (!pos) {
72 return false;
73 }
74
75 return (pos->type() >= AIRPORT) && (pos->type() <= SEAPORT);
76}
77
78// Static method
80{
81 return (pos && pos->type() == RUNWAY);
82}
83
84// Static method
86{
87 if (!pos) {
88 return false;
89 }
90
91 switch (pos->type()) {
92 case NDB:
93 case VOR:
94 case ILS:
95 case LOC:
96 case GS:
97 case DME:
98 case TACAN:
99 return true;
100 default:
101 return false;
102 }
103}
104
106{
107 switch (ty) {
112 return true;
113 default:
114 return false;
115 }
116}
117
119FGPositioned::createWaypoint(FGPositioned::Type aType, const std::string& aIdent, const SGGeod& aPos,
120 bool isTemporary,
121 const std::string& aName)
122{
124
125 if (!isValidCustomWaypointType(aType)) {
126 throw std::logic_error(std::string{"Create waypoint: not allowed for type:"} + nameForType(aType));
127 }
128
129 TypeFilter filter(aType);
130
131 FGPositionedRef existing = cache->findClosestWithIdent(aIdent, aPos, &filter);
132 if (existing) {
133 auto distanceNm = SGGeodesy::distanceNm(existing->geod(), aPos);
134 if (distanceNm < 100) {
135 SG_LOG(SG_NAVAID, SG_WARN, "attempt to insert duplicate waypoint:" << aIdent << " within 100nm of existing waypoint with same ident");
136 return existing;
137 }
138 }
139
140 const PositionedID id = cache->createPOI(aType, aIdent, aPos, aName, isTemporary);
141 return cache->loadById(id);
142}
143
145{
147 const auto ty = ref->type();
148 if (!POI::isType(ty) && (ty != FGPositioned::FIX)) {
149 SG_LOG(SG_NAVAID, SG_DEV_WARN, "attempt to remove non-POI waypoint:" << ref->ident());
150 return false;
151 }
152
153 return cache->removePOI(ref);
154}
155
156
157const SGVec3d&
159{
160 return mCart;
161}
162
164{
165 if (aName.empty() || (aName == "")) {
166 return INVALID;
167 }
168
169 typedef struct {
170 const char* _name;
171 Type _ty;
172 } NameTypeEntry;
173
174 const NameTypeEntry names[] = {
175 {"airport", AIRPORT},
176 {"heliport", HELIPORT},
177 {"seaport", SEAPORT},
178 {"vor", VOR},
179 {"loc", LOC},
180 {"ils", ILS},
181 {"gs", GS},
182 {"ndb", NDB},
183 {"wpt", WAYPOINT},
184 {"fix", FIX},
185 {"tacan", TACAN},
186 {"dme", DME},
187 {"atis", FREQ_ATIS},
188 {"awos", FREQ_AWOS},
189 {"tower", FREQ_TOWER},
190 {"ground", FREQ_GROUND},
191 {"approach", FREQ_APP_DEP},
192 {"departure", FREQ_APP_DEP},
193 {"clearance", FREQ_CLEARANCE},
194 {"unicom", FREQ_UNICOM},
195 {"runway", RUNWAY},
196 {"helipad", HELIPAD},
197 {"country", COUNTRY},
198 {"city", CITY},
199 {"town", TOWN},
200 {"village", VILLAGE},
201 {"taxiway", TAXIWAY},
202 {"pavement", PAVEMENT},
203 {"om", OM},
204 {"mm", MM},
205 {"im", IM},
206 {"mobile-tacan", MOBILE_TACAN},
207 {"obstacle", OBSTACLE},
208 {"parking", PARKING},
209 {"taxi-node", TAXI_NODE},
210 {"visual-reporting-point", VISUAL_REPORTING_POINT},
211
212 // aliases
213 {"localizer", LOC},
214 {"gnd", FREQ_GROUND},
215 {"twr", FREQ_TOWER},
216 {"waypoint", WAYPOINT},
217 {"apt", AIRPORT},
218 {"arpt", AIRPORT},
219 {"rwy", RUNWAY},
220 {"any", INVALID},
221 {"all", INVALID},
222 {"outer-marker", OM},
223 {"middle-marker", MM},
224 {"inner-marker", IM},
225 {"parking-stand", PARKING},
226 {"vrp", VISUAL_REPORTING_POINT},
227
228 {NULL, INVALID}};
229
230 std::string lowerName = simgear::strutils::lowercase(aName);
231
232 for (const NameTypeEntry* n = names; (n->_name != NULL); ++n) {
233 if (::strcmp(n->_name, lowerName.c_str()) == 0) {
234 return n->_ty;
235 }
236 }
237
238 SG_LOG(SG_NAVAID, SG_WARN, "FGPositioned::typeFromName: couldn't match:" << aName);
239 return INVALID;
240}
241
243{
244 switch (aTy) {
245 case RUNWAY: return "runway";
246 case HELIPAD: return "helipad";
247 case TAXIWAY: return "taxiway";
248 case PAVEMENT: return "pavement";
249 case PARKING: return "parking stand";
250 case FIX: return "fix";
251 case VOR: return "VOR";
252 case NDB: return "NDB";
253 case ILS: return "ILS";
254 case LOC: return "localizer";
255 case GS: return "glideslope";
256 case OM: return "outer-marker";
257 case MM: return "middle-marker";
258 case IM: return "inner-marker";
259 case AIRPORT: return "airport";
260 case HELIPORT: return "heliport";
261 case SEAPORT: return "seaport";
262 case WAYPOINT: return "waypoint";
263 case DME: return "dme";
264 case TACAN: return "tacan";
265 case FREQ_TOWER: return "tower";
266 case FREQ_ATIS: return "atis";
267 case FREQ_AWOS: return "awos";
268 case FREQ_GROUND: return "ground";
269 case FREQ_CLEARANCE: return "clearance";
270 case FREQ_UNICOM: return "unicom";
271 case FREQ_APP_DEP: return "approach-departure";
272 case TAXI_NODE: return "taxi-node";
273 case COUNTRY: return "country";
274 case CITY: return "city";
275 case TOWN: return "town";
276 case VILLAGE: return "village";
277 case VISUAL_REPORTING_POINT: return "visual-reporting-point";
278 case MOBILE_TACAN: return "mobile-tacan";
279 case OBSTACLE: return "obstacle";
280 default:
281 return "unknown";
282 }
283}
284
286// search / query functions
287
289FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter)
290{
291 validateSGGeod(aPos);
292 return NavDataCache::instance()->findClosestWithIdent(aIdent, aPos, aFilter);
293}
294
296FGPositioned::findFirstWithIdent(const std::string& aIdent, Filter* aFilter)
297{
298 if (aIdent.empty()) {
299 return NULL;
300 }
301
303 NavDataCache::instance()->findAllWithIdent(aIdent, aFilter, true);
304 if (r.empty()) {
305 return NULL;
306 }
307
308 return r.front();
309}
310
312FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter)
313{
314 validateSGGeod(aPos);
315
316 if (!validateFilter(aFilter)) {
317 return FGPositionedList();
318 }
319
320 FGPositionedList result;
321 Octree::findAllWithinRange(SGVec3d::fromGeod(aPos),
322 aRangeNm * SG_NM_TO_METER, aFilter, result, 0xffffff);
323 return result;
324}
325
327FGPositioned::findWithinRangePartial(const SGGeod& aPos, double aRangeNm, Filter* aFilter, bool& aPartial)
328{
329 validateSGGeod(aPos);
330
331 if (!validateFilter(aFilter)) {
332 return FGPositionedList();
333 }
334
335 int limitMsec = 32;
336 FGPositionedList result;
337 aPartial = Octree::findAllWithinRange(SGVec3d::fromGeod(aPos),
338 aRangeNm * SG_NM_TO_METER, aFilter, result,
339 limitMsec);
340 return result;
341}
342
344FGPositioned::findAllWithIdent(const std::string& aIdent, Filter* aFilter, bool aExact)
345{
346 if (!validateFilter(aFilter)) {
347 return FGPositionedList();
348 }
349
350 return NavDataCache::instance()->findAllWithIdent(aIdent, aFilter, aExact);
351}
352
354FGPositioned::findAllWithName(const std::string& aName, Filter* aFilter, bool aExact)
355{
356 if (!validateFilter(aFilter)) {
357 return FGPositionedList();
358 }
359
360 return NavDataCache::instance()->findAllWithName(aName, aFilter, aExact);
361}
362
364FGPositioned::findClosest(const SGGeod& aPos, double aCutoffNm, Filter* aFilter)
365{
366 validateSGGeod(aPos);
367
368 if (!validateFilter(aFilter)) {
369 return NULL;
370 }
371
372 FGPositionedList l(findClosestN(aPos, 1, aCutoffNm, aFilter));
373 if (l.empty()) {
374 return NULL;
375 }
376
377 assert(l.size() == 1);
378 return l.front();
379}
380
382FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter)
383{
384 validateSGGeod(aPos);
385
386 FGPositionedList result;
387 int limitMsec = 0xffff;
388 Octree::findNearestN(SGVec3d::fromGeod(aPos), aN, aCutoffNm * SG_NM_TO_METER, aFilter, result, limitMsec);
389 return result;
390}
391
393FGPositioned::findClosestNPartial(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter, bool &aPartial)
394{
395 validateSGGeod(aPos);
396
397 FGPositionedList result;
398 int limitMsec = 32;
399 aPartial = Octree::findNearestN(SGVec3d::fromGeod(aPos), aN, aCutoffNm * SG_NM_TO_METER, aFilter, result,
400 limitMsec);
401 return result;
402}
403
404void
405FGPositioned::sortByRange(FGPositionedList& aResult, const SGGeod& aPos)
406{
407 validateSGGeod(aPos);
408
409 SGVec3d cartPos(SGVec3d::fromGeod(aPos));
410// computer ordering values
412 FGPositionedList::iterator it = aResult.begin(), lend = aResult.end();
413 for (; it != lend; ++it) {
414 double d2 = distSqr((*it)->cart(), cartPos);
415 r.push_back(Octree::OrderedPositioned(*it, d2));
416 }
417
418// sort
419 std::sort(r.begin(), r.end());
420
421// convert to a plain list
422 unsigned int count = aResult.size();
423 for (unsigned int i=0; i<count; ++i) {
424 aResult[i] = r[i].get();
425 }
426}
427
428//------------------------------------------------------------------------------
429void FGPositioned::modifyPosition(const SGGeod& newPos)
430{
431 const_cast<SGGeod&>(mPosition) = newPos;
432 const_cast<SGVec3d&>(mCart) = SGVec3d::fromGeod(newPos);
433}
434
435//------------------------------------------------------------------------------
437{
438 const_cast<SGGeod&>(mPosition) = SGGeod::fromDeg(999,999);
439 const_cast<SGVec3d&>(mCart) = SGVec3d::zeros();
440}
441
442//------------------------------------------------------------------------------
447
452
453FGPositioned::TypeFilter::TypeFilter(std::initializer_list<Type> types)
454{
455 for (auto t : types) {
456 addType(t);
457 }
458}
459
461{
462 for (int t = aMinType; t <= aMaxType; t++) {
463 addType(static_cast<FGPositioned::Type>(t));
464 }
465}
466
467
469{
470 if (aTy == INVALID) {
471 return;
472 }
473
474 types.push_back(aTy);
475 mMinType = std::min(mMinType, aTy);
476 mMaxType = std::max(mMaxType, aTy);
477}
478
480FGPositioned::TypeFilter::fromString(const std::string& aFilterSpec)
481{
482 if (aFilterSpec.empty()) {
483 throw sg_format_exception("empty filter spec:", aFilterSpec);
484 }
485
486 string_list parts = simgear::strutils::split(aFilterSpec, ",");
487 TypeFilter f;
488
489 for (std::string token : parts) {
490 f.addType(typeFromName(token));
491 }
492
493 return f;
494}
495
496bool
498{
499 if (types.empty()) {
500 return true;
501 }
502
503 std::vector<Type>::const_iterator it = types.begin(),
504 end = types.end();
505 for (; it != end; ++it) {
506 if (aPos->type() == *it) {
507 return true;
508 }
509 }
510
511 return false;
512}
513
514POI::POI(PositionedID aGuid, Type ty, const std::string& aIdent, const SGGeod& aPos, const std::string& aName) : FGPositioned(aGuid, ty, aIdent, aPos),
515 mName(aName)
516{
517}
518
#define i(x)
Predicate class to support custom filtering of FGPositioned queries Default implementation of this pa...
virtual Type maxType() const
virtual Type minType() const
bool pass(FGPositioned *aPos) const override
Over-rideable filter method.
TypeFilter(Type aTy=INVALID)
static TypeFilter fromString(const std::string &aFilterSpec)
static Type typeFromName(const std::string &aName)
Map a candidate type string to a real type.
static FGPositionedList findAllWithName(const std::string &aName, Filter *aFilter=NULL, bool aExact=true)
As above, but searches names instead of idents.
static bool isRunwayType(FGPositioned *pos)
FGPositioned(PositionedID aGuid, Type ty, const std::string &aIdent, const SGGeod &aPos)
static FGPositionedRef createWaypoint(FGPositioned::Type aType, const std::string &aIdent, const SGGeod &aPos, bool isTemporary=false, const std::string &aName={})
friend class flightgear::NavDataCache
static FGPositionedRef findClosestWithIdent(const std::string &aIdent, const SGGeod &aPos, Filter *aFilter=NULL)
void modifyPosition(const SGGeod &newPos)
const std::string mIdent
static FGPositionedList findWithinRange(const SGGeod &aPos, double aRangeNm, Filter *aFilter)
static FGPositionedList findClosestNPartial(const SGGeod &aPos, unsigned int aN, double aCutoffNm, Filter *aFilter, bool &aPartial)
Same as above, but with a time-bound in msec too.
virtual const SGVec3d & cart() const
The cartesian position associated with this object.
static FGPositionedRef findFirstWithIdent(const std::string &aIdent, Filter *aFilter)
static void sortByRange(FGPositionedList &, const SGGeod &aPos)
Sort an FGPositionedList by distance from a position.
static FGPositionedRef loadByIdImpl(PositionedID id)
void invalidatePosition()
static bool isAirportType(FGPositioned *pos)
const PositionedID mGuid
@ PARKING
parking position - might be a gate, or stand
@ DME
important that DME & TACAN are adjacent to keep the TacanFilter efficient - DMEs are proxies for TACA...
virtual ~FGPositioned()
static FGPositionedList findAllWithIdent(const std::string &aIdent, Filter *aFilter=NULL, bool aExact=true)
Find all items with the specified ident.
static const char * nameForType(Type aTy)
Map a type to a human-readable string.
static bool isNavaidType(FGPositioned *pos)
const Type mType
static FGPositionedList findClosestN(const SGGeod &aPos, unsigned int aN, double aCutoffNm, Filter *aFilter=NULL)
Find the closest N items to a position, which pass the specified filter A cutoff range in NM must be ...
Type type() const
static FGPositionedList findWithinRangePartial(const SGGeod &aPos, double aRangeNm, Filter *aFilter, bool &aPartial)
static bool deleteWaypoint(FGPositionedRef aWpt)
static FGPositionedRef findClosest(const SGGeod &aPos, double aCutoffNm, Filter *aFilter=NULL)
Find the closest item to a position, which pass the specified filter A cutoff range in NM must be spe...
static bool isType(FGPositioned::Type ty)
POI(PositionedID aGuid, Type ty, const std::string &aIdent, const SGGeod &aPos, const std::string &aName)
PositionedID createPOI(FGPositioned::Type ty, const std::string &ident, const SGGeod &aPos, const std::string &aName, bool transient)
FGPositionedRef findClosestWithIdent(const std::string &aIdent, const SGGeod &aPos, FGPositioned::Filter *aFilter)
FGPositionedList findAllWithIdent(const std::string &ident, FGPositioned::Filter *filter, bool exact)
bool removePOI(FGPositionedRef wpt)
FGPositionedRef loadById(PositionedID guid)
retrieve an FGPositioned from the cache.
static NavDataCache * instance()
FGPositionedList findAllWithName(const std::string &ident, FGPositioned::Filter *filter, bool exact)
std::vector< std::string > string_list
Definition globals.hxx:36
Ordered< FGPositioned * > OrderedPositioned
bool findAllWithinRange(const SGVec3d &aPos, double aRangeM, FGPositioned::Filter *aFilter, FGPositionedList &aResults, int aCutoffMsec)
std::vector< OrderedPositioned > FindNearestResults
bool findNearestN(const SGVec3d &aPos, unsigned int aN, double aCutoffM, FGPositioned::Filter *aFilter, FGPositionedList &aResults, int aCutoffMsec)
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
SGSharedPtr< FGPositioned > FGPositionedRef
Definition airways.cxx:49
static bool validateFilter(FGPositioned::Filter *filter)
static void validateSGGeod(const SGGeod &geod)
static bool isValidCustomWaypointType(FGPositioned::Type ty)
SGSharedPtr< FGPositioned > FGPositionedRef
std::vector< FGPositionedRef > FGPositionedList
int64_t PositionedID