16#include <simgear/scene/util/OsgMath.hxx>
17#include <simgear/debug/logstream.hxx>
18#include <simgear/math/SGGeometryFwd.hxx>
19#include <simgear/math/SGIntersect.hxx>
20#include <simgear/math/SGLineSegment.hxx>
21#include <simgear/structure/exception.hxx>
22#include <simgear/timing/timestamp.hxx>
43 if (!aStart || !aEnd) {
44 throw sg_exception(
"Missing node arguments creating FGTaxiSegment");
51 double heading, length, az2;
52 SGGeodesy::inverse(start->geod(), end->
geod(), heading, az2, length);
53 return SGGeodesy::direct(start->geod(), heading, length * 0.5);
79 while (
i != blockTimes.end()) {
84 if (
i == blockTimes.end()) {
85 blockTimes.push_back(
Block(
id, blockTime, now));
86 sort(blockTimes.begin(), blockTimes.end());
88 i->updateTimeStamps(blockTime, now);
97 if (
i->getBlockTime() < now)
105 if (blockTimes.empty())
108 if (blockTimes.front().getTimeStamp() < (now - 30)) {
109 blockTimes.erase(blockTimes.begin());
121 currNode = nodes.begin();
122 currRoute = routes.begin();
124 if (nodes.size() != (routes.size()) + 1) {
125 SG_LOG(SG_GENERAL, SG_ALERT,
"ALERT: Misconfigured TaxiRoute : " << nodes.size() <<
" " << routes.size());
131 if (nodes.size() != (routes.size()) + 1) {
132 throw sg_range_exception(
"Misconfigured taxi route");
135 if (currNode == nodes.end())
138 if (currNode != nodes.begin()) {
141 if (currRoute == routes.end()) {
142 throw sg_range_exception(
"Misconfigured taxi route");
149 *rte = -1 * *(currRoute);
163 networkInitialized =
false;
168 for (
auto seg : segments) {
175 if (networkInitialized) {
176 SG_LOG(SG_GENERAL, SG_WARN,
"duplicate ground-network init");
184 for (
auto segment : segments) {
185 segment->setIndex(index++);
187 if (segment->oppositeDirection) {
193 assert(opp->oppositeDirection == NULL);
194 segment->oppositeDirection = opp;
195 opp->oppositeDirection = segment;
199 m_segmentsEndingAtNodeMap.insert(NodeFromSegmentMap::value_type{segment->getEnd(), segment});
202 networkInitialized =
true;
208 SGVec3d cartPos = SGVec3d::fromGeod(aGeod);
211 FGTaxiNodeVector::const_iterator it;
212 for (it = m_nodes.begin(); it != m_nodes.end(); ++it) {
213 double localDistanceSqr = distSqr(cartPos, (*it)->cart());
214 if (localDistanceSqr < d) {
215 d = localDistanceSqr;
226 const SGLineSegmentd runwayLine(rwy->
cart(), SGVec3d::fromGeod(rwy->
end()));
227 const double marginMSqr = marginM * marginM;
228 const SGVec3d cartPos = SGVec3d::fromGeod(aGeod);
230 std::copy_if(m_nodes.begin(), m_nodes.end(), std::back_inserter(nodes),
232 if (a->getIsOnRunway()) return false;
238 if (a->type() == FGPositioned::PARKING) return false;
240 return (distSqr(runwayLine, a->cart()) >= marginMSqr);
245 auto node = std::min_element(nodes.begin(), nodes.end(),
246 [cartPos](
const FGTaxiNodeRef& a,
const FGTaxiNodeRef& b) { return distSqr(cartPos, a->cart()) < distSqr(cartPos, b->cart()); });
248 if (node == nodes.end()) {
258 SGVec3d cartPos = SGVec3d::fromGeod(aGeod);
260 for (
auto it = m_nodes.begin(); it != m_nodes.end(); ++it) {
261 if (!(*it)->getIsOnRunway())
263 double localDistanceSqr = distSqr(cartPos, (*it)->cart());
264 if (localDistanceSqr < d) {
265 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunway from Threshold " << localDistanceSqr);
266 d = localDistanceSqr;
284 SGVec3d cartPos = SGVec3d::fromGeod(aGeod);
286 FGTaxiNodeVector::const_iterator it;
288 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunwayExit " << aRunway->
ident() <<
" " << aRunway->
headingDeg());
289 for (it = m_nodes.begin(); it != m_nodes.end(); ++it) {
290 if (!(*it)->getIsOnRunway())
292 double localDistanceSqr = distSqr(cartPos, (*it)->cart());
293 double headingTowardsExit = SGGeodesy::courseDeg(aGeod, (*it)->geod());
294 double diff = fabs(SGMiscd::normalizePeriodic(-180, 180, aRunway->
headingDeg() - headingTowardsExit));
295 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunwayExit Diff : " << diff <<
" Id : " << (*it)->getIndex());
302 if (exitSegments.size() > 2) {
307 if (exitSegments.size() == 2 &&
308 ((*exitSegments.at(0)).getIsOnRunway() || (*exitSegments.at(0)).getIsOnRunway())) {
311 if (exitSegments.empty()) {
312 SG_LOG(SG_AI, SG_ALERT,
"findNearestNodeOnRunwayExit broken node :" << diff <<
" Node Id : " << (*it)->getIndex() <<
" Apt : " << aRunway->
airport()->getId());
315 double exitHeading = SGGeodesy::courseDeg((*it)->geod(),
316 (exitSegments.back())->geod());
317 diff = fabs(SGMiscd::normalizePeriodic(-180, 180, aRunway->
headingDeg() - exitHeading));
318 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunwayExit2 Diff :" << diff <<
" Id : " << (*it)->getIndex());
323 if (localDistanceSqr < d) {
324 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunwayExit3 " << localDistanceSqr <<
" " << (*it)->getIndex());
325 d = localDistanceSqr;
330 SG_LOG(SG_AI, SG_ALERT,
"No Runway findNearestNodeOnRunwayExit");
333 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunwayExit found :" << result->getIndex());
337 for (it = m_nodes.begin(); it != m_nodes.end(); ++it) {
338 if (!(*it)->getIsOnRunway())
340 double localDistanceSqr = distSqr(cartPos, (*it)->cart());
342 double headingTowardsExit = SGGeodesy::courseDeg(aGeod, (*it)->geod());
343 double diff = fabs(aRunway->
headingDeg() - headingTowardsExit);
344 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunwayExitFallback1 " << aRunway->
headingDeg() <<
" "
345 <<
" Diff : " << diff <<
" " << (*it)->getIndex());
351 if (localDistanceSqr < d) {
352 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunwayExitFallback1 " << localDistanceSqr);
353 d = localDistanceSqr;
361 for (it = m_nodes.begin(); it != m_nodes.end(); ++it) {
362 if (!(*it)->getIsOnRunway())
364 double localDistanceSqr = distSqr(cartPos, (*it)->cart());
365 if (localDistanceSqr < d) {
366 SG_LOG(SG_AI, SG_BULK,
"findNearestNodeOnRunwayExitFallback2 " << localDistanceSqr);
367 d = localDistanceSqr;
374 SG_LOG(SG_AI, SG_WARN,
"No runway exit found " << aRunway->
airport()->getId() <<
"/" << aRunway->
name());
394 if ((idx > 0) && (idx <= segments.size()))
395 return segments[idx - 1];
410 for (
auto seg : segments) {
411 if (seg->startNode != from) {
415 if ((to == 0) || (seg->endNode == to)) {
442 if (!start || !end) {
443 throw sg_exception(
"Bad arguments to findShortestRoute");
448 std::map<FGTaxiNode*, ShortestPathData> searchData;
450 searchData[start].score = 0.0;
452 while (!unvisited.empty()) {
455 for (
auto i : unvisited) {
456 if (searchData[
i].score < searchData[best].score) {
463 remove(unvisited.begin(), unvisited.end(), best);
464 unvisited.erase(newend, unvisited.end());
471 double edgeLength = dist(best->cart(), target->cart());
472 double alt = searchData[best].score + edgeLength +
edgePenalty(target);
473 if (alt < searchData[target].score) {
474 searchData[target].score = alt;
475 searchData[target].previousNode = best;
480 if (searchData[end].score == HUGE_VAL) {
482 if (fullSearch && start && end) {
483 SG_LOG(SG_GENERAL, SG_ALERT,
484 "Failed to find route from waypoint " << start->
getIndex() <<
" to "
485 << end->
getIndex() <<
" at " << parent->getId());
496 while (searchData[bt].previousNode != 0) {
500 routes.push_back(idx);
501 bt = searchData[bt].previousNode;
503 nodes.push_back(start);
504 reverse(nodes.begin(), nodes.end());
505 reverse(routes.begin(), routes.end());
506 return FGTaxiRoute(nodes, routes, searchData[end].score, 0);
511 for (
auto& seg : segments) {
519 throw sg_exception(
"Passed invalid segment");
523 const auto range = m_segmentsEndingAtNodeMap.equal_range(seg->
getEnd());
524 for (
auto it = range.first; it != range.second; ++it) {
526 if (it->second == seg)
530 it->second->block(blockId, blockTime, now);
532 SG_LOG(SG_ATC, SG_BULK,
"blockSegmentsEndingAt " <<
"\t" <<
i <<
"\t" << seg->
getIndex() <<
"\t" << blockId);
535FGTaxiNodeRef FGGroundNetwork::findNodeByIndex(
int index)
const
537 FGTaxiNodeVector::const_iterator it;
538 for (it = m_nodes.begin(); it != m_nodes.end(); ++it) {
539 if ((*it)->getIndex() == index) {
559 auto it = std::find_if(m_parkings.begin(), m_parkings.end(), [
name](
const FGParkingRef&
p) {
560 return p->ident() == name;
563 if (it == m_parkings.end())
573 segments.push_back(seg);
575 FGTaxiNodeVector::iterator it = std::find(m_nodes.begin(), m_nodes.end(), from);
576 if (it == m_nodes.end()) {
577 m_nodes.push_back(from);
580 it = std::find(m_nodes.begin(), m_nodes.end(), to);
581 if (it == m_nodes.end()) {
582 m_nodes.push_back(to);
586void FGGroundNetwork::addParking(
const FGParkingRef& park)
588 m_parkings.push_back(park);
591 FGTaxiNodeVector::iterator it = std::find(m_nodes.begin(), m_nodes.end(), park);
592 if (it == m_nodes.end()) {
593 m_nodes.push_back(park);
600 FGTaxiSegmentVector::const_iterator it;
601 for (it = segments.begin(); it != segments.end(); ++it) {
602 if ((*it)->getStart() == from) {
603 result.push_back((*it)->getEnd());
620 for (
auto seg : segments) {
621 if (seg->startNode != from) {
BlockList::iterator BlockListIterator
FGTaxiNodeVector::iterator FGTaxiNodeVectorIterator
SGSharedPtr< FGTaxiNode > FGTaxiNodeRef
std::vector< FGParkingRef > FGParkingList
std::vector< FGTaxiNodeRef > FGTaxiNodeVector
SGSharedPtr< FGParking > FGParkingRef
FGTaxiNodeRef findNearestNodeOffRunway(const SGGeod &aGeod, FGRunway *aRunway, double distanceM) const
FGGroundNetwork(FGAirport *pr)
FGTaxiNodeRef findNearestNodeOnRunwayExit(const SGGeod &aGeod, FGRunway *aRunway=NULL) const
Returns the nearest node in that is in direction of runway heading.
FGTaxiNodeVector findSegmentsFrom(const FGTaxiNodeRef &from) const
Find the segments connected to the node.
FGTaxiSegment * findSegmentByHeading(const FGTaxiNode *from, const double heading) const
Find the taxiway segment best matching the heading.
FGAirport * airport() const
FGParkingRef findParkingByName(const std::string &name) const
const intVec & getApproachFrequencies() const
FGTaxiNodeRef findNearestNodeOnRunwayEntry(const SGGeod &aGeod) const
const intVec & getGroundFrequencies() const
FGParkingRef getParkingByIndex(unsigned int index) const
void unblockAllSegments(time_t now)
FGTaxiSegment * findSegment(const FGTaxiNode *from, const FGTaxiNode *to) const
Find the taxiway segment joining two (ground-net) nodes.
FGTaxiNodeRef findNearestNode(const SGGeod &aGeod) const
virtual ~FGGroundNetwork()
FGTaxiSegment * findOppositeSegment(unsigned int index) const
FGTaxiRoute findShortestRoute(FGTaxiNode *start, FGTaxiNode *end, bool fullSearch=true)
void blockSegmentsEndingAt(const FGTaxiSegment *seg, int blockId, time_t blockTime, time_t now)
const intVec & getTowerFrequencies() const
const FGParkingList & allParkings() const
virtual const std::string & name() const
Return the name of this positioned.
virtual const SGGeod & geod() const
virtual const SGVec3d & cart() const
The cartesian position associated with this object.
@ PARKING
parking position - might be a gate, or stand
const std::string & ident() const
double headingDeg() const
Runway heading in degrees.
FGAirportRef airport() const
SGGeod end() const
Get the 'far' end - this is equivalent to calling pointOnCenterline(lengthFt());.
bool getIsOnRunway() const
bool next(FGTaxiNodeRef &nde, int *rte)
FGTaxiSegment(FGTaxiNode *start, FGTaxiNode *end)
FGTaxiNodeRef getEnd() const
bool hasBlock(time_t now)
FGTaxiSegment * opposite()
FGTaxiNodeRef getStart() const
void block(int id, time_t blockTime, time_t now)
double getHeading() const
FGTaxiNodeRef previousNode
static int edgePenalty(FGTaxiNode *tn)
std::vector< int > intVec