1
0
Fork 0

AirportBuilder to generate airports from apt.dat

This commit is contained in:
Stuart Buchanan 2020-07-01 20:57:15 +01:00
parent bb75d24fc0
commit c4942eaa69
13 changed files with 1030 additions and 181 deletions

View file

@ -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 <algorithm>
#include <osgDB/Registry>
#include <osgUtil/Tessellator>
#include <osgUtil/DelaunayTriangulator>
#include "AirportBuilder.hxx"
#include <osg/Geode>
#include <osg/Geometry>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include "simgear/scene/model/ModelRegistry.hxx"
#include "simgear/scene/util/OsgMath.hxx"
#include "simgear/math/SGGeodesy.hxx"
#include "airport.hxx"
#include <Airports/apt_loader.hxx>
#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<osg::Node*> 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<SGGeod> 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<osgUtil::DelaunayTriangulator> triangulator = new osgUtil::DelaunayTriangulator;
triangulator->setInputPointArray(points);
triangulator->triangulate();
osg::ref_ptr<osg::Geometry> 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<osgUtil::Tessellator> 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<FGPavement::BezierNode*>(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<osg::Geometry> 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<osgUtil::Tessellator> 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<FGPavement::BezierNode*>(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<osg::Geometry> 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<osg::Geometry> 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<FGPavement::BezierNode*>(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<simgear::DefaultProcessPolicy, simgear::NoCachePolicy,
simgear::NoOptimizePolicy,
simgear::NoSubstitutePolicy, simgear::BuildGroupBVHPolicy>
AirportCallback;
namespace
{
osgDB::RegisterReaderWriterProxy<flightgear::AirportBuilder> g_readerAirportBuilder;
simgear::ModelRegistryCallbackProxy<AirportCallback> g_icaoCallbackProxy("icao");
}

View file

@ -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 <osgDB/Registry>
#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;
};
}

View file

@ -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}")

View file

@ -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<FGPavement>(mPavements, aIndex);
}
//------------------------------------------------------------------------------
FGPavementList FGAirport::getPavements() const
{
loadTaxiways();
return loadAllById<FGPavement>(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;

View file

@ -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<flightgear::SID*>& aSids,
const std::vector<flightgear::STAR*>& aStars,
const std::vector<flightgear::Approach*>& 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<FGRunwayRef> mRunways;
mutable PositionedIDVec mHelipads;
mutable PositionedIDVec mTaxiways;
PositionedIDVec mPavements;
std::vector<FGPavementRef> mPavements;
std::vector<FGPavementRef> mBoundary;
std::vector<FGPavementRef> mLineFeatures;
typedef SGSharedPtr<flightgear::SID> SIDRef;
typedef SGSharedPtr<flightgear::STAR> STARRef;
typedef SGSharedPtr<flightgear::Approach> ApproachRef;
std::vector<SIDRef> mSIDs;
std::vector<STARRef> mSTARs;
std::vector<ApproachRef> mApproaches;
@ -385,5 +397,3 @@ const FGAirport *fgFindAirportID( const std::string& id);
double fgGetAirportElev( const std::string& id );
#endif // _FG_SIMPLE_HXX

View file

@ -30,6 +30,7 @@
#include <simgear/compiler.h>
#include <algorithm>
#include <stdlib.h> // atof(), atoi()
#include <string.h> // memchr()
#include <ctype.h> // 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<string>& token)
}
}
void APTLoader::parsePavementNodeLine850(const string& aptDat,
unsigned int lineNum, int rowCode,
const vector<string>& token)
void APTLoader::parseNodeLine850(NodeList *nodelist,
const string& aptDat,
unsigned int lineNum, int rowCode,
const vector<string>& 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);
}
}

View file

@ -28,6 +28,7 @@
#include <string>
#include <vector>
#include <unordered_map>
#include "airport.hxx"
#include <simgear/compiler.h>
#include <simgear/structure/SGSharedPtr.hxx>
@ -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<std::string, RawAirportInfo> AirportInfoMapType;
typedef SGSharedPtr<FGPavement> FGPavementPtr;
typedef std::vector<FGPavementPtr> 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<std::string>& token);
void parsePavementLine850(const std::vector<std::string>& token);
void parsePavementNodeLine850(
void parseNodeLine850(
NodeList *nodelist,
const std::string& aptDat, unsigned int lineNum, int rowCode,
const std::vector<std::string>& token);
void parseCommLine(
const std::string& aptDat, unsigned int lineNum, unsigned int rowCode,
const std::vector<std::string>& token);
@ -130,12 +140,17 @@ private:
SGGeod tower;
std::string pavement_ident;
bool pavement;
std::vector<FGPavementPtr> 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);

View file

@ -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));
}

View file

@ -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; }

View file

@ -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<osg::Geometry *>(_geode->getDrawable(1));
osg::Geometry::PrimitiveSetList &pvt_prim_list = pvt_geom->getPrimitiveSetList();
pvt_prim_list.clear();
for (unsigned int i=0; i<apt->numPavements(); ++i)
auto pavementlist = airport->getPavements();
for (auto pvtiter = pavementlist.begin(); pvtiter != pavementlist.end(); ++pvtiter)
{
FGPavement* pvt(apt->getPavementByIndex(i));
osg::ref_ptr<osg::Geometry> geom = addPavementGeometry(pvt, tower_lat, tower_lon, scale);
osg::ref_ptr<osg::Geometry> geom = addPavementGeometry(*pvtiter, tower_lat, tower_lon, scale);
osg::Geometry::PrimitiveSetList &prim_list = geom->getPrimitiveSetList();
osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array *>(geom->getVertexArray());
size_t before = pvt_vertices->size(),

View file

@ -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;
}

View file

@ -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);

View file

@ -55,7 +55,7 @@ namespace Octree {
class Airway;
using AirwayRef = SGSharedPtr<Airway>;
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
*/