24#include <simgear/sg_inlines.h>
43 const QVector2D& b,
double* outT = NULL)
49 const qreal len2 = ab.lengthSquared();
56 return (
p - a).length();
60 const qreal t = (ac.x() * ab.x() + ac.y() * ab.y()) / len2;
67 return (
p - a).length();
73 return (
p - b).length();
79 const QVector2D proj = a + t * ab;
80 return (proj -
p).length();
86 const QPointF tVec = t.map(QPointF(1.0, 0.0)) - t.map(QPointF(0.0, 0.0));
87 return QVector2D(tVec).length();
93 m_parkingIconPath.moveTo(0,0);
94 m_parkingIconPath.lineTo(-16, -16);
95 m_parkingIconPath.lineTo(-64, -16);
96 m_parkingIconPath.lineTo(-64, 16);
97 m_parkingIconPath.lineTo(-16, 16);
98 m_parkingIconPath.lineTo(0, 0);
100 m_parkingIconLeftPath.moveTo(0,0);
101 m_parkingIconLeftPath.lineTo(16, -16);
102 m_parkingIconLeftPath.lineTo(64, -16);
103 m_parkingIconLeftPath.lineTo(64, 16);
104 m_parkingIconLeftPath.lineTo(16, 16);
105 m_parkingIconLeftPath.lineTo(0, 0);
107 m_helipadBoundsPath.moveTo(0, 0);
108 m_helipadBoundsPath.addEllipse(QPointF(0, 0), 16.0, 16.0);
110 m_helipadIconPath.moveTo(0,0);
111 m_helipadIconPath.addEllipse(QPointF(0, 0), 16.0, 16.0);
112 m_helipadIconPath.addEllipse(QPointF(0, 0), 13.0, 13.0);
115 f.setPixelSize(24.0);
117 QFontMetrics metrics(f);
118 qreal xOffset = metrics.horizontalAdvance(
"H") * 0.5;
119 qreal yOffset = metrics.capHeight() * 0.5;
120 m_helipadIconPath.addText(-xOffset, yOffset, f,
"H");
138 for (
unsigned int r=0; r<apt->numHelipads(); ++r) {
144 for (
unsigned int r = 0; r < apt->numHelipads(); ++r) {
149 for (
unsigned int r=0; r<apt->numRunways(); ++r) {
155 if (ground && ground->
exists()) {
173 if (pos && (m_selection == pos->
inner())) {
180 m_selection = pos->
inner();
189 if (m_approachDistance == distance) {
193 m_approachDistance = distance;
201 return m_approachDistance;
216 return m_airport->
guid();
232 if (m_approachExtensionEnabled == e)
234 m_approachExtensionEnabled = e;
242 Q_FOREACH(RunwayData rd, m_runways) {
243 if (rd.runway == rwy->reciprocalRunway()) {
251 r.widthM = qRound(rwy->widthM());
262 Q_FOREACH(
const RunwayData& r, m_runways) {
267 Q_FOREACH(
const TaxiwayData& t, m_taxiways) {
272 Q_FOREACH(
const ParkingData&
p, m_parking) {
276 Q_FOREACH(
const HelipadData&
p, m_helipads) {
281 if (runwaySelection && m_approachExtensionEnabled) {
290 ParkingData pd = {
project(park->geod()), park };
291 m_parking.push_back(pd);
298 HelipadData pd = {
project(pad->geod()), pad };
299 m_helipads.push_back(pd);
306 QTransform t =
p->transform();
308 QBrush brush(QColor(0x9f, 0x9f, 0x9f));
309 Q_FOREACH(
const QPainterPath& path, m_pavements) {
314 Q_FOREACH(
const TaxiwayData& t, m_taxiways) {
315 QPen pen(QColor(0x9f, 0x9f, 0x9f));
316 pen.setWidth(t.widthM);
318 p->drawLine(t.p1, t.p2);
330 QPen pen(QColor(0x5f, 0x5f, 0x5f));
332 pen.setCosmetic(
true);
335 Q_FOREACH(
const RunwayData& r, m_runways) {
336 drawILS(
p, r.runway);
337 drawILS(
p, r.runway->reciprocalRunway());
340 bool drawAircraft =
false;
347 Q_FOREACH(
const RunwayData& r, m_runways) {
349 QColor color(Qt::magenta);
350 if ((r.runway == runwaySelection) || (r.runway->reciprocalRunway() == runwaySelection)) {
357 pen.setWidth(r.widthM);
360 p->drawLine(r.p1, r.p2);
363 QString ident = QString::fromStdString(r.runway->ident());
366 p->rotate(r.runway->headingDeg());
370 p->setPen((r.runway == runwaySelection) ? Qt::yellow : Qt::magenta);
371 p->drawText(QRect(-100, 5, 200, 200), ident, Qt::AlignHCenter | Qt::AlignTop);
373 FGRunway* recip = r.runway->reciprocalRunway();
374 QString recipIdent = QString::fromStdString(recip->
ident());
381 p->setPen((r.runway->reciprocalRunway() == runwaySelection) ? Qt::yellow : Qt::magenta);
382 p->drawText(QRect(-100, 5, 200, 200), recipIdent, Qt::AlignHCenter | Qt::AlignTop);
385 if (runwaySelection) {
387 aircraftPos = runwaySelection->
geod();
391 if (runwaySelection && m_approachExtensionEnabled) {
397 QPen pen(Qt::yellow);
400 p->drawLine(pt, pt2);
412 QPen testPen(Qt::cyan);
414 testPen.setCosmetic(
true);
416 p->setBrush(Qt::NoBrush);
420 Q_FOREACH(
const RunwayData& r, m_runways) {
421 QPainterPath pp = pathForRunway(r, t, minWidth);
427void AirportDiagram::drawHelipads(QPainter* painter)
431 Q_FOREACH(
const HelipadData&
p, m_helipads) {
433 painter->translate(
p.pt);
435 if (
p.helipad == selectedHelipad) {
436 painter->setBrush(Qt::yellow);
438 painter->setBrush(Qt::magenta);
441 painter->drawPath(m_helipadIconPath);
446void AirportDiagram::drawParking(QPainter* painter,
const ParkingData&
p)
const
449 painter->translate(
p.pt);
451 double hdg =
p.parking->getHeading();
452 bool useLeftIcon =
false;
453 QRect labelRect(-62, -14, 40, 28);
458 labelRect = QRect(22, -14, 40, 28);
463 painter->rotate(hdg);
464 painter->setPen(Qt::NoPen);
467 if (
p.parking == selectedParking) {
468 painter->setBrush(Qt::yellow);
470 painter->setBrush(QColor(255, 196, 196));
473 painter->drawPath(useLeftIcon ? m_parkingIconLeftPath : m_parkingIconPath);
477 if (
p.parking != selectedParking) {
478 painter->fillRect(labelRect, Qt::white);
481 QFont f = painter->font();
485 QString parkingName = QString::fromStdString(
p.parking->name());
486 int textFlags = Qt::AlignVCenter | Qt::AlignHCenter | Qt::TextWordWrap;
487 QRectF bounds = painter->boundingRect(labelRect, textFlags, parkingName);
488 if (bounds.height() > labelRect.height()) {
494 painter->setPen(Qt::black);
495 painter->drawText(labelRect, textFlags, parkingName);
499AirportDiagram::ParkingData AirportDiagram::findParkingData(
const FGParkingRef &pk)
const
502 if (!selectedParking)
505 Q_FOREACH(
const ParkingData&
p, m_parking) {
506 if (
p.parking == selectedParking) {
514void AirportDiagram::drawParkings(QPainter* painter)
const
518 Q_FOREACH(
const ParkingData&
p, m_parking) {
519 if (
p.parking == selectedParking) {
523 drawParking(painter,
p);
526 if (selectedParking) {
527 drawParking(painter, findParkingData(selectedParking));
531void AirportDiagram::drawILS(QPainter* painter,
FGRunwayRef runway)
const
536 FGNavRecord* loc = runway->ILS();
541 QPointF threshold =
project(runway->threshold());
542 double rangeM = loc->
get_range() * SG_NM_TO_METER;
544 SG_NORMALIZE_RANGE(radial, 0.0, 360.0);
547 QPointF endCentre =
project(SGGeodesy::direct(loc->
geod(), radial, -rangeM));
548 QPointF endR =
project(SGGeodesy::direct(loc->
geod(), radial + halfBeamWidth, -rangeM * 1.1));
549 QPointF endL =
project(SGGeodesy::direct(loc->
geod(), radial - halfBeamWidth, -rangeM * 1.1));
551 painter->drawLine(threshold, endCentre);
552 painter->drawLine(threshold, endL);
553 painter->drawLine(threshold, endR);
554 painter->drawLine(endL, endCentre);
555 painter->drawLine(endR, endCentre);
564 Q_FOREACH (
const HelipadData& pad, m_helipads) {
565 QPainterPath pp = pathForHelipad(pad, t);
567 if (pp.contains(me->pos())) {
573 Q_FOREACH(
const RunwayData& r, m_runways) {
574 QPainterPath pp = pathForRunway(r, t, minWidth);
575 if (pp.contains(me->pos())) {
577 QPointF p1(t.map(r.p1)),
p2(t.map(r.p2));
586 Q_FOREACH(
const ParkingData& parking, m_parking) {
587 QPainterPath pp = pathForParking(parking, t);
588 if (pp.contains(me->pos())) {
595QPainterPath AirportDiagram::pathForRunway(
const RunwayData& r,
const QTransform& t,
596 const double minWidth)
const
599 double width = qMax(
static_cast<double>(r.widthM), minWidth);
600 double halfWidth = width * 0.5;
601 QVector2D v = QVector2D(r.p2 - r.p1);
603 QVector2D halfVec = QVector2D(v.y(), -v.x()) * halfWidth;
605 pp.moveTo(r.p1 - halfVec.toPointF());
606 pp.lineTo(r.p1 + halfVec.toPointF());
607 pp.lineTo(r.p2 + halfVec.toPointF());
608 pp.lineTo(r.p2 - halfVec.toPointF());
614QPainterPath AirportDiagram::pathForParking(
const ParkingData&
p,
const QTransform& t)
const
616 bool useLeftIcon =
false;
617 double hdg =
p.parking->getHeading();
627 x.translate(
p.pt.x(),
p.pt.y());
629 return x.map(useLeftIcon ? m_parkingIconLeftPath : m_parkingIconPath);
632QPainterPath AirportDiagram::pathForHelipad(
const HelipadData& h,
const QTransform& t)
const
635 x.translate(h.pt.x(), h.pt.y());
636 return x.map(m_helipadBoundsPath);
639void AirportDiagram::buildTaxiways()
642 for (
unsigned int tIndex=0; tIndex < m_airport->numTaxiways(); ++tIndex) {
647 td.p2 =
project(tx->pointOnCenterline(tx->lengthM()));
649 td.widthM = tx->widthM();
650 m_taxiways.append(td);
654void AirportDiagram::buildPavements()
657 auto pavementlist = m_airport->getPavements();
658 for (
auto pvtiter = pavementlist.begin(); pvtiter != pavementlist.end(); ++pvtiter)
661 if (pave->getNodeList().empty()) {
668 QPointF p0 =
project(pave->getNodeList().front()->mPos);
670 FGPavement::NodeList::const_iterator it;
671 for (it = pave->getNodeList().begin(); it != pave->getNodeList().end(); ) {
672 const FGPavement::BezierNode *bn =
dynamic_cast<const FGPavement::BezierNode *
>(it->get());
673 bool close = (*it)->mClose;
677 QPointF nextPoint = (it == pave->getNodeList().end()) ? startPoint :
project((*it)->mPos);
681 QPointF endPoint = close ? startPoint : nextPoint;
682 pp.quadTo(control, endPoint);
696 startPoint = QPointF();
706 m_pavements.append(pp);
static double distanceToLineSegment(const QVector2D &p, const QVector2D &a, const QVector2D &b, double *outT=NULL)
static double unitLengthAfterMapping(const QTransform &t)
SGSharedPtr< FGTaxiway > FGTaxiwayRef
SGSharedPtr< FGHelipad > FGHelipadRef
SGSharedPtr< FGPavement > FGPavementRef
SGSharedPtr< FGAirport > FGAirportRef
SGSharedPtr< FGRunway > FGRunwayRef
SGSharedPtr< FGParking > FGParkingRef
void setApproachExtension(QuantityValue distance)
void clicked(QmlPositioned *pos)
void paintContents(QPainter *) override
void setApproachExtensionEnabled(bool e)
void doComputeBounds() override
qlonglong airportGuid() const
void setAirportGuid(qlonglong guid)
void addParking(FGParkingRef park)
QuantityValue approachExtension
void addRunway(FGRunwayRef rwy)
AirportDiagram(QQuickItem *pr=nullptr)
void approachExtensionChanged()
virtual ~AirportDiagram()
void setAirport(FGAirportRef apt)
void mouseReleaseEvent(QMouseEvent *me) override
void setSelection(QmlPositioned *pos)
QmlPositioned * selection
void addHelipad(FGHelipadRef pad)
BaseDiagram(QQuickItem *pr=nullptr)
void clearIgnoredNavaids()
QTransform transform() const
void paintAirplaneIcon(QPainter *painter, const SGGeod &geod, int headingDeg)
SGGeod m_projectionCenter
void extendBounds(const QPointF &p, double radiusM=1.0)
void addIgnoredNavaid(FGPositionedRef pos)
QPointF project(const SGGeod &geod) const
void recomputeBounds(bool resetZoom)
const FGParkingList & allParkings() const
double get_multiuse() const
double localizerWidth() const
return the localizer width, in degrees computation is based up ICAO stdandard width at the runway thr...
virtual const SGGeod & geod() const
const std::string & ident() const
double headingDeg() const
Runway heading in degrees.
SGGeod pointOnCenterline(double aOffset) const
Retrieve a position on the extended centerline.
FGPositionedRef inner() const
static NavDataCache * instance()
T * fgpositioned_cast(FGPositioned *p)