]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/groundnetwork.cxx
Bugfix: Retrieval of the next radio frequency was broken. Contributed by Adrian Musceac.
[flightgear.git] / src / Airports / groundnetwork.cxx
index 0470e4af92ef9a28663887cd57e42fc90d404924..3a3c6d7144f37790aa162d7a8cd265685786112d 100644 (file)
 #include <math.h>
 #include <algorithm>
 
+
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/MatrixTransform>
+#include <osg/Shape>
+
 #include <simgear/debug/logstream.hxx>
 #include <simgear/route/waypoint.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/material/matlib.hxx>
+#include <simgear/scene/material/mat.hxx>
 
 #include <Airports/simple.hxx>
 #include <Airports/dynamics.hxx>
 #include <AIModel/AIAircraft.hxx>
 #include <AIModel/AIFlightPlan.hxx>
 
+#include <ATC/atc_mgr.hxx>
+
+#include <Scenery/scenery.hxx>
+
 #include "groundnetwork.hxx"
 
 /***************************************************************************
@@ -77,19 +90,28 @@ void FGTaxiSegment::setEnd(FGTaxiNodeVector * nodes)
 
 // There is probably a computationally cheaper way of 
 // doing this.
-void FGTaxiSegment::setTrackDistance()
+void FGTaxiSegment::setDimensions(double elevation)
 {
     length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
+    //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
+
+    double az2; //, distanceM;
+    SGGeodesy::inverse(start->getGeod(), end->getGeod(), heading, az2, length);
+    double coveredDistance = length * 0.5;
+    SGGeodesy::direct(start->getGeod(), heading, coveredDistance, center, az2);
+    //start->setElevation(elevation);
+    //end->setElevation(elevation);
+    //cerr << "Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
 }
 
 
-void FGTaxiSegment::setCourseDiff(double crse)
-{
-    headingDiff = fabs(course - crse);
+//void FGTaxiSegment::setCourseDiff(double crse)
+//{
+//    headingDiff = fabs(course - crse);
 
-    if (headingDiff > 180)
-        headingDiff = fabs(headingDiff - 360);
-}
+//    if (headingDiff > 180)
+//        headingDiff = fabs(headingDiff - 360);
+//}
 
 
 /***************************************************************************
@@ -152,6 +174,7 @@ bool FGTaxiRoute::next(int *nde, int *rte)
     return true;
 };
 
+
 void FGTaxiRoute::rewind(int route)
 {
     int currPoint;
@@ -191,13 +214,42 @@ FGGroundNetwork::FGGroundNetwork()
     //maxDepth    = 1000;
     count = 0;
     currTraffic = activeTraffic.begin();
+    group = 0;
+    networkInitialized = false;
 
 }
 
 FGGroundNetwork::~FGGroundNetwork()
 {
+    //cerr << "Running Groundnetwork Destructor " << endl;
+    bool saveData = false;
+    ofstream cachefile;
+    if (fgGetBool("/sim/ai/groundnet-cache")) {
+        SGPath cacheData(fgGetString("/sim/fg-home"));
+        cacheData.append("ai");
+        string airport = parent->getId();
+
+        if ((airport) != "") {
+            char buffer[128];
+            ::snprintf(buffer, 128, "%c/%c/%c/",
+                       airport[0], airport[1], airport[2]);
+            cacheData.append(buffer);
+            if (!cacheData.exists()) {
+                cacheData.create_dir(0777);
+            }
+            cacheData.append(airport + "-groundnet-cache.txt");
+            cachefile.open(cacheData.str().c_str());
+            saveData = true;
+        }
+    }
+    cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
     for (FGTaxiNodeVectorIterator node = nodes.begin();
          node != nodes.end(); node++) {
+         if (saveData) {
+            cachefile << (*node)->getIndex     () << " "
+                      << (*node)->getElevation ()   << " " 
+                      << endl;
+         }
         delete(*node);
     }
     nodes.clear();
@@ -207,6 +259,45 @@ FGGroundNetwork::~FGGroundNetwork()
         delete(*seg);
     }
     segments.clear();
+    if (saveData) {
+        cachefile.close();
+    }
+}
+
+void FGGroundNetwork::saveElevationCache() {
+    //cerr << "Running Groundnetwork Destructor " << endl;
+    bool saveData = false;
+    ofstream cachefile;
+    if (fgGetBool("/sim/ai/groundnet-cache")) {
+        SGPath cacheData(fgGetString("/sim/fg-home"));
+        cacheData.append("ai");
+        string airport = parent->getId();
+
+        if ((airport) != "") {
+            char buffer[128];
+            ::snprintf(buffer, 128, "%c/%c/%c/",
+                       airport[0], airport[1], airport[2]);
+            cacheData.append(buffer);
+            if (!cacheData.exists()) {
+                cacheData.create_dir(0777);
+            }
+            cacheData.append(airport + "-groundnet-cache.txt");
+            cachefile.open(cacheData.str().c_str());
+            saveData = true;
+        }
+    }
+    cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
+    for (FGTaxiNodeVectorIterator node = nodes.begin();
+         node != nodes.end(); node++) {
+         if (saveData) {
+            cachefile << (*node)->getIndex     () << " "
+                      << (*node)->getElevation () << " " 
+                      << endl;
+         }
+    }
+    if (saveData) {
+        cachefile.close();
+    }
 }
 
 void FGGroundNetwork::addSegment(const FGTaxiSegment & seg)
@@ -227,6 +318,7 @@ void FGGroundNetwork::addNodes(FGParkingVec * parkings)
         n.setIndex(i->getIndex());
         n.setLatitude(i->getLatitude());
         n.setLongitude(i->getLongitude());
+        n.setElevation(parent->getElevation());
         nodes.push_back(new FGTaxiNode(n));
 
         i++;
@@ -237,7 +329,13 @@ void FGGroundNetwork::addNodes(FGParkingVec * parkings)
 
 void FGGroundNetwork::init()
 {
+    if (networkInitialized) {
+        FGATCController::init();
+        //cerr << "FGground network already initialized" << endl;
+        return;
+    }
     hasNetwork = true;
+    nextSave = 0;
     int index = 1;
     sort(nodes.begin(), nodes.end(), compare_nodes);
     //sort(segments.begin(), segments.end(), compare_segments());
@@ -245,7 +343,7 @@ void FGGroundNetwork::init()
     while (i != segments.end()) {
         (*i)->setStart(&nodes);
         (*i)->setEnd(&nodes);
-        (*i)->setTrackDistance();
+        (*i)->setDimensions(parent->getElevation() * SG_FEET_TO_METER);
         (*i)->setIndex(index);
         if ((*i)->isPushBack()) {
             pushBackNodes.push_back((*i)->getEnd());
@@ -286,6 +384,49 @@ void FGGroundNetwork::init()
     //}
     //cerr << "Done initializing ground network" << endl;
     //exit(1);
+    if (fgGetBool("/sim/ai/groundnet-cache")) {
+        SGPath cacheData(fgGetString("/sim/fg-home"));
+        cacheData.append("ai");
+        string airport = parent->getId();
+
+        if ((airport) != "") {
+            char buffer[128];
+            ::snprintf(buffer, 128, "%c/%c/%c/",
+                       airport[0], airport[1], airport[2]);
+            cacheData.append(buffer);
+            if (!cacheData.exists()) {
+                cacheData.create_dir(0777);
+            }
+            int index;
+            double elev;
+            cacheData.append(airport + "-groundnet-cache.txt");
+            if (cacheData.exists()) {
+                ifstream data(cacheData.c_str());
+                string revisionStr;
+                data >> revisionStr;
+                if (revisionStr != "[GroundNetcachedata:ref:2011:09:04]") {
+                    SG_LOG(SG_GENERAL, SG_ALERT,"GroundNetwork Warning: discarding outdated cachefile " << 
+                            cacheData.c_str() << " for Airport " << airport);
+                } else {
+                    for (FGTaxiNodeVectorIterator i = nodes.begin();
+                        i != nodes.end(); 
+                        i++) {
+                        (*i)->setElevation(parent->getElevation() * SG_FEET_TO_METER);
+                        data >> index >> elev;
+                        if (data.eof())
+                            break;
+                        if (index != (*i)->getIndex()) {
+                            SG_LOG(SG_GENERAL, SG_ALERT, "Index read from ground network cache at airport " << airport << " does not match index in the network itself");
+                        } else {
+                            (*i)->setElevation(elev);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    //cerr << "Finished initializing " << parent->getId() << " groundnetwork " << endl;
+    networkInitialized = true;
 }
 
 int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
@@ -489,6 +630,7 @@ void FGGroundNetwork::announcePosition(int id,
     }
 }
 
+
 void FGGroundNetwork::signOff(int id)
 {
     TrafficVectorIterator i = activeTraffic.begin();
@@ -510,11 +652,58 @@ void FGGroundNetwork::signOff(int id)
         i = activeTraffic.erase(i);
     }
 }
+/**
+ * The ground network can deal with the following states:
+ * 0 =  Normal; no action required
+ * 1 = "Acknowledge "Hold position
+ * 2 = "Acknowledge "Resume taxi".
+ * 3 = "Issue TaxiClearance"
+ * 4 = Acknowledge Taxi Clearance"
+ * 5 = Post acknowlegde taxiclearance: Start taxiing
+ * 6 = Report runway
+ * 7 = Acknowledge report runway
+ * 8 = Switch tower frequency
+ * 9 = Acknowledge switch tower frequency
+ *************************************************************************************************************************/
+bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId,
+                               AtcMsgDir msgDir)
+{
+    int state = i->getState();
+    if ((state >= minState) && (state <= maxState) && available) {
+        if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
+            //cerr << "Checking state " << state << " for " << i->getAircraft()->getCallSign() << endl;
+            static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
+            int n = trans_num->getIntValue();
+            if (n == 0) {
+                trans_num->setIntValue(-1);
+                 // PopupCallback(n);
+                 //cerr << "Selected transmission message " << n << endl;
+                 FGATCManager *atc = (FGATCManager*) globals->get_subsystem("atc");
+                 FGATCDialogNew::instance()->removeEntry(1);
+            } else {
+                //cerr << "creating message for " << i->getAircraft()->getCallSign() << endl;
+                transmit(&(*i), msgId, msgDir, false);
+                return false;
+            }
+        }
+        transmit(&(*i), msgId, msgDir, true);
+        i->updateState();
+        lastTransmission = now;
+        available = false;
+        return true;
+    }
+    return false;
+}
 
 void FGGroundNetwork::updateAircraftInformation(int id, double lat, double lon,
                                                 double heading, double speed, double alt,
                                                 double dt)
 {
+    time_t currentTime = time(NULL);
+    if (nextSave < currentTime) {
+        saveElevationCache();
+        nextSave = currentTime + 100 + rand() % 200;
+    }
     // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to 
     // Transmit air-to-ground "Ready to taxi request:
     // Transmit ground to air approval / hold
@@ -568,33 +757,23 @@ void FGGroundNetwork::updateAircraftInformation(int id, double lat, double lon,
         if ((now - lastTransmission) > 15) {
             available = true;
         }
-        if ((state < 3) && available) {
-             transmit(&(*current), MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND);
-             current->setState(3);
-             lastTransmission = now;
-             available = false;
+        if (checkTransmissionState(0,2, current, now, MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
+            current->setState(3);
         }
-        if ((state == 3) && available) {
-            transmit(&(*current), MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR);
+        if (checkTransmissionState(3,3, current, now, MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR)) {
             current->setState(4);
-            lastTransmission = now;
-            available = false;
         }
-        if ((state == 4) && available) {
-            transmit(&(*current), MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND);
+        if (checkTransmissionState(4,4, current, now, MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) {
             current->setState(5);
-            lastTransmission = now;
-            available = false;
         }
         if ((state == 5) && available) {
             current->setState(0);
             current->getAircraft()->setTaxiClearanceRequest(false);
-            current->setHoldPosition(true);
+            current->setHoldPosition(false);
             available = false;
         }
 
     }
-
 }
 
 /**
@@ -678,6 +857,10 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
                 if (bearing > 180)
                     bearing = 360 - bearing;
                 if ((dist < mindist) && (bearing < 60.0)) {
+                    //cerr << "Current aircraft " << current->getAircraft()->getTrafficRef()->getCallSign()
+                    //     << " is closest to " << i->getAircraft()->getTrafficRef()->getCallSign() 
+                    //     << ", which has status " << i->getAircraft()->isScheduledForTakeoff() 
+                    //     << endl;
                     mindist = dist;
                     closest = i;
                     minbearing = bearing;
@@ -686,6 +869,8 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
             }
         }
         // Finally, check UserPosition
+        // Note, as of 2011-08-01, this should no longer be necessecary.
+        /*
         double userLatitude = fgGetDouble("/position/latitude-deg");
         double userLongitude = fgGetDouble("/position/longitude-deg");
         SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
@@ -700,7 +885,7 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
             minbearing = bearing;
             otherReasonToSlowDown = true;
         }
-
+        */
         current->clearSpeedAdjustment();
 
         if (current->checkPositionAndIntentions(*closest)
@@ -713,11 +898,18 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
                     return;
                 else
                     current->setWaitsForId(closest->getId());
-                if (closest->getId() != current->getId())
+                if (closest->getId() != current->getId()) {
                     current->setSpeedAdjustment(closest->getSpeed() *
                                                 (mindist / 100));
-                else
+                    if ( 
+                        closest->getAircraft()->getTakeOffStatus() && 
+                        (current->getAircraft()->getTrafficRef()->getDepartureAirport() ==  closest->getAircraft()->getTrafficRef()->getDepartureAirport()) &&
+                        (current->getAircraft()->GetFlightPlan()->getRunway() == closest->getAircraft()->GetFlightPlan()->getRunway())
+                       )
+                            current->getAircraft()->scheduleForATCTowerDepartureControl(1); 
+                } else {
                     current->setSpeedAdjustment(0);     // This can only happen when the user aircraft is the one closest
+                }
                 if (mindist < maxAllowableDistance) {
                     //double newSpeed = (maxAllowableDistance-mindist);
                     //current->setSpeedAdjustment(newSpeed);
@@ -818,8 +1010,10 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat,
                         //(!(current->getSpeedAdjustment())))
 
                     {
-                        current->setHoldPosition(true);
-                        current->setWaitsForId(i->getId());
+                        if (!(isUserAircraft(i->getAircraft()))) { // test code. Don't wait for the user, let the user wait for you.
+                            current->setHoldPosition(true);
+                            current->setWaitsForId(i->getId());
+                        }
                         //cerr << "Hold check 5: " << current->getCallSign() <<"  Setting Hold Position: distance to node ("  << node << ") "
                         //           << dist << " meters. Waiting for " << i->getCallSign();
                         //if (opposing)
@@ -837,50 +1031,63 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat,
         }
     }
     bool currStatus = current->hasHoldPosition();
-
+    current->setHoldPosition(origStatus);
     // Either a Hold Position or a resume taxi transmission has been issued
     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
     if ((now - lastTransmission) > 2) {
         available = true;
     }
-    if ((origStatus != currStatus) && available) {
-        //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
-        if (currStatus == true) { // No has a hold short instruction
-           transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR);
-           //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
-           current->setState(1);
-        } else {
-           transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR);
-           //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
-           current->setState(2);
+    if (current->getState() == 0) {
+        if ((origStatus != currStatus) && available) {
+            //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
+            if (currStatus == true) { // No has a hold short instruction
+                transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true);
+                //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
+                current->setState(1);
+            } else {
+                transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true);
+                //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
+                current->setState(2);
+            }
+            lastTransmission = now;
+            available = false;
+            // Don't act on the changed instruction until the transmission is confirmed
+            // So set back to original status
+            //cerr << "Current state " << current->getState() << endl;
         }
-        lastTransmission = now;
-        available = false;
-        // Don't act on the changed instruction until the transmission is confirmed
-        // So set back to original status
-        current->setHoldPosition(origStatus);
-        //cerr << "Current state " << current->getState() << endl;
-    } else {
+
     }
-    int state = current->getState();
-    if ((state == 1) && (available)) {
-        //cerr << "ACKNOWLEDGE HOLD" << endl;
-        transmit(&(*current), MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND);
-        current->setState(0);
-        current->setHoldPosition(true);
-        lastTransmission = now;
-        available = false;
+    // 6 = Report runway
+    // 7 = Acknowledge report runway
+    // 8 = Switch tower frequency
+    //9 = Acknowledge switch tower frequency
 
+    //int state = current->getState();
+    if (checkTransmissionState(1,1, current, now, MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND)) {
+            current->setState(0);
+            current->setHoldPosition(true);
     }
-    if ((state == 2) && (available)) {
-        //cerr << "ACKNOWLEDGE RESUME" << endl;
-        transmit(&(*current), MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND);
-        current->setState(0);
-        current->setHoldPosition(false);
-        lastTransmission = now;
-        available = false;
+    if (checkTransmissionState(2,2, current, now, MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND)) {
+            current->setState(0);
+            current->setHoldPosition(false);
     }
-}
+    if (current->getAircraft()->getTakeOffStatus() && (current->getState() == 0)) {
+            //cerr << "Scheduling " << current->getAircraft()->getCallSign() << " for hold short" << endl;
+            current->setState(6);
+        }
+    if (checkTransmissionState(6,6, current, now, MSG_REPORT_RUNWAY_HOLD_SHORT, ATC_AIR_TO_GROUND)) {
+    }
+    if (checkTransmissionState(7,7, current, now, MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, ATC_GROUND_TO_AIR)) {
+    }
+    if (checkTransmissionState(8,8, current, now, MSG_SWITCH_TOWER_FREQUENCY, ATC_GROUND_TO_AIR)) {
+    }
+    if (checkTransmissionState(9,9, current, now, MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, ATC_AIR_TO_GROUND)) {
+    }
+
+
+
+            //current->setState(0);
+} 
 
 /**
  * Check whether situations occur where the current aircraft is waiting for itself
@@ -1036,3 +1243,197 @@ FGATCInstruction FGGroundNetwork::getInstruction(int id)
     }
     return FGATCInstruction();
 }
+
+// Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
+static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
+                            double lon, double elev, double hdg, double slope)
+{
+    SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
+    obj_pos = geod.makeZUpFrame();
+    // hdg is not a compass heading, but a counter-clockwise rotation
+    // around the Z axis
+    obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
+                                        0.0, 0.0, 1.0));
+    obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
+                                        0.0, 1.0, 0.0));
+}
+
+
+
+
+void FGGroundNetwork::render(bool visible)
+{
+
+    SGMaterialLib *matlib = globals->get_matlib();
+    if (group) {
+        //int nr = ;
+        globals->get_scenery()->get_scene_graph()->removeChild(group);
+        //while (group->getNumChildren()) {
+        //  cerr << "Number of children: " << group->getNumChildren() << endl;
+        //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
+          //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
+           //geode->releaseGLObjects();
+           //group->removeChild(geode);
+           //delete geode;
+        group = 0;
+    }
+    if (visible) {
+        group = new osg::Group;
+        FGScenery * local_scenery = globals->get_scenery();
+        double elevation_meters = 0.0;
+        double elevation_feet = 0.0;
+        //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
+        double dx = 0;
+        for   (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
+            // Handle start point
+            int pos = i->getCurrentPosition() - 1;
+            if (pos >= 0) {
+            
+                SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
+                SGGeod end  (SGGeod::fromDeg(segments[pos]->getEnd()->getLongitude(), segments[pos]->getEnd()->getLatitude()));
+
+                double length = SGGeodesy::distanceM(start, end);
+                //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
+
+                double az2, heading; //, distanceM;
+                SGGeodesy::inverse(start, end, heading, az2, length);
+                double coveredDistance = length * 0.5;
+                SGGeod center;
+                SGGeodesy::direct(start, heading, coveredDistance, center, az2);
+                //cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
+            ///////////////////////////////////////////////////////////////////////////////
+                // Make a helper function out of this
+                osg::Matrix obj_pos;
+                osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
+                obj_trans->setDataVariance(osg::Object::STATIC);
+                // Experimental: Calculate slope here, based on length, and the individual elevations
+                double elevationStart;
+                if (isUserAircraft((i)->getAircraft())) {
+                    elevationStart = fgGetDouble("/position/ground-elev-m");
+                } else {
+                    elevationStart = ((i)->getAircraft()->_getAltitude()); 
+                }
+                double elevationEnd   = segments[pos]->getEnd()->getElevation();
+                //cerr << "Using elevation " << elevationEnd << endl;
+
+                if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) {
+                    SGGeod center2 = end;
+                    center2.setElevationM(SG_MAX_ELEVATION_M);
+                    if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
+                        elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
+                            //elevation_meters += 0.5;
+                    }
+                    else { 
+                        elevationEnd = parent->getElevation();
+                    }
+                    segments[pos]->getEnd()->setElevation(elevationEnd);
+                }
+                double elevationMean  = (elevationStart + elevationEnd) / 2.0;
+                double elevDiff       = elevationEnd - elevationStart;
+               
+               double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
+                
+               //cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
+
+                WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean+ 0.5, -(heading), slope );
+
+                obj_trans->setMatrix( obj_pos );
+                //osg::Vec3 center(0, 0, 0)
+
+                float width = length /2.0;
+                osg::Vec3 corner(-width, 0, 0.25f);
+                osg::Vec3 widthVec(2*width + 1, 0, 0);
+                osg::Vec3 heightVec(0, 1, 0);
+                osg::Geometry* geometry;
+                geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
+                simgear::EffectGeode* geode = new simgear::EffectGeode;
+                geode->setName("test");
+                geode->addDrawable(geometry);
+                //osg::Node *custom_obj;
+                SGMaterial *mat = matlib->find("UnidirectionalTaper");
+                if (mat)
+                    geode->setEffect(mat->get_effect());
+                obj_trans->addChild(geode);
+                // wire as much of the scene graph together as we can
+                //->addChild( obj_trans );
+                group->addChild( obj_trans );
+            /////////////////////////////////////////////////////////////////////
+            } else {
+                //cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
+            }
+            for(intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
+                osg::Matrix obj_pos;
+                int k = (*j)-1;
+                if (k >= 0) {
+                    osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
+                    obj_trans->setDataVariance(osg::Object::STATIC);
+
+                    // Experimental: Calculate slope here, based on length, and the individual elevations
+                    double elevationStart = segments[k]->getStart()->getElevation();
+                    double elevationEnd   = segments[k]->getEnd  ()->getElevation();
+                    if ((elevationStart == 0)  || (elevationStart == parent->getElevation())) {
+                        SGGeod center2 = segments[k]->getStart()->getGeod();
+                        center2.setElevationM(SG_MAX_ELEVATION_M);
+                        if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
+                            elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
+                            //elevation_meters += 0.5;
+                        }
+                        else { 
+                            elevationStart = parent->getElevation();
+                        }
+                        segments[k]->getStart()->setElevation(elevationStart);
+                    }
+                    if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
+                        SGGeod center2 = segments[k]->getEnd()->getGeod();
+                        center2.setElevationM(SG_MAX_ELEVATION_M);
+                        if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
+                            elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
+                            //elevation_meters += 0.5;
+                        }
+                        else { 
+                            elevationEnd = parent->getElevation();
+                        }
+                        segments[k]->getEnd()->setElevation(elevationEnd);
+                    }
+
+                    double elevationMean  = (elevationStart + elevationEnd) / 2.0;
+                    double elevDiff       = elevationEnd - elevationStart;
+                    double length         = segments[k]->getLength();
+                    double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
+                
+                   // cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
+
+
+                    WorldCoordinate( obj_pos, segments[k]->getLatitude(), segments[k]->getLongitude(), elevationMean+ 0.5, -(segments[k]->getHeading()), slope );
+
+                    obj_trans->setMatrix( obj_pos );
+                    //osg::Vec3 center(0, 0, 0)
+
+                    float width = segments[k]->getLength() /2.0;
+                    osg::Vec3 corner(-width, 0, 0.25f);
+                    osg::Vec3 widthVec(2*width + 1, 0, 0);
+                    osg::Vec3 heightVec(0, 1, 0);
+                    osg::Geometry* geometry;
+                    geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
+                    simgear::EffectGeode* geode = new simgear::EffectGeode;
+                    geode->setName("test");
+                    geode->addDrawable(geometry);
+                    //osg::Node *custom_obj;
+                    SGMaterial *mat = matlib->find("UnidirectionalTaper");
+                    if (mat)
+                        geode->setEffect(mat->get_effect());
+                    obj_trans->addChild(geode);
+                    // wire as much of the scene graph together as we can
+                    //->addChild( obj_trans );
+                    group->addChild( obj_trans );
+                }
+            }
+            //dx += 0.1;
+        }
+        globals->get_scenery()->get_scene_graph()->addChild(group);
+    }
+}
+
+string FGGroundNetwork::getName() {
+    return string(parent->getId() + "-ground");
+}