diff --git a/src/Airports/AirportBuilder.cxx b/src/Airports/AirportBuilder.cxx new file mode 100644 index 000000000..6d897ec84 --- /dev/null +++ b/src/Airports/AirportBuilder.cxx @@ -0,0 +1,569 @@ +// AirportBuilder.hxx -- Builder to create airports based on airport data for +// rendering in the scenery +// +// Written by Stuart Buchanan, started June 2020 +// +// Copyright (C) 2020 Stuart Buchanan stuart13@gmail.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include + +#include +#include +#include +#include "AirportBuilder.hxx" + +#include +#include + +#include +#include +#include "simgear/scene/model/ModelRegistry.hxx" +#include "simgear/scene/util/OsgMath.hxx" +#include "simgear/math/SGGeodesy.hxx" +#include "airport.hxx" +#include +#include "runways.hxx" +#include "pavement.hxx" + +namespace flightgear +{ + +AirportBuilder::AirportBuilder() +{ + supportsExtension("icao", "Dummy name to build an airport from apt.dat"); +} + +AirportBuilder::~AirportBuilder() +{ +} + +const char* AirportBuilder::className() const +{ + return "Airport Builder"; +} + +osgDB::ReaderWriter::ReadResult +AirportBuilder::readNode(const std::string& fileName, + const osgDB::Options* options) const +{ + const SGPath aptFile = SGPath(fileName); + const string airportId = aptFile.file_base(); + + APTLoader aptLoader; + + const FGAirport* airport = aptLoader.loadAirportFromFile(airportId, aptFile); + if (! airport) return ReadResult::FILE_NOT_HANDLED;; + + std::cout << "AirportBuilder, building airport : " << airportId << " " << airport->getName() << "\n"; + std::cout << "Elevation : " << airport->getElevation() << "ft\n"; + std::cout << "Latitude : " << airport->getLatitude() << "ft\n"; + std::cout << "Longitude : " << airport->getLongitude() << "ft\n"; + + std::cout << "Runways : " << airport->numRunways() << "\n"; + std::cout << "Helipads : " << airport->numHelipads() << "\n"; + std::cout << "Taxiways : " << airport->numTaxiways() << "\n"; + std::cout << "Pavements : " << airport->numPavements() << "\n"; + std::cout << "Line Features : " << airport->numLineFeatures() << "\n"; + + const SGVec3f center = SGVec3f::fromGeod(airport->geod()); + const SGVec3f offset = SGVec3f::fromGeod(SGGeod::fromGeodM(airport->geod(), airport->geod().getElevationM() + 1.0)); + const osg::Vec3f up = osg::Vec3f(toOsg(offset - center)); + + + std::vector nodeList; + osg::Group* group = new osg::Group(); + + // Build the various components + auto runwaylist = airport->getRunways(); + std::for_each( runwaylist.begin(), + runwaylist.end(), + [&, center] (FGRunwayRef p) { group->addChild(this->createRunway(center, up, p)); } ); + + + // Build the pavements + auto pavementlist = airport->getPavements(); + std::for_each( pavementlist.begin(), + pavementlist.end(), + [&, center, up] (FGPavementRef p) { group->addChild(this->createPavement(center, up, p)); } ); + + // Build the boundary + auto boundaryList = airport->getBoundary(); + std::for_each( boundaryList.begin(), + boundaryList.end(), + [&, center] (FGPavementRef p) { group->addChild(this->createBoundary(center, up, p)); } ); + + // Build line features + auto lineList = airport->getLineFeatures(); + std::for_each( lineList.begin(), + lineList.end(), + [&, center] (FGPavementRef p) { group->addChild(this->createLine(center, up, p)); } ); + + return group; +} + +osg::Node* AirportBuilder::createRunway(const SGVec3f center, const osg::Vec3f up, const FGRunwayRef runway) const +{ + std::vector nodes; + nodes.push_back(runway->pointOffCenterline(0.0, -0.5 * runway->widthM())); + nodes.push_back(runway->pointOffCenterline(0.0, 0.5 * runway->widthM())); + nodes.push_back(runway->pointOffCenterline(runway->lengthM(), 0.5 * runway->widthM())); + nodes.push_back(runway->pointOffCenterline(runway->lengthM(), -0.5 * runway->widthM())); + + osg::Vec3Array* points = new osg::Vec3Array; + points->reserve(nodes.size()); + std::transform( nodes.begin(), + nodes.end(), + std::back_inserter(*points), + [center](SGGeod n) { return toOsg(SGVec3f::fromGeod(SGGeod::fromGeodM(n, n.getElevationM() + RUNWAY_OFFSET)) - center); } + ); + + osg::ref_ptr triangulator = new osgUtil::DelaunayTriangulator; + triangulator->setInputPointArray(points); + triangulator->triangulate(); + + osg::ref_ptr geometry = new osg::Geometry; + geometry->setVertexArray(points); + geometry->addPrimitiveSet(triangulator->getTriangles()); // triangles with constraint cut + + osg::Vec3Array* n = new osg::Vec3Array; + n->push_back(up); + + osg::Vec4Array* c = new osg::Vec4Array; + c->push_back(osg::Vec4f(0.2f,0.2f,0.2f,1.0f)); + + geometry->setColorArray(c, osg::Array::BIND_OVERALL); + geometry->setNormalArray(n, osg::Array::BIND_OVERALL); + + simgear::EffectGeode* geode = new simgear::EffectGeode; + geode->addDrawable(geometry); + //geode->setEffect(_effect.get()); + + return geode; +} + +osg::Node* AirportBuilder::createPavement(const SGVec3f center, const osg::Vec3f up, const FGPavementRef pavement) const +{ + const FGPavement::NodeList nodes = pavement->getNodeList(); + + if (nodes.size() == 0) return NULL; + + osg::ref_ptr tessellator = new osgUtil::Tessellator; + tessellator->setBoundaryOnly(false); + tessellator->beginTessellation(); + tessellator->beginContour(); + + // Track the previous vertex for bezier curve generation. + osg::Vec3f* previous; + + // Bezier control. Bezier curve is node n-1 -> n -> n+1 so need to store for + // handling the next node in addition to generating for the BezierNode itself. + bool bezier = false; + osg::Vec3f control; + + auto itr = nodes.begin(); + for (; itr < nodes.end(); ++itr) { + FGPavement::NodeBase* node = *itr; + + // Create an offset so we can easily layer the boundary, pavement, lights and runways + SGGeod offset = SGGeod::fromGeodM(node->mPos, node->mPos.getElevationM() + PAVEMENT_OFFSET); + osg::Vec3f* v = new osg::Vec3f(toOsg(SGVec3f::fromGeod(offset) - center)); + + if (FGPavement::BezierNode *bez = dynamic_cast(node)) { + // Store the bezier control node for generation when we know the next node. + SGGeod controlGeod = SGGeod::fromGeodM(bez->mControl, bez->mControl.getElevationM() + PAVEMENT_OFFSET); + control = osg::Vec3f(toOsg(SGVec3f::fromGeod(controlGeod) - center)); + bezier = true; + + // Generate from the last node to this one, reflecting the control point + for (float t = 0.05f; t < 0.95f; t += 0.05f ) { + osg::Vec3f p0 = *previous; + osg::Vec3f p1 = *v + *v - control; + osg::Vec3f p2 = *v; + + osg::Vec3f* b = new osg::Vec3f(p1 + + (p0 - p1)*(1.0f - t)*(1.0f - t) + + (p2 - p1)*t*t); + + tessellator->addVertex(b); + } + + tessellator->addVertex(v); + + } else if (bezier) { + // Last node was a BezierNode, so generate the required bezier curve to this node. + for (float t = 0.05f; t < 0.95f; t += 0.05f ) { + osg::Vec3f p0 = *previous; + osg::Vec3f p1 = control; + osg::Vec3f p2 = *v; + + osg::Vec3f* b = new osg::Vec3f(p1 + + (p0 - p1)*(1.0f - t)*(1.0f - t) + + (p2 - p1)*t*t); + + tessellator->addVertex(b); + } + + bezier = false; + tessellator->addVertex(v); + } else { + // SimpleNode + tessellator->addVertex(v); + } + + + previous = v; + if (node->mClose) { + tessellator->endContour(); + tessellator->beginContour(); + } + } + + tessellator->endContour(); + tessellator->endTessellation(); + + // Build the geometry based on the tessellation results. + + osg::ref_ptr geometry = new osg::Geometry; + auto primList = tessellator->getPrimList(); + osg::Vec3Array* points = new osg::Vec3Array(); + geometry->setVertexArray(points); + unsigned int idx = 0; + auto primItr = primList.begin(); + for (; primItr < primList.end(); ++ primItr) { + auto vertices = (*primItr)->_vertices; + std::for_each( vertices.begin(), + vertices.end(), + [points](auto v) { points->push_back(*v); } + ); + + geometry->addPrimitiveSet(new osg::DrawArrays((*primItr)->_mode, idx, vertices.size())); + idx += vertices.size(); + } + + osg::Vec3Array* n = new osg::Vec3Array; + n->push_back(up); + + osg::Vec4Array* c = new osg::Vec4Array; + c->push_back(osg::Vec4f(0.5f, 0.5f, 0.5f, 1.0f)); + + geometry->setColorArray(c, osg::Array::BIND_OVERALL); + geometry->setNormalArray(n, osg::Array::BIND_OVERALL); + + simgear::EffectGeode* geode = new simgear::EffectGeode; + geode->addDrawable(geometry); + //geode->setEffect(_effect.get()); + + return geode; +} + +osg::Node* AirportBuilder::createBoundary(const SGVec3f center, const osg::Vec3f up, const FGPavementRef pavement) const +{ + const FGPavement::NodeList nodes = pavement->getNodeList(); + + if (nodes.size() == 0) return NULL; + + osg::ref_ptr tessellator = new osgUtil::Tessellator; + tessellator->setBoundaryOnly(false); + tessellator->beginTessellation(); + tessellator->beginContour(); + + // Track the previous vertex for bezier curve generation. + osg::Vec3f* previous; + + // Bezier control. Bezier curve is node n-1 -> n -> n+1 so need to store for + // handling the next node in addition to generating for the BezierNode itself. + bool bezier = false; + osg::Vec3f control; + + + auto itr = nodes.begin(); + for (; itr < nodes.end(); ++itr) { + FGPavement::NodeBase* node = *itr; + + // Create an offset so we can easily layer the boundary, pavement, lights and runways + SGGeod offset = SGGeod::fromGeodM(node->mPos, node->mPos.getElevationM() + BOUNDARY_OFFSET); + osg::Vec3f* v = new osg::Vec3f(toOsg(SGVec3f::fromGeod(offset) - center)); + + if (FGPavement::BezierNode *bez = dynamic_cast(node)) { + // Store the bezier control node for generation when we know the next node. + SGGeod controlGeod = SGGeod::fromGeodM(bez->mControl, bez->mControl.getElevationM() + BOUNDARY_OFFSET); + control = osg::Vec3f(toOsg(SGVec3f::fromGeod(controlGeod) - center)); + bezier = true; + + // Generate from the last node to this one, reflecting the control point + for (float t = 0.05f; t < 0.95f; t += 0.05f ) { + osg::Vec3f p0 = *previous; + osg::Vec3f p1 = *v + *v - control; + osg::Vec3f p2 = *v; + + osg::Vec3f* b = new osg::Vec3f(p1 + + (p0 - p1)*(1.0f - t)*(1.0f - t) + + (p2 - p1)*t*t); + + tessellator->addVertex(b); + } + + tessellator->addVertex(v); + + } else if (bezier) { + // Last node was a BezierNode, so generate the required bezier curve to this node. + for (float t = 0.05f; t < 0.95f; t += 0.05f ) { + osg::Vec3f p0 = *previous; + osg::Vec3f p1 = control; + osg::Vec3f p2 = *v; + + osg::Vec3f* b = new osg::Vec3f(p1 + + (p0 - p1)*(1.0f - t)*(1.0f - t) + + (p2 - p1)*t*t); + + tessellator->addVertex(b); + } + + bezier = false; + tessellator->addVertex(v); + } else { + // SimpleNode + tessellator->addVertex(v); + } + + previous = v; + if (node->mClose) { + tessellator->endContour(); + tessellator->beginContour(); + } + } + + tessellator->endContour(); + tessellator->endTessellation(); + + // Build the geometry based on the tessellation results. + osg::ref_ptr geometry = new osg::Geometry; + auto primList = tessellator->getPrimList(); + osg::Vec3Array* points = new osg::Vec3Array(); + geometry->setVertexArray(points); + unsigned int idx = 0; + auto primItr = primList.begin(); + for (; primItr < primList.end(); ++ primItr) { + auto vertices = (*primItr)->_vertices; + std::for_each( vertices.begin(), + vertices.end(), + [points](auto v) { points->push_back(*v); } + ); + + geometry->addPrimitiveSet(new osg::DrawArrays((*primItr)->_mode, idx, vertices.size())); + idx += vertices.size(); + } + + osg::Vec3Array* n = new osg::Vec3Array; + n->push_back(up); + + osg::Vec4Array* c = new osg::Vec4Array; + c->push_back(osg::Vec4f(0.3f, 0.7f, 0.3f, 1.0f)); + + geometry->setColorArray(c, osg::Array::BIND_OVERALL); + geometry->setNormalArray(n, osg::Array::BIND_OVERALL); + + simgear::EffectGeode* geode = new simgear::EffectGeode; + geode->addDrawable(geometry); + //geode->setEffect(_effect.get()); + + return geode; +} + +osg::Node* AirportBuilder::createLine(const SGVec3f center, const osg::Vec3f up, const FGPavementRef line) const +{ + const FGPavement::NodeList nodes = line->getNodeList(); + + if (nodes.size() == 0) return NULL; + + // Track the previous vertex for bezier curve generation. + osg::Vec3f previous; + + // Track along the vertex list to create the correct Drawables + unsigned int idx = 0; + unsigned int length = 0; + + // Track the last set line code + unsigned int paintCode = 0; + + unsigned int lines = 0; + + // Bezier control. Bezier curve is node n-1 -> n -> n+1 so need to store for + // handling the next node in addition to generating for the BezierNode itself. + bool bezier = false; + osg::Vec3f control; + + osg::ref_ptr geometry = new osg::Geometry; + osg::Vec3Array* points = new osg::Vec3Array(); + geometry->setVertexArray(points); + + osg::Vec3Array* n = new osg::Vec3Array; + n->push_back(up); + geometry->setNormalArray(n, osg::Array::BIND_OVERALL); + + osg::Vec4Array* c = new osg::Vec4Array; + geometry->setColorArray(c, osg::Array::BIND_PER_VERTEX); + + auto itr = nodes.begin(); + for (; itr < nodes.end(); ++itr) { + FGPavement::NodeBase* node = *itr; + + // Create an offset so we can easily layer the boundary, pavement, lights and runways + SGGeod offset = SGGeod::fromGeodM(node->mPos, node->mPos.getElevationM() + MARKING_OFFSET); + osg::Vec3f v = osg::Vec3f(toOsg(SGVec3f::fromGeod(offset) - center)); + + if (node->mPaintCode != 0) paintCode = node->mPaintCode; + + if (FGPavement::BezierNode *bez = dynamic_cast(node)) { + // Store the bezier control node for generation when we know the next node. + SGGeod controlGeod = SGGeod::fromGeodM(bez->mControl, bez->mControl.getElevationM() + MARKING_OFFSET); + control = osg::Vec3f(toOsg(SGVec3f::fromGeod(controlGeod) - center)); + bezier = true; + + // Generate from the last node to this one, reflecting the control point + for (float t = 0.05f; t < 0.95f; t += 0.05f ) { + osg::Vec3f p0 = previous; + osg::Vec3f p1 = v + v - control; + osg::Vec3f p2 = v; + + osg::Vec3f b = + osg::Vec3f(p1 + + (p0 - p1)*(1.0f - t)*(1.0f - t) + + (p2 - p1)*t*t); + + points->push_back(b); + c->push_back(getLineColor(paintCode)); + length += 1; + } + + points->push_back(v); + c->push_back(getLineColor(paintCode)); + length += 1; + + if (node->mClose && node->mLoop) { + // Special case if this is the end node and it loops back to the start. + // In this case there is not going to be a forward node to generate, so + // we need to generate the bezier curve now. + + for (float t = 0.05f; t < 0.95f; t += 0.05f ) { + osg::Vec3f p0 = v; + osg::Vec3f p1 = control; + osg::Vec3f p2 = points->at(idx); + + osg::Vec3f b = + osg::Vec3f(p1 + + (p0 - p1)*(1.0f - t)*(1.0f - t) + + (p2 - p1)*t*t); + + points->push_back(b); + c->push_back(getLineColor(paintCode)); + length += 1; + } + + bezier = false; + } + + } else if (bezier) { + // Last node was a BezierNode, so generate the required bezier curve to this node. + for (float t = 0.05f; t < 0.95f; t += 0.05f ) { + osg::Vec3f p0 = previous; + osg::Vec3f p1 = control; + osg::Vec3f p2 = v; + + osg::Vec3f b = + osg::Vec3f(p1 + + (p0 - p1)*(1.0f - t)*(1.0f - t) + + (p2 - p1)*t*t); + + points->push_back(b); + c->push_back(getLineColor(paintCode)); + length += 1; + } + + bezier = false; + } else { + // SimpleNode + points->push_back(v); + c->push_back(getLineColor(paintCode)); + length += 1; + } + + previous = v; + + if (node->mClose) { + geometry->addPrimitiveSet(new osg::DrawArrays( + node->mLoop ? GL_LINE_LOOP : GL_LINE_STRIP, + idx, + length)); + idx += length; + length = 0; + lines++; + } + } + + simgear::EffectGeode* geode = new simgear::EffectGeode; + geode->addDrawable(geometry); + //geode->setEffect(_effect.get()); + + return geode; +} + +osg::Vec4f AirportBuilder::getLineColor(const int aPaintCode) const { + if (aPaintCode == 1) return osg::Vec4f(0.7f, 0.7f, 0.3f, 1.0f); // Solid yellow + if (aPaintCode == 2) return osg::Vec4f(0.7f, 0.7f, 0.3f, 1.0f); // Broken yellow + if (aPaintCode == 3) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Double yellow + if (aPaintCode == 4) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Two broken yellow lines and two solid yellow lines. Broken line on left of string. + if (aPaintCode == 5) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Broken yellow line with parallel solid yellow line + if (aPaintCode == 6) return osg::Vec4f(0.5f, 0.5f, 0.0f, 1.0f); // Yellow cross-hatched + if (aPaintCode == 7) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Solid Yellow with broken yellow on each side + if (aPaintCode == 8) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Widely separated, broken yellow line + if (aPaintCode == 9) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Widely separated, broken double yellow line + + // As above with black border + if (aPaintCode == 51) return osg::Vec4f(0.7f, 0.7f, 0.3f, 1.0f); // Solid yellow + if (aPaintCode == 52) return osg::Vec4f(0.7f, 0.7f, 0.3f, 1.0f); // Broken yellow + if (aPaintCode == 53) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Double yellow + if (aPaintCode == 54) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Two broken yellow lines and two solid yellow lines. Broken line on left of string. + if (aPaintCode == 55) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Broken yellow line with parallel solid yellow line + if (aPaintCode == 56) return osg::Vec4f(0.5f, 0.5f, 0.0f, 1.0f); // Yellow cross-hatched + if (aPaintCode == 57) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Solid Yellow with broken yellow on each side + if (aPaintCode == 58) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Widely separated, broken yellow line + if (aPaintCode == 59) return osg::Vec4f(0.9f, 0.9f, 0.3f, 1.0f); // Widely separated, broken double yellow line + + if (aPaintCode == 20) return osg::Vec4f(0.8f, 0.8f, 0.8f, 1.0f); // Solid White + if (aPaintCode == 21) return osg::Vec4f(0.7f, 0.7f, 0.7f, 1.0f); // White chequerboard + if (aPaintCode == 22) return osg::Vec4f(0.6f, 0.6f, 0.6f, 1.0f); // Broken white line + + //std::cout << "Unknown line colour " << aPaintCode << "\n"; + return osg::Vec4f(0.7f, 0.7f, 0.3f, 1.0f); // Default (probably an error), so red + +} + + +} +typedef simgear::ModelRegistryCallback +AirportCallback; + +namespace +{ + osgDB::RegisterReaderWriterProxy g_readerAirportBuilder; + simgear::ModelRegistryCallbackProxy g_icaoCallbackProxy("icao"); +} diff --git a/src/Airports/AirportBuilder.hxx b/src/Airports/AirportBuilder.hxx new file mode 100644 index 000000000..c477912d7 --- /dev/null +++ b/src/Airports/AirportBuilder.hxx @@ -0,0 +1,59 @@ +// AirportBuilder.hxx -- Builder to create airports based on airport data for +// rendering in the scenery +// +// Written by Stuart Buchanan, started June 2020 +// +// Copyright (C) 2020 Stuart Buchanan stuart13@gmail.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ +#include +#include "airport.hxx" + +namespace flightgear +{ + + static const float RUNWAY_OFFSET = 3.0; + +class AirportBuilder : public osgDB::ReaderWriter { +public: + + // The different layers are offset to avoid z-buffering issues. As they + // are viewed from above only, this doesn't cause any problems visually. + const float MARKING_OFFSET = 2.0; + const float PAVEMENT_OFFSET = 1.0; + const float BOUNDARY_OFFSET = 0.0; + + AirportBuilder(); + virtual ~AirportBuilder(); + + virtual const char* className() const; + + virtual ReadResult readNode(const std::string& fileName, + const osgDB::Options* options) + const; + +private: + osg::Node* createAirport(const std::string airportId) const; + osg::Node* createRunway(const SGVec3f center, const osg::Vec3f up, const FGRunwayRef runway) const; + osg::Node* createPavement(const SGVec3f center, const osg::Vec3f up, const FGPavementRef pavement) const; + osg::Node* createBoundary(const SGVec3f center, const osg::Vec3f up, const FGPavementRef pavement) const; + osg::Node* createLine(const SGVec3f center, const osg::Vec3f up, const FGPavementRef pavement) const; + osg::Vec4f getLineColor(const int aPaintCode) const; + +}; + +} diff --git a/src/Airports/CMakeLists.txt b/src/Airports/CMakeLists.txt index 48e8cf824..de6547395 100644 --- a/src/Airports/CMakeLists.txt +++ b/src/Airports/CMakeLists.txt @@ -15,11 +15,12 @@ set(SOURCES sidstar.cxx airport.cxx xmlloader.cxx - airportdynamicsmanager.cxx + airportdynamicsmanager.cxx + AirportBuilder.cxx ) set(HEADERS - airports_fwd.hxx + airports_fwd.hxx apt_loader.hxx dynamicloader.hxx dynamics.hxx @@ -34,7 +35,8 @@ set(HEADERS sidstar.hxx airport.hxx xmlloader.hxx - airportdynamicsmanager.hxx + airportdynamicsmanager.hxx + AirportBuilder.hxx ) - + flightgear_component(Airports "${SOURCES}" "${HEADERS}") diff --git a/src/Airports/airport.cxx b/src/Airports/airport.cxx index 6ef502c92..683d0af04 100644 --- a/src/Airports/airport.cxx +++ b/src/Airports/airport.cxx @@ -354,22 +354,52 @@ FGTaxiwayList FGAirport::getTaxiways() const //------------------------------------------------------------------------------ unsigned int FGAirport::numPavements() const { - loadTaxiways(); return mPavements.size(); } -//------------------------------------------------------------------------------ -FGPavementRef FGAirport::getPavementByIndex(unsigned int aIndex) const -{ - loadTaxiways(); - return loadById(mPavements, aIndex); -} - //------------------------------------------------------------------------------ FGPavementList FGAirport::getPavements() const { - loadTaxiways(); - return loadAllById(mPavements); + return mPavements; +} + +void FGAirport::addPavement(FGPavementRef pavement) +{ + mPavements.push_back(pavement); +} + +//------------------------------------------------------------------------------ +unsigned int FGAirport::numBoundary() const +{ + return mBoundary.size(); +} + +//------------------------------------------------------------------------------ +FGPavementList FGAirport::getBoundary() const +{ + return mBoundary; +} + +void FGAirport::addBoundary(FGPavementRef boundary) +{ + mBoundary.push_back(boundary); +} + +//------------------------------------------------------------------------------ +unsigned int FGAirport::numLineFeatures() const +{ + return mLineFeatures.size(); +} + +//------------------------------------------------------------------------------ +FGPavementList FGAirport::getLineFeatures() const +{ + return mLineFeatures; +} + +void FGAirport::addLineFeature(FGPavementRef linefeature) +{ + mLineFeatures.push_back(linefeature); } //------------------------------------------------------------------------------ @@ -1001,7 +1031,7 @@ Transition *FGAirport::selectSIDByTransition(const FGRunway* runway, const stri for (auto sid : mSIDs) { if (runway && !sid->isForRunway(runway)) continue; - + auto trans = sid->findTransitionByName(aIdent); if (trans) { return trans; @@ -1028,7 +1058,7 @@ Transition *FGAirport::selectSTARByTransition(const FGRunway* runway, const stri for (auto star : mSTARs) { if (runway && !star->isForRunway(runway)) continue; - + auto trans = star->findTransitionByName(aIdent); if (trans) { return trans; diff --git a/src/Airports/airport.hxx b/src/Airports/airport.hxx index 0e53a9822..c36e8ad5e 100644 --- a/src/Airports/airport.hxx +++ b/src/Airports/airport.hxx @@ -94,7 +94,7 @@ class FGAirport : public FGPositioned FGRunwayRef getActiveRunwayForUsage() const; FGAirportDynamicsRef getDynamics() const; - + FGGroundNetwork* groundNetwork() const; unsigned int numRunways() const; @@ -124,7 +124,7 @@ class FGAirport : public FGPositioned double ilsWeight; }; FGRunwayRef findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams * parms = NULL ) const; - + /** * return the most likely target runway based on a position. * Specifically, return the runway for which the course from aPos @@ -133,7 +133,7 @@ class FGAirport : public FGPositioned * aiming towards. */ FGRunwayRef findBestRunwayForPos(const SGGeod& aPos) const; - + /** * Retrieve all runways at the airport, but excluding the reciprocal * runways. For example at KSFO this might return 1L, 1R, 28L and 28R, @@ -151,7 +151,7 @@ class FGAirport : public FGPositioned * Retrieve all runways at the airport */ FGRunwayList getRunways() const; - + /** * Useful predicate for FMS/GPS/NAV displays and similar - check if this * aiport has a hard-surfaced runway of at least the specified length. @@ -167,7 +167,17 @@ class FGAirport : public FGPositioned unsigned int numPavements() const; FGPavementRef getPavementByIndex(unsigned int aIndex) const; FGPavementList getPavements() const; - + void addPavement(FGPavementRef pavement); + + unsigned int numBoundary() const; + FGPavementRef getBoundaryIndex(unsigned int aIndex) const; + FGPavementList getBoundary() const; + void addBoundary(FGPavementRef boundary); + + unsigned int numLineFeatures() const; + FGPavementList getLineFeatures() const; + void addLineFeature(FGPavementRef linefeature); + class AirportFilter : public Filter { public: @@ -187,7 +197,7 @@ class FGAirport : public FGPositioned return true; } }; - + /** * Filter which passes heliports and seaports in addition to airports */ @@ -198,18 +208,18 @@ class FGAirport : public FGPositioned return SEAPORT; } }; - + class HardSurfaceFilter : public AirportFilter { public: HardSurfaceFilter(double minLengthFt = -1); - + virtual bool passAirport(FGAirport* aApt) const; - + private: double mMinLengthFt; }; - + /** * Filter which passes specified port type and in case of airport checks * if a runway larger the /sim/navdb/min-runway-lenght-ft exists. @@ -234,11 +244,11 @@ class FGAirport : public FGPositioned double _min_runway_length_ft; }; - + void setProcedures(const std::vector& aSids, const std::vector& aStars, const std::vector& aApproaches); - + void addSID(flightgear::SID* aSid); void addSTAR(flightgear::STAR* aStar); void addApproach(flightgear::Approach* aApp); @@ -247,7 +257,7 @@ class FGAirport : public FGPositioned flightgear::SID* getSIDByIndex(unsigned int aIndex) const; flightgear::SID* findSIDWithIdent(const std::string& aIdent) const; flightgear::SIDList getSIDs() const; - + flightgear::Transition* selectSIDByEnrouteTransition(FGPositioned* enroute) const; flightgear::Transition* selectSIDByTransition(const FGRunway* runway, const std::string& aIdent) const; @@ -255,7 +265,7 @@ class FGAirport : public FGPositioned flightgear::STAR* getSTARByIndex(unsigned int aIndex) const; flightgear::STAR* findSTARWithIdent(const std::string& aIdent) const; flightgear::STARList getSTARs() const; - + flightgear::Transition* selectSTARByEnrouteTransition(FGPositioned* enroute) const; flightgear::Transition* selectSTARByTransition(const FGRunway* runway, const std::string& aIdent) const; @@ -266,48 +276,48 @@ class FGAirport : public FGPositioned ( flightgear::ProcedureType type = flightgear::PROCEDURE_INVALID ) const; - + /** * Syntactic wrapper around FGPositioned::findClosest - find the closest * match for filter, and return it cast to FGAirport. The default filter * passes airports, but not seaports or heliports */ static FGAirportRef findClosest(const SGGeod& aPos, double aCuttofNm, Filter* filter = NULL); - + /** - * Helper to look up an FGAirport instance by unique ident. Throws an + * Helper to look up an FGAirport instance by unique ident. Throws an * exception if the airport could not be found - so callers can assume * the result is non-NULL. */ static FGAirportRef getByIdent(const std::string& aIdent); - + /** * Helper to look up an FGAirport instance by unique ident. Returns NULL * if the airport could not be found. */ static FGAirportRef findByIdent(const std::string& aIdent); - + /** * Specialised helper to implement the AirportList dialog. Performs a * case-insensitive search on airport names and ICAO codes, and returns - * matches in a format suitable for use by a puaList. + * matches in a format suitable for use by a puaList. */ static char** searchNamesAndIdents(const std::string& aFilter); - - + + /** * Sort an FGPositionedList of airports by size (number of runways + length) * this is meant to prioritise more important airports. */ static void sortBySize(FGPositionedList&); - + flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const; - + flightgear::CommStationList commStations() const; - + static void clearAirportsCache(); - - + + #if defined(BUILDING_TESTSUITE) // helper to allow testing without needing a full Airports heirarchy void testSuiteInjectGroundnetXML(const SGPath& path); @@ -318,29 +328,29 @@ private: // disable these FGAirport operator=(FGAirport &other); FGAirport(const FGAirport&); - + /** * helper to read airport data from the scenery XML files. */ void loadSceneryDefinitions() const; - + /** * Helpers to process property data loaded from an ICAO.threshold.xml file */ void readThresholdData(SGPropertyNode* aRoot); void processThreshold(SGPropertyNode* aThreshold); - + void readILSData(SGPropertyNode* aRoot); - + void validateTowerData() const; - + /** * Helper to parse property data loaded from an ICAO.twr.xml file */ void readTowerData(SGPropertyNode* aRoot); - + PositionedIDVec itemsOfType(FGPositioned::Type ty) const; - + std::string _name; bool _has_metar; @@ -348,11 +358,11 @@ private: void loadHelipads() const; void loadTaxiways() const; void loadProcedures() const; - + mutable bool mTowerDataLoaded; mutable bool mHasTower; mutable SGGeod mTowerPosition; - + mutable bool mRunwaysLoaded; mutable bool mHelipadsLoaded; mutable bool mTaxiwaysLoaded; @@ -362,15 +372,17 @@ private: bool mILSDataLoaded; mutable std::vector mRunways; - + mutable PositionedIDVec mHelipads; mutable PositionedIDVec mTaxiways; - PositionedIDVec mPavements; - + std::vector mPavements; + std::vector mBoundary; + std::vector mLineFeatures; + typedef SGSharedPtr SIDRef; typedef SGSharedPtr STARRef; typedef SGSharedPtr ApproachRef; - + std::vector mSIDs; std::vector mSTARs; std::vector mApproaches; @@ -385,5 +397,3 @@ const FGAirport *fgFindAirportID( const std::string& id); double fgGetAirportElev( const std::string& id ); #endif // _FG_SIMPLE_HXX - - diff --git a/src/Airports/apt_loader.cxx b/src/Airports/apt_loader.cxx index 2db735d03..c4380aafe 100644 --- a/src/Airports/apt_loader.cxx +++ b/src/Airports/apt_loader.cxx @@ -30,6 +30,7 @@ #include +#include #include // atof(), atoi() #include // memchr() #include // isspace() @@ -225,74 +226,12 @@ void APTLoader::loadAirports() it != airportInfoMap.end(); it++) { // Full path to the apt.dat file this airport info comes from const string aptDat = it->second.file.utf8Str(); - last_apt_id = it->first; // this is just the current airport identifier - // The first line for this airport was already split over whitespace, but - // remains to be parsed for the most part. - parseAirportLine(it->second.rowCode, it->second.firstLineTokens); - const LinesList& lines = it->second.otherLines; - // Loop over the second and subsequent lines - for (LinesList::const_iterator linesIt = lines.begin(); - linesIt != lines.end(); linesIt++) { - // Beware that linesIt->str may end with an '\r' character, see above! - unsigned int rowCode = linesIt->rowCode; + // this is just the current airport identifier + last_apt_id = it->first; + RawAirportInfo rawinfo = it->second; - if ( rowCode == 10 ) { // Runway v810 - parseRunwayLine810(aptDat, linesIt->number, - simgear::strutils::split(linesIt->str)); - } else if ( rowCode == 100 ) { // Runway v850 - parseRunwayLine850(aptDat, linesIt->number, - simgear::strutils::split(linesIt->str)); - } else if ( rowCode == 101 ) { // Water Runway v850 - parseWaterRunwayLine850(aptDat, linesIt->number, - simgear::strutils::split(linesIt->str)); - } else if ( rowCode == 102 ) { // Helipad v850 - parseHelipadLine850(aptDat, linesIt->number, - simgear::strutils::split(linesIt->str)); - } else if ( rowCode == 18 ) { - // beacon entry (ignore) - } else if ( rowCode == 14 ) { // Viewpoint/control tower - parseViewpointLine(aptDat, linesIt->number, - simgear::strutils::split(linesIt->str)); - } else if ( rowCode == 19 ) { - // windsock entry (ignore) - } else if ( rowCode == 20 ) { - // Taxiway sign (ignore) - } else if ( rowCode == 21 ) { - // lighting objects (ignore) - } else if ( rowCode == 15 ) { - // custom startup locations (ignore) - } else if ( rowCode == 0 ) { - // ?? - } else if ( rowCode >= 50 && rowCode <= 56) { - parseCommLine(aptDat, linesIt->number, rowCode, - simgear::strutils::split(linesIt->str)); - } else if ( rowCode == 110 ) { - pavement = true; - parsePavementLine850(simgear::strutils::split(linesIt->str, 0, 4)); - } else if ( rowCode >= 111 && rowCode <= 114 ) { - if ( pavement ) - parsePavementNodeLine850(aptDat, linesIt->number, rowCode, - simgear::strutils::split(linesIt->str)); - } else if ( rowCode >= 115 && rowCode <= 116 ) { - // other pavement nodes (ignore) - } else if ( rowCode == 120 ) { - pavement = false; - } else if ( rowCode == 130 ) { - pavement = false; - } else if ( rowCode >= 1000 ) { - // airport traffic flow (ignore) - } else { - std::ostringstream oss; - string cleanedLine = cleanLine(linesIt->str); - oss << aptDat << ":" << linesIt->number << ": unknown row code " << - rowCode; - SG_LOG( SG_GENERAL, SG_ALERT, oss.str() << " (" << cleanedLine << ")" ); - throw sg_format_exception(oss.str(), cleanedLine); - } - } // of loop over the second and subsequent apt.dat lines for the airport - - finishAirport(aptDat); + loadAirport(aptDat, last_apt_id, &rawinfo); nbLoadedAirports++; if ((nbLoadedAirports % 300) == 0) { @@ -307,6 +246,140 @@ void APTLoader::loadAirports() "Loaded data for " << nbLoadedAirports << " airports" ); } +// Parse and return specific apt.dat file containing a single airport. +const FGAirport* APTLoader::loadAirportFromFile(std::string id, const SGPath& aptdb_file) +{ + std::size_t bytesReadSoFar = 10; + std::size_t totalSizeOfAllAptDatFiles = 100; + + readAptDatFile(aptdb_file.str(), bytesReadSoFar, totalSizeOfAllAptDatFiles); + + RawAirportInfo rawInfo = airportInfoMap[id]; + return loadAirport(aptdb_file.c_str(), id, &rawInfo, true); +} + +const FGAirport* APTLoader::loadAirport(const string aptDat, const std::string airportID, RawAirportInfo* airport_info, bool createFGAirport) +{ + // The first line for this airport was already split over whitespace, but + // remains to be parsed for the most part. + parseAirportLine(airport_info->rowCode, airport_info->firstLineTokens); + const LinesList& lines = airport_info->otherLines; + + NodeBlock current_block = None; + + // Loop over the second and subsequent lines + for (LinesList::const_iterator linesIt = lines.begin(); + linesIt != lines.end(); linesIt++) { + // Beware that linesIt->str may end with an '\r' character, see above! + unsigned int rowCode = linesIt->rowCode; + + if ( rowCode == 10 ) { // Runway v810 + parseRunwayLine810(aptDat, linesIt->number, + simgear::strutils::split(linesIt->str)); + } else if ( rowCode == 100 ) { // Runway v850 + parseRunwayLine850(aptDat, linesIt->number, + simgear::strutils::split(linesIt->str)); + } else if ( rowCode == 101 ) { // Water Runway v850 + parseWaterRunwayLine850(aptDat, linesIt->number, + simgear::strutils::split(linesIt->str)); + } else if ( rowCode == 102 ) { // Helipad v850 + parseHelipadLine850(aptDat, linesIt->number, + simgear::strutils::split(linesIt->str)); + } else if ( rowCode == 18 ) { + // beacon entry (ignore) + } else if ( rowCode == 14 ) { // Viewpoint/control tower + parseViewpointLine(aptDat, linesIt->number, + simgear::strutils::split(linesIt->str)); + } else if ( rowCode == 19 ) { + // windsock entry (ignore) + } else if ( rowCode == 20 ) { + // Taxiway sign (ignore) + } else if ( rowCode == 21 ) { + // lighting objects (ignore) + } else if ( rowCode == 15 ) { + // custom startup locations (ignore) + } else if ( rowCode == 0 ) { + // ?? + } else if ( rowCode >= 50 && rowCode <= 56) { + parseCommLine(aptDat, linesIt->number, rowCode, + simgear::strutils::split(linesIt->str)); + } else if ( rowCode == 110 ) { + current_block = Pavement; + parsePavementLine850(simgear::strutils::split(linesIt->str, 0, 4)); + } else if ( rowCode >= 111 && rowCode <= 116 ) { + switch(current_block) { + case Pavement : + parseNodeLine850(&pavements, aptDat, linesIt->number, rowCode, + simgear::strutils::split(linesIt->str)); + break; + case AirportBoundary : + parseNodeLine850(&airport_boundary, aptDat, linesIt->number, rowCode, + simgear::strutils::split(linesIt->str)); + break; + case LinearFeature : + parseNodeLine850(&linear_feature, aptDat, linesIt->number, rowCode, + simgear::strutils::split(linesIt->str)); + break; + default : + case None : + std::ostringstream oss; + string cleanedLine = cleanLine(linesIt->str); + oss << aptDat << ":" << linesIt->number << ": unexpected row code " << + rowCode; + SG_LOG( SG_GENERAL, SG_ALERT, oss.str() << " (" << cleanedLine << ")" ); + throw sg_format_exception(oss.str(), cleanedLine); + break; + } + } else if ( rowCode == 120 ) { + current_block = LinearFeature; + } else if ( rowCode == 130 ) { + current_block = AirportBoundary; + } else if ( rowCode >= 1000 ) { + // airport traffic flow (ignore) + } else { + std::ostringstream oss; + string cleanedLine = cleanLine(linesIt->str); + oss << aptDat << ":" << linesIt->number << ": unknown row code " << + rowCode; + SG_LOG( SG_GENERAL, SG_ALERT, oss.str() << " (" << cleanedLine << ")" ); + throw sg_format_exception(oss.str(), cleanedLine); + } + } // of loop over the second and subsequent apt.dat lines for the airport + + finishAirport(aptDat); + + if (createFGAirport) { + FGAirportRef airport = FGAirport::findByIdent(airportID); + + std::for_each( + pavements.begin(), + pavements.end(), + [airport] (FGPavementRef p) { airport->addPavement(p); } ); + + std::for_each( + airport_boundary.begin(), + airport_boundary.end(), + [airport] (FGPavementRef p) { airport->addBoundary(p); } ); + + std::for_each( + linear_feature.begin(), + linear_feature.end(), + [airport] (FGPavementRef p) { airport->addLineFeature(p); } ); + + pavements.clear(); + airport_boundary.clear(); + linear_feature.clear(); + + + return airport; + + } else { + // No FGAirport requested + return NULL; + } +} + + // Tell whether an apt.dat line is blank or a comment line bool APTLoader::isBlankOrCommentLine(const std::string& line) { @@ -482,6 +555,11 @@ void APTLoader::parseRunwayLine850(const string& aptDat, unsigned int lineNum, double width = atof( token[1].c_str() ); int surface_code = atoi( token[2].c_str() ); + int shoulder_code = atoi( token[3].c_str() ); + float smoothness = atof( token[4].c_str() ); + int center_lights = atoi( token[5].c_str() ); + int edge_lights = atoi( token[6].c_str() ); + int distance_remaining = atoi( token[7].c_str() ); double lat_1 = atof( token[9].c_str() ); double lon_1 = atof( token[10].c_str() ); @@ -513,17 +591,33 @@ void APTLoader::parseRunwayLine850(const string& aptDat, unsigned int lineNum, double stopway1 = atof( token[12].c_str() ); double stopway2 = atof( token[21].c_str() ); + int markings1 = atoi( token[13].c_str() ); + int markings2 = atoi( token[22].c_str() ); + + int approach1 = atoi( token[14].c_str() ); + int approach2 = atoi( token[23].c_str() ); + + int tdz1 = atoi( token[15].c_str() ); + int tdz2 = atoi( token[24].c_str() ); + + int reil1 = atoi( token[16].c_str() ); + int reil2 = atoi( token[25].c_str() ); + PositionedID rwy = cache->insertRunway(FGPositioned::RUNWAY, rwy_no_1, pos_1, currentAirportPosID, heading_1, length, - width, displ_thresh1, stopway1, - surface_code); + width, displ_thresh1, stopway1, markings1, + approach1, tdz1, reil1, + surface_code, shoulder_code, smoothness, + center_lights, edge_lights, distance_remaining); PositionedID reciprocal = cache->insertRunway( FGPositioned::RUNWAY, rwy_no_2, pos_2, currentAirportPosID, heading_2, length, - width, displ_thresh2, stopway2, - surface_code); + width, displ_thresh2, stopway2, markings2, + approach2, tdz2, reil2, + surface_code, shoulder_code, smoothness, + center_lights, edge_lights, distance_remaining); cache->setRunwayReciprocal(rwy, reciprocal); } @@ -563,9 +657,11 @@ void APTLoader::parseWaterRunwayLine850(const string& aptDat, const string& rwy_no_1(token[3]); const string& rwy_no_2(token[6]); + // For water runways we overload the edge_lights to indicate use of buoys, + // as they too will be objects. Also, water runways don't have edge lights. PositionedID rwy = cache->insertRunway(FGPositioned::RUNWAY, rwy_no_1, pos_1, currentAirportPosID, heading_1, length, - width, 0.0, 0.0, 13); + width, 0.0, 0.0, 0, 0, 0, 0, 13, 0, 1.0, 0, 1, 0); PositionedID reciprocal = cache->insertRunway( FGPositioned::RUNWAY, @@ -602,10 +698,15 @@ void APTLoader::parseHelipadLine850(const string& aptDat, unsigned int lineNum, const string& rwy_no(token[1]); int surface_code = atoi( token[7].c_str() ); + int markings = atoi( token[8].c_str() ); + int shoulder_code = atoi( token[9].c_str() ); + float smoothness = atof( token[10].c_str() ); + int edge_lights = atoi( token[11].c_str() ); cache->insertRunway(FGPositioned::HELIPAD, rwy_no, pos, - currentAirportPosID, heading, length, - width, 0.0, 0.0, surface_code); + currentAirportPosID, heading, length, + width, 0.0, 0.0, markings, 0, 0, 0, + surface_code, shoulder_code, smoothness, 0, edge_lights, 0); } void APTLoader::parseViewpointLine(const string& aptDat, unsigned int lineNum, @@ -636,12 +737,13 @@ void APTLoader::parsePavementLine850(const vector& token) } } -void APTLoader::parsePavementNodeLine850(const string& aptDat, - unsigned int lineNum, int rowCode, - const vector& token) +void APTLoader::parseNodeLine850(NodeList *nodelist, + const string& aptDat, + unsigned int lineNum, int rowCode, + const vector& token) { - static const unsigned int minNbTokens[] = {3, 5, 3, 5}; - assert(111 <= rowCode && rowCode <= 114); + static const unsigned int minNbTokens[] = {3, 5, 3, 5, 3, 5}; + assert(111 <= rowCode && rowCode <= 116); if (token.size() < minNbTokens[rowCode-111]) { SG_LOG( SG_GENERAL, SG_WARN, @@ -656,20 +758,37 @@ void APTLoader::parsePavementNodeLine850(const string& aptDat, SGGeod pos(SGGeod::fromDegFt(lon, lat, 0.0)); FGPavement* pvt = 0; - if ( !pavement_ident.empty() ) { + if (( !pavement_ident.empty() ) || ( nodelist->size() == 0 )) { pvt = new FGPavement( 0, pavement_ident, pos ); - pavements.push_back( pvt ); + nodelist->push_back( pvt ); pavement_ident = ""; } else { - pvt = pavements.back(); + pvt = nodelist->back(); } - if ( rowCode == 112 || rowCode == 114 ) { + + int paintCode = 0; + int lightCode = 0; + + // Line information. The 2nd last token is the painted line type. Last token + // is the light type of the segment. Only applicable to codes 111-114. + if ((rowCode < 115) && (token.size() == (minNbTokens[rowCode-111] + 1))) { + // We've got a line paint code but no lighting code + paintCode = atoi(token[minNbTokens[rowCode-111]].c_str()); + } + + if ((rowCode < 115) && (token.size() == (minNbTokens[rowCode-111] + 2))) { + // We've got a line paint code and a lighting code + paintCode = atoi(token[minNbTokens[rowCode-111] -1].c_str()); + lightCode = atoi(token[minNbTokens[rowCode-111]].c_str()); + } + + if ((rowCode == 112) || (rowCode == 114) || (rowCode == 116)) { double lat_b = atof( token[3].c_str() ); double lon_b = atof( token[4].c_str() ); SGGeod pos_b(SGGeod::fromDegFt(lon_b, lat_b, 0.0)); - pvt->addBezierNode(pos, pos_b, rowCode == 114); + pvt->addBezierNode(pos, pos_b, (rowCode == 114) || (rowCode == 116), (rowCode == 114), paintCode, lightCode); } else { - pvt->addNode(pos, rowCode == 113); + pvt->addNode(pos, (rowCode == 113) || (rowCode == 115), (rowCode == 113), paintCode, lightCode); } } diff --git a/src/Airports/apt_loader.hxx b/src/Airports/apt_loader.hxx index d2c39eb3a..9fb578c3d 100644 --- a/src/Airports/apt_loader.hxx +++ b/src/Airports/apt_loader.hxx @@ -28,6 +28,7 @@ #include #include #include +#include "airport.hxx" #include #include @@ -58,6 +59,10 @@ public: // 'airportInfoMap' has only one entry per airport). void loadAirports(); + // Load a specific airport defined in aptdb_file, and return a "rich" view + // of the airport including taxiways, pavement and line features. + const FGAirport* loadAirportFromFile(std::string id, const SGPath& aptdb_file); + private: struct Line { @@ -88,10 +93,13 @@ private: typedef std::unordered_map AirportInfoMapType; typedef SGSharedPtr FGPavementPtr; + typedef std::vector NodeList; APTLoader(const APTLoader&); // disable copy constructor APTLoader& operator=(const APTLoader&); // disable copy-assignment operator + const FGAirport* loadAirport(const string aptDat, const std::string airportID, RawAirportInfo* airport_info, bool createFGAirport=false); + // Tell whether an apt.dat line is blank or a comment line bool isBlankOrCommentLine(const std::string& line); // Return a copy of 'line' with trailing '\r' char(s) removed @@ -112,9 +120,11 @@ private: void parseViewpointLine(const std::string& aptDat, unsigned int lineNum, const std::vector& token); void parsePavementLine850(const std::vector& token); - void parsePavementNodeLine850( + void parseNodeLine850( + NodeList *nodelist, const std::string& aptDat, unsigned int lineNum, int rowCode, const std::vector& token); + void parseCommLine( const std::string& aptDat, unsigned int lineNum, unsigned int rowCode, const std::vector& token); @@ -130,12 +140,17 @@ private: SGGeod tower; std::string pavement_ident; - bool pavement; - std::vector pavements; + NodeList pavements; + NodeList airport_boundary; + NodeList linear_feature; // Not an airport identifier in the sense of the apt.dat spec! PositionedID currentAirportPosID; NavDataCache* cache; + + // Enum to keep track of whether we are tracking a pavement, airport boundary + // or linear feature when parsing the file. + enum NodeBlock { None, Pavement, AirportBoundary, LinearFeature}; }; bool metarDataLoad(const SGPath& path); diff --git a/src/Airports/pavement.cxx b/src/Airports/pavement.cxx index dbb57d147..b734de05b 100644 --- a/src/Airports/pavement.cxx +++ b/src/Airports/pavement.cxx @@ -1,4 +1,4 @@ -// pavement.cxx - class to represent complex taxiway specified in v850 apt.dat +// pavement.cxx - class to represent complex taxiway specified in v850 apt.dat // // Copyright (C) 2009 Frederic Bouvier // @@ -29,12 +29,12 @@ FGPavement::FGPavement(PositionedID aGuid, const std::string& aIdent, const SGGe { } -void FGPavement::addNode(const SGGeod &aPos, bool aClose) +void FGPavement::addNode(const SGGeod &aPos, bool aClose, bool aLoop, int aPaintCode, int aLightCode) { - mNodes.push_back(new SimpleNode(aPos, aClose)); + mNodes.push_back(new SimpleNode(aPos, aClose, aLoop, aPaintCode, aLightCode)); } -void FGPavement::addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose) +void FGPavement::addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose, bool aLoop, int aPaintCode, int aLightCode) { - mNodes.push_back(new BezierNode(aPos, aCtrlPt, aClose)); + mNodes.push_back(new BezierNode(aPos, aCtrlPt, aClose, aLoop, aPaintCode, aLightCode)); } diff --git a/src/Airports/pavement.hxx b/src/Airports/pavement.hxx index c9a45806a..b62d25861 100644 --- a/src/Airports/pavement.hxx +++ b/src/Airports/pavement.hxx @@ -1,4 +1,4 @@ -// pavement.hxx - class to represent complex taxiway specified in v850 apt.dat +// pavement.hxx - class to represent complex taxiway specified in v850 apt.dat // // Copyright (C) 2009 Frederic Bouvier // @@ -38,21 +38,30 @@ public: { SGGeod mPos; bool mClose; + bool mLoop; + int mPaintCode; + int mLightCode; virtual ~NodeBase(){} // To enable RTTI }; - struct SimpleNode : public NodeBase //111,113 + struct SimpleNode : public NodeBase //111,113,115 { - SimpleNode(const SGGeod &aPos, bool aClose) { + SimpleNode(const SGGeod &aPos, bool aClose, bool aLoop, int aPaintCode, int aLightCode) { mPos = aPos; mClose = aClose; + mLoop = aLoop; + mPaintCode = aPaintCode; + mLightCode = aLightCode; } }; - struct BezierNode : public NodeBase //112,114 + struct BezierNode : public NodeBase //112,114,116 { - BezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose) { + BezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose, bool aLoop, int aPaintCode, int aLightCode) { mPos = aPos; mClose = aClose; + mLoop = aLoop; mControl = aCtrlPt; + mPaintCode = aPaintCode; + mLightCode = mLightCode; } SGGeod mControl; }; @@ -61,8 +70,8 @@ public: FGPavement(PositionedID aGuid, const std::string& aIdent, const SGGeod& aPos); - void addNode(const SGGeod &aPos, bool aClose = false); - void addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose = false); + void addNode(const SGGeod &aPos, bool aClose = false, bool aLoop = false, int paintCode = 0, int lightCode = 0); + void addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose = false, bool aLoop = false, int paintCode = 0, int lightCode = 0); const NodeList &getNodeList() const { return mNodes; } diff --git a/src/Cockpit/groundradar.cxx b/src/Cockpit/groundradar.cxx index 52403be02..7c44b0131 100644 --- a/src/Cockpit/groundradar.cxx +++ b/src/Cockpit/groundradar.cxx @@ -79,7 +79,7 @@ GroundRadar::~GroundRadar() void GroundRadar::update (double /* dt */) { - + } void GroundRadar::valueChanged(SGPropertyNode*) @@ -231,7 +231,7 @@ void GroundRadar::updateTexture() const double tower_lat = tower_location.getLatitudeDeg(); const double tower_lon = tower_location.getLongitudeDeg(); double scale = SG_METER_TO_NM * 200 / _range_node->getDoubleValue(); - + const FGAirport* apt = fgFindAirportID(airport_name); assert(apt); @@ -248,10 +248,11 @@ void GroundRadar::updateTexture() osg::Geometry *pvt_geom = dynamic_cast(_geode->getDrawable(1)); osg::Geometry::PrimitiveSetList &pvt_prim_list = pvt_geom->getPrimitiveSetList(); pvt_prim_list.clear(); - for (unsigned int i=0; inumPavements(); ++i) + + auto pavementlist = airport->getPavements(); + for (auto pvtiter = pavementlist.begin(); pvtiter != pavementlist.end(); ++pvtiter) { - FGPavement* pvt(apt->getPavementByIndex(i)); - osg::ref_ptr geom = addPavementGeometry(pvt, tower_lat, tower_lon, scale); + osg::ref_ptr geom = addPavementGeometry(*pvtiter, tower_lat, tower_lon, scale); osg::Geometry::PrimitiveSetList &prim_list = geom->getPrimitiveSetList(); osg::Vec3Array *vertices = dynamic_cast(geom->getVertexArray()); size_t before = pvt_vertices->size(), diff --git a/src/GUI/AirportDiagram.cxx b/src/GUI/AirportDiagram.cxx index c95392c91..388f83d2c 100644 --- a/src/GUI/AirportDiagram.cxx +++ b/src/GUI/AirportDiagram.cxx @@ -45,10 +45,10 @@ static double distanceToLineSegment(const QVector2D& p, const QVector2D& a, { QVector2D ab(b - a); QVector2D ac(p - a); - + // Squared length, to avoid a sqrt const qreal len2 = ab.lengthSquared(); - + // Line null, the projection can't exist, we return the first point if (qIsNull(len2)) { if (outT) { @@ -56,10 +56,10 @@ static double distanceToLineSegment(const QVector2D& p, const QVector2D& a, } return (p - a).length(); } - + // Parametric value of the projection on the line const qreal t = (ac.x() * ab.x() + ac.y() * ab.y()) / len2; - + if (t < 0.0) { // Point is before the first point if (outT) { @@ -76,11 +76,11 @@ static double distanceToLineSegment(const QVector2D& p, const QVector2D& a, if (outT) { *outT = t; } - + const QVector2D proj = a + t * ab; return (proj - p).length(); } - + return 0.0; } @@ -345,13 +345,13 @@ void AirportDiagram::paintContents(QPainter* p) if ((r.runway == runwaySelection) || (r.runway->reciprocalRunway() == runwaySelection)) { color = Qt::yellow; } - + p->setTransform(t); QPen pen(color); pen.setWidth(r.widthM); p->setPen(pen); - + p->drawLine(r.p1, r.p2); // draw idents @@ -361,7 +361,7 @@ void AirportDiagram::paintContents(QPainter* p) p->rotate(r.runway->headingDeg()); // invert scaling factor so we can use screen pixel sizes here p->scale(1.0 / m_scale, 1.0/ m_scale); - + p->setPen((r.runway == runwaySelection) ? Qt::yellow : Qt::magenta); p->drawText(QRect(-100, 5, 200, 200), ident, Qt::AlignHCenter | Qt::AlignTop); @@ -657,8 +657,10 @@ void AirportDiagram::buildTaxiways() void AirportDiagram::buildPavements() { m_pavements.clear(); - for (unsigned int pIndex=0; pIndex < m_airport->numPavements(); ++pIndex) { - FGPavementRef pave = m_airport->getPavementByIndex(pIndex); + auto pavementlist = m_airport->getPavements(); + for (auto pvtiter = pavementlist.begin(); pvtiter != pavementlist.end(); ++pvtiter) + { + FGPavementRef pave = *pvtiter; if (pave->getNodeList().empty()) { continue; } diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 5c68ec7bf..fb99bf3da 100644 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -1816,7 +1816,9 @@ PositionedID NavDataCache::insertRunway(FGPositioned::Type ty, const string& ident, const SGGeod& pos, PositionedID apt, double heading, double length, double width, double displacedThreshold, - double stopway, int surfaceCode) + double stopway, int markings, int approach, int tdz, int reil, + int surfaceCode, int shoulder_code, float smoothness, int center_lights, + int edge_lights, int distance_remaining) { // only runways are spatially indexed; don't bother indexing taxiways // or pavements @@ -1831,10 +1833,31 @@ NavDataCache::insertRunway(FGPositioned::Type ty, const string& ident, sqlite3_bind_int(d->insertRunway, 5, surfaceCode); sqlite3_bind_double(d->insertRunway, 6, displacedThreshold); sqlite3_bind_double(d->insertRunway, 7, stopway); + sqlite3_bind_int(d->insertRunway, 8, markings); + sqlite3_bind_int(d->insertRunway, 9, approach); + sqlite3_bind_int(d->insertRunway, 10, tdz); + sqlite3_bind_int(d->insertRunway, 11, reil); + sqlite3_bind_int(d->insertRunway, 12, shoulder_code); + sqlite3_bind_double(d->insertRunway, 13, smoothness); + sqlite3_bind_int(d->insertRunway, 14, center_lights); + sqlite3_bind_int(d->insertRunway, 15, edge_lights); + sqlite3_bind_int(d->insertRunway, 116, distance_remaining); return d->execInsert(d->insertRunway); } +PositionedID +NavDataCache::insertRunway(FGPositioned::Type ty, const string& ident, + const SGGeod& pos, PositionedID apt, + double heading, double length, double width, double displacedThreshold, + double stopway, int surfaceCode) +{ + return insertRunway(ty, ident, pos, apt, + heading, length, width, displacedThreshold, + stopway, 0, 0, 0, 0, + surfaceCode, 0, 0.0f, 0, 0, 0); +} + void NavDataCache::setRunwayReciprocal(PositionedID runway, PositionedID recip) { sqlite3_bind_int64(d->setRunwayReciprocal, 1, runway); diff --git a/src/Navaids/NavDataCache.hxx b/src/Navaids/NavDataCache.hxx index 873cba0b6..9282c238a 100644 --- a/src/Navaids/NavDataCache.hxx +++ b/src/Navaids/NavDataCache.hxx @@ -55,7 +55,7 @@ namespace Octree { class Airway; using AirwayRef = SGSharedPtr; - + class NavDataCache { public: @@ -160,10 +160,20 @@ public: PositionedID insertAirport(FGPositioned::Type ty, const std::string& ident, const std::string& name); void insertTower(PositionedID airportId, const SGGeod& pos); - PositionedID insertRunway(FGPositioned::Type ty, const std::string& ident, - const SGGeod& pos, PositionedID apt, - double heading, double length, double width, double displacedThreshold, - double stopway, int surfaceCode); + + + PositionedID insertRunway(FGPositioned::Type ty, const string& ident, + const SGGeod& pos, PositionedID apt, + double heading, double length, double width, double displacedThreshold, + double stopway, int markings, int approach, int tdz, int reil, + int surfaceCode, int shoulder_code, float smoothness, int center_lights, + int edge_lights, int distance_remaining); + + PositionedID insertRunway(FGPositioned::Type ty, const string& ident, + const SGGeod& pos, PositionedID apt, + double heading, double length, double width, double displacedThreshold, + double stopway, int surfaceCode); + void setRunwayReciprocal(PositionedID runway, PositionedID recip); void setRunwayILS(PositionedID runway, PositionedID ils); @@ -291,9 +301,9 @@ public: * in an airway */ AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos); - + AirwayRef loadAirway(int airwayID); - + /** * Waypoints on the airway */