2015-03-07 23:52:03 +00:00
|
|
|
// AirportDiagram.cxx - part of GUI launcher using Qt5
|
|
|
|
//
|
|
|
|
// Written by James Turner, started December 2014.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2014 James Turner <zakalawe@mac.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.
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
#include "AirportDiagram.hxx"
|
|
|
|
|
2015-06-05 08:26:40 +00:00
|
|
|
#include <limits>
|
|
|
|
|
2015-11-03 22:05:09 +00:00
|
|
|
#include <simgear/sg_inlines.h>
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
#include <QPainter>
|
|
|
|
#include <QDebug>
|
2015-06-05 08:26:40 +00:00
|
|
|
#include <QVector2D>
|
|
|
|
#include <QMouseEvent>
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
#include <Airports/airport.hxx>
|
|
|
|
#include <Airports/runways.hxx>
|
|
|
|
#include <Airports/parking.hxx>
|
|
|
|
#include <Airports/pavement.hxx>
|
|
|
|
|
2015-11-03 22:05:09 +00:00
|
|
|
#include <Navaids/navrecord.hxx>
|
|
|
|
|
2015-06-05 08:26:40 +00:00
|
|
|
static double distanceToLineSegment(const QVector2D& p, const QVector2D& a,
|
|
|
|
const QVector2D& b, double* outT = NULL)
|
|
|
|
{
|
|
|
|
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) {
|
|
|
|
*outT = 0.0;
|
|
|
|
}
|
|
|
|
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) {
|
|
|
|
*outT = 0.0;
|
|
|
|
}
|
|
|
|
return (p - a).length();
|
|
|
|
} else if (t > 1.0) {
|
|
|
|
// Point is after the second point
|
|
|
|
if (outT) {
|
|
|
|
*outT = 1.0;
|
|
|
|
}
|
|
|
|
return (p - b).length();
|
|
|
|
} else {
|
|
|
|
if (outT) {
|
|
|
|
*outT = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QVector2D proj = a + t * ab;
|
|
|
|
return (proj - p).length();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
}
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
AirportDiagram::AirportDiagram(QWidget* pr) :
|
2015-11-03 21:28:36 +00:00
|
|
|
BaseDiagram(pr),
|
|
|
|
m_approachDistanceNm(-1.0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AirportDiagram::~AirportDiagram()
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
2015-11-03 21:28:36 +00:00
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AirportDiagram::setAirport(FGAirportRef apt)
|
|
|
|
{
|
|
|
|
m_airport = apt;
|
|
|
|
m_projectionCenter = apt ? apt->geod() : SGGeod();
|
|
|
|
m_runways.clear();
|
2015-11-03 21:28:36 +00:00
|
|
|
m_approachDistanceNm = -1.0;
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
if (apt) {
|
|
|
|
buildTaxiways();
|
|
|
|
buildPavements();
|
|
|
|
}
|
2015-03-07 23:52:03 +00:00
|
|
|
|
2015-11-12 00:10:06 +00:00
|
|
|
clearIgnoredNavaids();
|
|
|
|
addIgnoredNavaid(apt);
|
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
recomputeBounds(true);
|
2014-12-26 12:20:51 +00:00
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2015-06-05 08:26:40 +00:00
|
|
|
FGRunwayRef AirportDiagram::selectedRunway() const
|
|
|
|
{
|
|
|
|
return m_selectedRunway;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AirportDiagram::setSelectedRunway(FGRunwayRef r)
|
|
|
|
{
|
|
|
|
if (r == m_selectedRunway) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_selectedRunway = r;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
void AirportDiagram::setApproachExtensionDistance(double distanceNm)
|
|
|
|
{
|
|
|
|
m_approachDistanceNm = distanceNm;
|
|
|
|
recomputeBounds(true);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
void AirportDiagram::addRunway(FGRunwayRef rwy)
|
|
|
|
{
|
|
|
|
Q_FOREACH(RunwayData rd, m_runways) {
|
|
|
|
if (rd.runway == rwy->reciprocalRunway()) {
|
|
|
|
return; // only add one end of reciprocal runways
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RunwayData r;
|
2015-11-03 21:28:36 +00:00
|
|
|
r.p1 = project(rwy->geod());
|
|
|
|
r.p2 = project(rwy->end());
|
2014-12-26 12:20:51 +00:00
|
|
|
r.widthM = qRound(rwy->widthM());
|
|
|
|
r.runway = rwy;
|
|
|
|
m_runways.append(r);
|
2015-11-03 21:28:36 +00:00
|
|
|
|
|
|
|
recomputeBounds(false);
|
2014-12-26 12:20:51 +00:00
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
void AirportDiagram::doComputeBounds()
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
2015-11-03 21:28:36 +00:00
|
|
|
Q_FOREACH(const RunwayData& r, m_runways) {
|
|
|
|
extendBounds(r.p1);
|
|
|
|
extendBounds(r.p2);
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_FOREACH(const TaxiwayData& t, m_taxiways) {
|
|
|
|
extendBounds(t.p1);
|
|
|
|
extendBounds(t.p2);
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_FOREACH(const ParkingData& p, m_parking) {
|
|
|
|
extendBounds(p.pt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_selectedRunway && (m_approachDistanceNm > 0.0)) {
|
|
|
|
double d = SG_NM_TO_METER * m_approachDistanceNm;
|
|
|
|
QPointF pt = project(m_selectedRunway->pointOnCenterline(-d));
|
|
|
|
extendBounds(pt);
|
|
|
|
}
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
void AirportDiagram::addParking(FGParkingRef park)
|
2015-06-05 08:26:40 +00:00
|
|
|
{
|
2015-11-03 21:28:36 +00:00
|
|
|
ParkingData pd = { project(park->geod()), park };
|
|
|
|
m_parking.push_back(pd);
|
|
|
|
recomputeBounds(false);
|
|
|
|
update();
|
2015-06-05 08:26:40 +00:00
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
void AirportDiagram::paintContents(QPainter* p)
|
|
|
|
{
|
2015-11-06 05:47:49 +00:00
|
|
|
QTransform t = p->transform();
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
// pavements
|
|
|
|
QBrush brush(QColor(0x9f, 0x9f, 0x9f));
|
|
|
|
Q_FOREACH(const QPainterPath& path, m_pavements) {
|
2015-11-03 21:28:36 +00:00
|
|
|
p->drawPath(path);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// taxiways
|
|
|
|
Q_FOREACH(const TaxiwayData& t, m_taxiways) {
|
|
|
|
QPen pen(QColor(0x9f, 0x9f, 0x9f));
|
|
|
|
pen.setWidth(t.widthM);
|
2015-11-03 21:28:36 +00:00
|
|
|
p->setPen(pen);
|
|
|
|
p->drawLine(t.p1, t.p2);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// runways
|
|
|
|
QFont f;
|
|
|
|
f.setPixelSize(14);
|
2015-11-03 21:28:36 +00:00
|
|
|
p->setFont(f);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2015-11-03 22:05:09 +00:00
|
|
|
// draw ILS first so underneath all runways
|
2015-11-12 00:10:06 +00:00
|
|
|
QPen pen(QColor(0x5f, 0x5f, 0x5f));
|
|
|
|
pen.setWidth(1);
|
|
|
|
pen.setCosmetic(true);
|
2015-11-03 22:05:09 +00:00
|
|
|
p->setPen(pen);
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
Q_FOREACH(const RunwayData& r, m_runways) {
|
2015-11-03 22:05:09 +00:00
|
|
|
drawILS(p, r.runway);
|
|
|
|
drawILS(p, r.runway->reciprocalRunway());
|
|
|
|
}
|
|
|
|
|
|
|
|
// now draw the runways for real
|
|
|
|
Q_FOREACH(const RunwayData& r, m_runways) {
|
|
|
|
|
2015-06-05 08:26:40 +00:00
|
|
|
QColor color(Qt::magenta);
|
|
|
|
if ((r.runway == m_selectedRunway) || (r.runway->reciprocalRunway() == m_selectedRunway)) {
|
|
|
|
color = Qt::yellow;
|
|
|
|
}
|
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
p->setTransform(t);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2015-06-05 08:26:40 +00:00
|
|
|
QPen pen(color);
|
2014-12-26 12:20:51 +00:00
|
|
|
pen.setWidth(r.widthM);
|
2015-11-03 21:28:36 +00:00
|
|
|
p->setPen(pen);
|
2015-06-05 08:26:40 +00:00
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
p->drawLine(r.p1, r.p2);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
// draw idents
|
|
|
|
QString ident = QString::fromStdString(r.runway->ident());
|
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
p->translate(r.p1);
|
|
|
|
p->rotate(r.runway->headingDeg());
|
2014-12-26 12:20:51 +00:00
|
|
|
// invert scaling factor so we can use screen pixel sizes here
|
2015-11-03 21:28:36 +00:00
|
|
|
p->scale(1.0 / m_scale, 1.0/ m_scale);
|
2015-06-05 08:26:40 +00:00
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
p->setPen((r.runway == m_selectedRunway) ? Qt::yellow : Qt::magenta);
|
|
|
|
p->drawText(QRect(-100, 5, 200, 200), ident, Qt::AlignHCenter | Qt::AlignTop);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
FGRunway* recip = r.runway->reciprocalRunway();
|
|
|
|
QString recipIdent = QString::fromStdString(recip->ident());
|
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
p->setTransform(t);
|
|
|
|
p->translate(r.p2);
|
|
|
|
p->rotate(recip->headingDeg());
|
|
|
|
p->scale(1.0 / m_scale, 1.0/ m_scale);
|
|
|
|
|
|
|
|
p->setPen((r.runway->reciprocalRunway() == m_selectedRunway) ? Qt::yellow : Qt::magenta);
|
|
|
|
p->drawText(QRect(-100, 5, 200, 200), recipIdent, Qt::AlignHCenter | Qt::AlignTop);
|
|
|
|
}
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
if (m_selectedRunway && (m_approachDistanceNm > 0.0)) {
|
|
|
|
p->setTransform(t);
|
|
|
|
// draw approach extension point
|
|
|
|
double d = SG_NM_TO_METER * m_approachDistanceNm;
|
|
|
|
QPointF pt = project(m_selectedRunway->pointOnCenterline(-d));
|
|
|
|
QPointF pt2 = project(m_selectedRunway->geod());
|
2015-11-03 22:05:09 +00:00
|
|
|
QPen pen(Qt::yellow);
|
|
|
|
pen.setWidth(2.0 / m_scale);
|
2015-11-03 21:28:36 +00:00
|
|
|
p->setPen(pen);
|
|
|
|
p->drawLine(pt, pt2);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
2015-11-14 16:46:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
// aircraft pos and heading...
|
|
|
|
// paintAirplaneIcon(painter, );
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2015-11-03 22:05:09 +00:00
|
|
|
void AirportDiagram::drawILS(QPainter* painter, FGRunwayRef runway) const
|
|
|
|
{
|
|
|
|
if (!runway)
|
|
|
|
return;
|
|
|
|
|
|
|
|
FGNavRecord* loc = runway->ILS();
|
|
|
|
if (!loc)
|
|
|
|
return;
|
|
|
|
|
|
|
|
double halfBeamWidth = loc->localizerWidth() * 0.5;
|
|
|
|
QPointF threshold = project(runway->threshold());
|
|
|
|
double rangeM = loc->get_range() * SG_NM_TO_METER;
|
|
|
|
double radial = loc->get_multiuse();
|
|
|
|
SG_NORMALIZE_RANGE(radial, 0.0, 360.0);
|
|
|
|
|
|
|
|
// compute the three end points at the wide end of the arrow
|
|
|
|
QPointF endCentre = project(SGGeodesy::direct(loc->geod(), radial, -rangeM));
|
|
|
|
QPointF endR = project(SGGeodesy::direct(loc->geod(), radial + halfBeamWidth, -rangeM * 1.1));
|
|
|
|
QPointF endL = project(SGGeodesy::direct(loc->geod(), radial - halfBeamWidth, -rangeM * 1.1));
|
|
|
|
|
|
|
|
painter->drawLine(threshold, endCentre);
|
|
|
|
painter->drawLine(threshold, endL);
|
|
|
|
painter->drawLine(threshold, endR);
|
|
|
|
painter->drawLine(endL, endCentre);
|
|
|
|
painter->drawLine(endR, endCentre);
|
|
|
|
}
|
|
|
|
|
2015-06-05 08:26:40 +00:00
|
|
|
void AirportDiagram::mouseReleaseEvent(QMouseEvent* me)
|
|
|
|
{
|
2015-11-03 21:28:36 +00:00
|
|
|
if (m_didPan)
|
|
|
|
return; // ignore panning drag+release ops here
|
|
|
|
|
2015-06-05 08:26:40 +00:00
|
|
|
QTransform t(transform());
|
|
|
|
double minDist = std::numeric_limits<double>::max();
|
|
|
|
FGRunwayRef bestRunway;
|
|
|
|
|
|
|
|
Q_FOREACH(const RunwayData& r, m_runways) {
|
|
|
|
QPointF p1(t.map(r.p1)), p2(t.map(r.p2));
|
|
|
|
double t;
|
|
|
|
double d = distanceToLineSegment(QVector2D(me->pos()),
|
|
|
|
QVector2D(p1),
|
|
|
|
QVector2D(p2), &t);
|
|
|
|
if (d < minDist) {
|
|
|
|
if (t > 0.5) {
|
|
|
|
bestRunway = r.runway->reciprocalRunway();
|
|
|
|
} else {
|
|
|
|
bestRunway = r.runway;
|
|
|
|
}
|
|
|
|
minDist = d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (minDist < 16.0) {
|
|
|
|
emit clickedRunway(bestRunway);
|
|
|
|
}
|
|
|
|
}
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
void AirportDiagram::buildTaxiways()
|
|
|
|
{
|
|
|
|
m_taxiways.clear();
|
|
|
|
for (unsigned int tIndex=0; tIndex < m_airport->numTaxiways(); ++tIndex) {
|
|
|
|
FGTaxiwayRef tx = m_airport->getTaxiwayByIndex(tIndex);
|
|
|
|
|
|
|
|
TaxiwayData td;
|
|
|
|
td.p1 = project(tx->geod());
|
|
|
|
td.p2 = project(tx->pointOnCenterline(tx->lengthM()));
|
2015-11-03 21:28:36 +00:00
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
td.widthM = tx->widthM();
|
|
|
|
m_taxiways.append(td);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AirportDiagram::buildPavements()
|
|
|
|
{
|
|
|
|
m_pavements.clear();
|
|
|
|
for (unsigned int pIndex=0; pIndex < m_airport->numPavements(); ++pIndex) {
|
|
|
|
FGPavementRef pave = m_airport->getPavementByIndex(pIndex);
|
|
|
|
if (pave->getNodeList().empty()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPainterPath pp;
|
|
|
|
QPointF startPoint;
|
|
|
|
bool closed = true;
|
|
|
|
QPointF p0 = project(pave->getNodeList().front()->mPos);
|
|
|
|
|
|
|
|
FGPavement::NodeList::const_iterator it;
|
|
|
|
for (it = pave->getNodeList().begin(); it != pave->getNodeList().end(); ) {
|
|
|
|
const FGPavement::BezierNode *bn = dynamic_cast<const FGPavement::BezierNode *>(it->get());
|
|
|
|
bool close = (*it)->mClose;
|
|
|
|
|
|
|
|
// increment iterator so we can look at the next point
|
|
|
|
++it;
|
|
|
|
QPointF nextPoint = (it == pave->getNodeList().end()) ? startPoint : project((*it)->mPos);
|
|
|
|
|
|
|
|
if (bn) {
|
|
|
|
QPointF control = project(bn->mControl);
|
|
|
|
QPointF endPoint = close ? startPoint : nextPoint;
|
|
|
|
pp.quadTo(control, endPoint);
|
|
|
|
} else {
|
|
|
|
// straight line segment
|
|
|
|
if (closed) {
|
|
|
|
pp.moveTo(p0);
|
|
|
|
closed = false;
|
|
|
|
startPoint = p0;
|
|
|
|
} else
|
|
|
|
pp.lineTo(p0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (close) {
|
|
|
|
closed = true;
|
|
|
|
pp.closeSubpath();
|
|
|
|
startPoint = QPointF();
|
|
|
|
}
|
|
|
|
|
|
|
|
p0 = nextPoint;
|
|
|
|
} // of nodes iteration
|
|
|
|
|
|
|
|
if (!closed) {
|
|
|
|
pp.closeSubpath();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pavements.append(pp);
|
|
|
|
} // of pavements iteration
|
|
|
|
}
|