1 // groundnet.cxx - Implimentation of the FlightGear airport ground handling code
3 // Written by Durk Talsma, started June 2005.
5 // Copyright (C) 2004 Durk Talsma.
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "groundnetwork.hxx"
33 #include <boost/foreach.hpp>
35 #include <simgear/debug/logstream.hxx>
36 #include <simgear/scene/util/OsgMath.hxx>
37 #include <simgear/structure/exception.hxx>
38 #include <simgear/timing/timestamp.hxx>
40 #include <Airports/airport.hxx>
41 #include <Airports/dynamics.hxx>
42 #include <Airports/runways.hxx>
44 #include <Scenery/scenery.hxx>
47 using flightgear::NavDataCache;
49 /***************************************************************************
51 **************************************************************************/
53 FGTaxiSegment::FGTaxiSegment(FGTaxiNode* aStart, FGTaxiNode* aEnd) :
60 if (!aStart || !aEnd) {
61 throw sg_exception("Missing node arguments creating FGTaxiSegment");
65 SGGeod FGTaxiSegment::getCenter() const
67 FGTaxiNode* start(getStart()), *end(getEnd());
68 double heading, length, az2;
69 SGGeodesy::inverse(start->geod(), end->geod(), heading, az2, length);
70 return SGGeodesy::direct(start->geod(), heading, length * 0.5);
73 FGTaxiNodeRef FGTaxiSegment::getEnd() const
75 return const_cast<FGTaxiNode*>(endNode);
78 FGTaxiNodeRef FGTaxiSegment::getStart() const
80 return const_cast<FGTaxiNode*>(startNode);
83 double FGTaxiSegment::getLength() const
85 return dist(getStart()->cart(), getEnd()->cart());
88 double FGTaxiSegment::getHeading() const
90 return SGGeodesy::courseDeg(getStart()->geod(), getEnd()->geod());
94 void FGTaxiSegment::block(int id, time_t blockTime, time_t now)
96 BlockListIterator i = blockTimes.begin();
97 while (i != blockTimes.end()) {
102 if (i == blockTimes.end()) {
103 blockTimes.push_back(Block(id, blockTime, now));
104 sort(blockTimes.begin(), blockTimes.end());
106 i->updateTimeStamps(blockTime, now);
110 // The segment has a block if any of the block times listed in the block list is
111 // smaller than the current time.
112 bool FGTaxiSegment::hasBlock(time_t now)
114 for (BlockListIterator i = blockTimes.begin(); i != blockTimes.end(); i++) {
115 if (i->getBlockTime() < now)
121 void FGTaxiSegment::unblock(time_t now)
123 if (blockTimes.size()) {
124 BlockListIterator i = blockTimes.begin();
125 if (i->getTimeStamp() < (now - 30)) {
133 /***************************************************************************
135 **************************************************************************/
136 bool FGTaxiRoute::next(FGTaxiNodeRef& node, int *rte)
138 if (nodes.size() != (routes.size()) + 1) {
139 SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
140 throw sg_range_exception("Misconfigured taxi route");
142 if (currNode == nodes.end())
145 if (currNode != nodes.begin()) {
149 // Handle special case for the first node.
150 *rte = -1 * *(currRoute);
156 /***************************************************************************
158 **************************************************************************/
160 FGGroundNetwork::FGGroundNetwork() :
166 networkInitialized = false;
169 FGGroundNetwork::~FGGroundNetwork()
172 BOOST_FOREACH(FGTaxiSegment* seg, segments) {
176 // owning references to ground-net nodes will also drop
179 void FGGroundNetwork::init(FGAirportDynamics* dyn)
181 if (networkInitialized) {
182 SG_LOG(SG_GENERAL, SG_WARN, "duplicate ground-network init");
187 parent = dyn->parent();
192 // establish pairing of segments
193 BOOST_FOREACH(FGTaxiSegment* segment, segments) {
194 segment->setIndex(index++);
196 if (segment->oppositeDirection) {
197 continue; // already established
200 FGTaxiSegment* opp = findSegment(segment->endNode, segment->startNode);
202 assert(opp->oppositeDirection == NULL);
203 segment->oppositeDirection = opp;
204 opp->oppositeDirection = segment;
208 networkInitialized = true;
211 FGTaxiNodeRef FGGroundNetwork::findNearestNode(const SGGeod & aGeod) const
214 SGVec3d cartPos = SGVec3d::fromGeod(aGeod);
215 FGTaxiNodeRef result;
217 FGTaxiNodeVector::const_iterator it;
218 for (it = m_nodes.begin(); it != m_nodes.end(); ++it) {
219 double localDistanceSqr = distSqr(cartPos, (*it)->cart());
220 if (localDistanceSqr < d) {
221 d = localDistanceSqr;
229 FGTaxiNodeRef FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway) const
234 SGVec3d cartPos = SGVec3d::fromGeod(aGeod);
235 FGTaxiNodeRef result = 0;
236 FGTaxiNodeVector::const_iterator it;
237 for (it = m_nodes.begin(); it != m_nodes.end(); ++it) {
238 if (!(*it)->getIsOnRunway())
241 double localDistanceSqr = distSqr(cartPos, (*it)->cart());
242 if (localDistanceSqr < d) {
243 d = localDistanceSqr;
251 FGTaxiSegment *FGGroundNetwork::findOppositeSegment(unsigned int index) const
253 FGTaxiSegment* seg = findSegment(index);
256 return seg->opposite();
259 const FGParkingList &FGGroundNetwork::allParkings() const
264 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) const
266 if ((idx > 0) && (idx <= segments.size()))
267 return segments[idx - 1];
269 //cerr << "Alert: trying to find invalid segment " << idx << endl;
274 FGTaxiSegment* FGGroundNetwork::findSegment(const FGTaxiNode* from, const FGTaxiNode* to) const
280 // completely boring linear search of segments. Can be improved if/when
281 // this ever becomes a hot-spot
282 BOOST_FOREACH(FGTaxiSegment* seg, segments) {
283 if (seg->startNode != from) {
287 if ((to == 0) || (seg->endNode == to)) {
292 return NULL; // not found
295 static int edgePenalty(FGTaxiNode* tn)
297 return (tn->type() == FGPositioned::PARKING ? 10000 : 0) +
298 (tn->getIsOnRunway() ? 1000 : 0);
301 class ShortestPathData
309 FGTaxiNodeRef previousNode;
312 FGTaxiRoute FGGroundNetwork::findShortestRoute(FGTaxiNode* start, FGTaxiNode* end, bool fullSearch)
314 if (!start || !end) {
315 throw sg_exception("Bad arguments to findShortestRoute");
317 //implements Dijkstra's algorithm to find shortest distance route from start to end
318 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
319 FGTaxiNodeVector unvisited(m_nodes);
320 std::map<FGTaxiNode*, ShortestPathData> searchData;
322 searchData[start].score = 0.0;
324 while (!unvisited.empty()) {
325 // find lowest scored unvisited
326 FGTaxiNodeRef best = unvisited.front();
327 BOOST_FOREACH(FGTaxiNodeRef i, unvisited) {
328 if (searchData[i].score < searchData[best].score) {
333 // remove 'best' from the unvisited set
334 FGTaxiNodeVectorIterator newend =
335 remove(unvisited.begin(), unvisited.end(), best);
336 unvisited.erase(newend, unvisited.end());
338 if (best == end) { // found route or best not connected
342 BOOST_FOREACH(FGTaxiNodeRef target, segmentsFrom(best)) {
343 double edgeLength = dist(best->cart(), target->cart());
344 double alt = searchData[best].score + edgeLength + edgePenalty(target);
345 if (alt < searchData[target].score) { // Relax (u,v)
346 searchData[target].score = alt;
347 searchData[target].previousNode = best;
349 } // of outgoing arcs/segments from current best node iteration
350 } // of unvisited nodes remaining
352 if (searchData[end].score == HUGE_VAL) {
353 // no valid route found
355 SG_LOG(SG_GENERAL, SG_ALERT,
356 "Failed to find route from waypoint " << start << " to "
357 << end << " at " << parent->getId());
360 return FGTaxiRoute();
363 // assemble route from backtrace information
364 FGTaxiNodeVector nodes;
366 FGTaxiNode *bt = end;
368 while (searchData[bt].previousNode != 0) {
370 FGTaxiSegment *segment = findSegment(searchData[bt].previousNode, bt);
371 int idx = segment->getIndex();
372 routes.push_back(idx);
373 bt = searchData[bt].previousNode;
376 nodes.push_back(start);
377 reverse(nodes.begin(), nodes.end());
378 reverse(routes.begin(), routes.end());
379 return FGTaxiRoute(nodes, routes, searchData[end].score, 0);
382 void FGGroundNetwork::unblockAllSegments(time_t now)
384 FGTaxiSegmentVector::iterator tsi;
385 for ( tsi = segments.begin(); tsi != segments.end(); tsi++) {
386 (*tsi)->unblock(now);
390 void FGGroundNetwork::blockSegmentsEndingAt(FGTaxiSegment *seg, int blockId, time_t blockTime, time_t now)
393 throw sg_exception("Passed invalid segment");
395 FGTaxiNode *node = seg->getEnd();
396 FGTaxiSegmentVector::iterator tsi;
397 for ( tsi = segments.begin(); tsi != segments.end(); tsi++) {
398 FGTaxiSegment* otherSegment = *tsi;
399 if ((otherSegment->getEnd() == node) && (otherSegment != seg)) {
400 otherSegment->block(blockId, blockTime, now);
405 FGTaxiNodeRef FGGroundNetwork::findNodeByIndex(int index) const
407 FGTaxiNodeVector::const_iterator it;
408 for (it = m_nodes.begin(); it != m_nodes.end(); ++it) {
409 if ((*it)->getIndex() == index) {
414 return FGTaxiNodeRef();
417 void FGGroundNetwork::addSegment(const FGTaxiNodeRef &from, const FGTaxiNodeRef &to)
419 FGTaxiSegment* seg = new FGTaxiSegment(from, to);
420 segments.push_back(seg);
422 FGTaxiNodeVector::iterator it = std::find(m_nodes.begin(), m_nodes.end(), from);
423 if (it == m_nodes.end()) {
424 m_nodes.push_back(from);
427 it = std::find(m_nodes.begin(), m_nodes.end(), to);
428 if (it == m_nodes.end()) {
429 m_nodes.push_back(to);
433 void FGGroundNetwork::addParking(const FGParkingRef &park)
435 m_parkings.push_back(park);
438 FGTaxiNodeVector::iterator it = std::find(m_nodes.begin(), m_nodes.end(), park);
439 if (it == m_nodes.end()) {
440 m_nodes.push_back(park);
444 FGTaxiNodeVector FGGroundNetwork::segmentsFrom(const FGTaxiNodeRef &from) const
446 FGTaxiNodeVector result;
447 FGTaxiSegmentVector::const_iterator it;
448 for (it = segments.begin(); it != segments.end(); ++it) {
449 if ((*it)->getStart() == from) {
450 result.push_back((*it)->getEnd());