1
0
Fork 0

Cap number of airports displayed in diagrams

- restrict heliports / seaports by aircraft type
- prioritise by runway length
- clean up airport label names
This commit is contained in:
James Turner 2015-11-17 07:36:54 +00:00
parent 1010caeaf6
commit e356e691b2
8 changed files with 241 additions and 87 deletions

View file

@ -47,14 +47,18 @@ const int STANDARD_THUMBNAIL_WIDTH = 172;
using namespace simgear::pkg;
AircraftItem::AircraftItem() :
excluded(false)
excluded(false),
usesHeliports(false),
usesSeaports(false)
{
// oh for C++11 initialisers
for (int i=0; i<4; ++i) ratings[i] = 0;
}
AircraftItem::AircraftItem(QDir dir, QString filePath) :
excluded(false)
excluded(false),
usesHeliports(false),
usesSeaports(false)
{
for (int i=0; i<4; ++i) ratings[i] = 0;
@ -89,6 +93,21 @@ AircraftItem::AircraftItem(QDir dir, QString filePath) :
if (sim->hasChild("variant-of")) {
variantOf = sim->getStringValue("variant-of");
}
if (sim->hasChild("tags")) {
SGPropertyNode_ptr tagsNode = sim->getChild("tags");
int nChildren = tagsNode->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode* c = tagsNode->getChild(i);
if (strcmp(c->getName(), "tag") == 0) {
const char* tagName = c->getStringValue();
usesHeliports |= (strcmp(tagName, "helicopter") == 0);
// could also consider vtol tag?
usesSeaports |= (strcmp(tagName, "seaplane") == 0);
usesSeaports |= (strcmp(tagName, "floats") == 0);
}
} // of tags iteration
} // of set-xml has tags
}
QString AircraftItem::baseName() const
@ -574,6 +593,10 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, quint32 variantIn
"I'm just a poor boy, I need no sympathy because I'm easy come, easy go."
"Litte high, little low. Anywhere the wind blows.";
#endif
} else if (role == AircraftIsHelicopterRole) {
return item->usesHeliports;
} else if (role == AircraftIsSeaplaneRole) {
return item->usesSeaports;
}
return QVariant();

View file

@ -46,6 +46,8 @@ const int AircraftPackageSizeRole = Qt::UserRole + 12;
const int AircraftInstallDownloadedSizeRole = Qt::UserRole + 13;
const int AircraftURIRole = Qt::UserRole + 14;
const int AircraftThumbnailSizeRole = Qt::UserRole + 15;
const int AircraftIsHelicopterRole = Qt::UserRole + 16;
const int AircraftIsSeaplaneRole = Qt::UserRole + 17;
const int AircraftRatingRole = Qt::UserRole + 100;
const int AircraftVariantDescriptionRole = Qt::UserRole + 200;
@ -79,8 +81,9 @@ struct AircraftItem
int ratings[4];
QString variantOf;
QDateTime pathModTime;
QList<AircraftItemPtr> variants;
bool usesHeliports;
bool usesSeaports;
private:
mutable QPixmap m_thumbnail;
};

View file

@ -31,6 +31,8 @@
#include <Navaids/positioned.hxx>
#include <Airports/airport.hxx>
#include "QtLauncher_fwd.hxx"
/* equatorial and polar earth radius */
const float rec = 6378137; // earth radius, equator (?)
const float rpol = 6356752.314f; // earth radius, polar (?)
@ -133,30 +135,61 @@ void BaseDiagram::paintAirplaneIcon(QPainter* painter, const SGGeod& geod, int h
class MapFilter : public FGPositioned::TypeFilter
{
public:
MapFilter()
MapFilter(LauncherAircraftType aircraft)
{
// addType(FGPositioned::FIX);
addType(FGPositioned::AIRPORT);
addType(FGPositioned::HELIPORT);
addType(FGPositioned::SEAPORT);
addType(FGPositioned::NDB);
addType(FGPositioned::VOR);
if (aircraft == Helicopter) {
addType(FGPositioned::HELIPAD);
}
if (aircraft == Seaplane) {
addType(FGPositioned::SEAPORT);
} else {
addType(FGPositioned::AIRPORT);
}
}
virtual bool pass(FGPositioned* aPos) const
{
bool ok = TypeFilter::pass(aPos);
// fix-filtering code disabled since fixed are entirely disabled
#if 0
if (ok && (aPos->type() == FGPositioned::FIX)) {
// ignore fixes which end in digits
if (aPos->ident().length() > 4 && isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) {
return false;
}
}
#endif
return ok;
}
};
void BaseDiagram::splitItems(const FGPositionedList& in, FGPositionedList& navaids,
FGPositionedList& ports)
{
FGPositionedList::const_iterator it = in.begin();
for (; it != in.end(); ++it) {
if (FGAirport::isAirportType(it->ptr())) {
ports.push_back(*it);
} else {
navaids.push_back(*it);
}
}
}
bool orderAirportsByRunwayLength(const FGPositionedRef& a,
const FGPositionedRef& b)
{
FGAirport* aptA = static_cast<FGAirport*>(a.ptr());
FGAirport* aptB = static_cast<FGAirport*>(b.ptr());
return aptA->longestRunway()->lengthFt() > aptB->longestRunway()->lengthFt();
}
void BaseDiagram::paintNavaids(QPainter* painter)
{
@ -169,87 +202,106 @@ void BaseDiagram::paintNavaids(QPainter* painter)
SGGeod viewCenter = unproject(invT.map(rect().center()), m_projectionCenter);
SGGeod bottomRight = unproject(invT.map(rect().bottomRight()), m_projectionCenter);
double minRunwayLengthFt = (16 / m_scale) * SG_METER_TO_FEET;
double drawRangeNm = std::max(SGGeodesy::distanceNm(viewCenter, topLeft),
SGGeodesy::distanceNm(viewCenter, bottomRight));
MapFilter f;
MapFilter f(m_aircraftType);
FGPositionedList items = FGPositioned::findWithinRange(viewCenter, drawRangeNm, &f);
FGPositionedList navaids, ports;
splitItems(items, navaids, ports);
if (ports.size() >= 40) {
FGPositionedList::iterator middle = ports.begin() + 40;
std::partial_sort(ports.begin(), middle, ports.end(),
orderAirportsByRunwayLength);
ports.resize(40);
}
m_labelRects.clear();
m_labelRects.reserve(items.size());
FGPositionedList::const_iterator it;
for (it = items.begin(); it != items.end(); ++it) {
FGPositionedRef pos(*it);
bool drawAsIcon = true;
if (isNavaidIgnored(pos))
continue;
FGPositioned::Type ty(pos->type());
if (ty == FGPositioned::AIRPORT) {
FGAirport* apt = static_cast<FGAirport*>(pos.ptr());
if (apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) {
drawAsIcon = false;
painter->setTransform(xf);
QVector<QLineF> lines = projectAirportRuwaysWithCenter(apt, m_projectionCenter);
QPen pen(QColor(0x03, 0x83, 0xbf), 8);
pen.setCosmetic(true);
painter->setPen(pen);
painter->drawLines(lines);
QPen linePen(Qt::white, 2);
linePen.setCosmetic(true);
painter->setPen(linePen);
painter->drawLines(lines);
painter->resetTransform();
}
}
if (drawAsIcon) {
QPixmap pm = iconForPositioned(pos);
QPointF loc = xf.map(project(pos->geod()));
QRect iconRect = pm.rect();
iconRect.moveCenter(loc.toPoint());
painter->drawPixmap(iconRect, pm);
bool isNDB = (ty == FGPositioned::NDB);
// compute label text so we can measure it
QString label;
if (FGAirport::isAirportType(pos.ptr())) {
label = QString::fromStdString((*it)->name());
} else {
label = QString::fromStdString((*it)->ident());
}
if (ty == FGPositioned::NDB) {
FGNavRecord* nav = static_cast<FGNavRecord*>(pos.ptr());
label.append("\n").append(QString::number(nav->get_freq() / 100));
} else if (ty == FGPositioned::VOR) {
FGNavRecord* nav = static_cast<FGNavRecord*>(pos.ptr());
label.append("\n").append(QString::number(nav->get_freq() / 100.0, 'f', 1));
}
QRect textBounds = painter->boundingRect(QRect(0, 0, 100, 100),
Qt::TextWordWrap, label);
int textFlags;
textBounds = rectAndFlagsForLabel(pos->guid(), iconRect,
textBounds.size(),
textFlags);
painter->setPen(isNDB ? QColor(0x9b, 0x5d, 0xa2) : QColor(0x03, 0x83, 0xbf));
painter->drawText(textBounds, textFlags, label);
}
for (it = ports.begin(); it != ports.end(); ++it) {
paintNavaid(painter, xf, *it);
}
for (it = navaids.begin(); it != navaids.end(); ++it) {
paintNavaid(painter, xf, *it);
}
// restore transform
painter->setTransform(xf);
}
void BaseDiagram::paintNavaid(QPainter* painter, const QTransform& t, const FGPositionedRef &pos)
{
bool drawAsIcon = true;
if (isNavaidIgnored(pos))
return;
const double minRunwayLengthFt = (16 / m_scale) * SG_METER_TO_FEET;
FGPositioned::Type ty(pos->type());
if (ty == FGPositioned::AIRPORT) {
FGAirport* apt = static_cast<FGAirport*>(pos.ptr());
if (apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) {
drawAsIcon = false;
painter->setTransform(t);
QVector<QLineF> lines = projectAirportRuwaysWithCenter(apt, m_projectionCenter);
QPen pen(QColor(0x03, 0x83, 0xbf), 8);
pen.setCosmetic(true);
painter->setPen(pen);
painter->drawLines(lines);
QPen linePen(Qt::white, 2);
linePen.setCosmetic(true);
painter->setPen(linePen);
painter->drawLines(lines);
painter->resetTransform();
}
}
if (drawAsIcon) {
QPixmap pm = iconForPositioned(pos);
QPointF loc = t.map(project(pos->geod()));
QRect iconRect = pm.rect();
iconRect.moveCenter(loc.toPoint());
painter->drawPixmap(iconRect, pm);
bool isNDB = (ty == FGPositioned::NDB);
// compute label text so we can measure it
QString label;
if (FGAirport::isAirportType(pos.ptr())) {
label = QString::fromStdString(pos->name());
label = fixNavaidName(label);
} else {
label = QString::fromStdString(pos->ident());
}
if (ty == FGPositioned::NDB) {
FGNavRecord* nav = static_cast<FGNavRecord*>(pos.ptr());
label.append("\n").append(QString::number(nav->get_freq() / 100));
} else if (ty == FGPositioned::VOR) {
FGNavRecord* nav = static_cast<FGNavRecord*>(pos.ptr());
label.append("\n").append(QString::number(nav->get_freq() / 100.0, 'f', 1));
}
QRect textBounds = painter->boundingRect(QRect(0, 0, 100, 100),
Qt::TextWordWrap, label);
int textFlags;
textBounds = rectAndFlagsForLabel(pos->guid(), iconRect,
textBounds.size(),
textFlags);
painter->setPen(isNDB ? QColor(0x9b, 0x5d, 0xa2) : QColor(0x03, 0x83, 0xbf));
painter->drawText(textBounds, textFlags, label);
}
}
bool BaseDiagram::isNavaidIgnored(const FGPositionedRef &pos) const
{
return m_ignored.contains(pos);
@ -626,6 +678,12 @@ QVector<QLineF> BaseDiagram::projectAirportRuwaysWithCenter(FGAirportRef apt, co
return r;
}
void BaseDiagram::setAircraftType(LauncherAircraftType type)
{
m_aircraftType = type;
update();
}
QVector<QLineF> BaseDiagram::projectAirportRuwaysIntoRect(FGAirportRef apt, const QRectF &bounds)
{
QVector<QLineF> r = projectAirportRuwaysWithCenter(apt, apt->geod());

View file

@ -30,6 +30,8 @@
#include <Navaids/positioned.hxx>
#include <Airports/airport.hxx>
#include "QtLauncher_fwd.hxx"
class BaseDiagram : public QWidget
{
Q_OBJECT
@ -50,6 +52,8 @@ public:
static QVector<QLineF> projectAirportRuwaysIntoRect(FGAirportRef apt, const QRectF& bounds);
static QVector<QLineF> projectAirportRuwaysWithCenter(FGAirportRef apt, const SGGeod &c);
void setAircraftType(LauncherAircraftType type);
protected:
virtual void paintEvent(QPaintEvent* pe);
@ -80,6 +84,7 @@ protected:
QPointF m_panOffset, m_lastMousePos;
int m_wheelAngleDeltaAccumulator;
bool m_didPan;
LauncherAircraftType m_aircraftType;
static void extendRect(QRectF& r, const QPointF& p);
@ -118,6 +123,11 @@ private:
mutable QVector<QRect> m_labelRects;
static int textFlagsForLabelPosition(LabelPosition pos);
void splitItems(const FGPositionedList &in, FGPositionedList &navaids, FGPositionedList &ports);
void paintNavaid(QPainter *painter,
const QTransform& t,
const FGPositionedRef &pos);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(BaseDiagram::IconOptions)

View file

@ -59,11 +59,21 @@ QString fixNavaidName(QString s)
continue;
}
if (up == "MUNI") {
if (up == "CO") {
changedWords.append("County");
continue;
}
if ((up == "MUNI") || (up == "MUN")) {
changedWords.append("Municipal");
continue;
}
if (up == "MEM") {
changedWords.append("Memorial");
continue;
}
if (up == "RGNL") {
changedWords.append("Regional");
continue;
@ -85,11 +95,19 @@ QString fixNavaidName(QString s)
continue;
}
if ((up == "VOR") || (up == "NDB") || (up == "VOR-DME") || (up == "VORTAC") || (up == "NDB-DME")) {
if ((up == "VOR") || (up == "NDB")
|| (up == "VOR-DME") || (up == "VORTAC")
|| (up == "NDB-DME")
|| (up == "AFB") || (up == "RAF"))
{
changedWords.append(w);
continue;
}
if ((up =="[X]") || (up == "[H]") || (up == "[S]")) {
continue; // consume
}
QChar firstChar = w.at(0).toUpper();
w = w.mid(1).toLower();
w.prepend(firstChar);
@ -171,14 +189,21 @@ FGPositionedList loadPositionedList(QVariant v)
class IdentSearchFilter : public FGPositioned::TypeFilter
{
public:
IdentSearchFilter()
IdentSearchFilter(LauncherAircraftType aircraft)
{
addType(FGPositioned::AIRPORT);
addType(FGPositioned::SEAPORT);
addType(FGPositioned::HELIPAD);
addType(FGPositioned::VOR);
addType(FGPositioned::FIX);
addType(FGPositioned::NDB);
if (aircraft == Helicopter) {
addType(FGPositioned::HELIPAD);
}
if (aircraft == Seaplane) {
addType(FGPositioned::SEAPORT);
} else {
addType(FGPositioned::AIRPORT);
}
}
};
@ -191,7 +216,7 @@ public:
{
}
void setSearch(QString t)
void setSearch(QString t, LauncherAircraftType aircraft)
{
beginResetModel();
@ -200,7 +225,7 @@ public:
std::string term(t.toUpper().toStdString());
IdentSearchFilter filter;
IdentSearchFilter filter(aircraft);
FGPositionedList exactMatches = NavDataCache::instance()->findAllWithIdent(term, &filter, true);
for (unsigned int i=0; i<exactMatches.size(); ++i) {
@ -318,7 +343,8 @@ private:
LocationWidget::LocationWidget(QWidget *parent) :
QWidget(parent),
m_ui(new Ui::LocationWidget)
m_ui(new Ui::LocationWidget),
m_aircraftType(Airplane)
{
m_ui->setupUi(this);
@ -596,7 +622,7 @@ void LocationWidget::onSearch()
return;
}
m_searchModel->setSearch(search);
m_searchModel->setSearch(search, m_aircraftType);
if (m_searchModel->isSearchActive()) {
m_ui->searchStatusText->setText(QString("Searching for '%1'").arg(search));
@ -868,6 +894,14 @@ void LocationWidget::setBaseLocation(FGPositionedRef ref)
updateDescription();
}
void LocationWidget::setAircraftType(LauncherAircraftType ty)
{
m_aircraftType = ty;
// nothing happens until next search
m_ui->navaidDiagram->setAircraftType(ty);
m_ui->airportDiagram->setAircraftType(ty);
}
void LocationWidget::onOffsetDataChanged()
{
m_ui->navaidDiagram->setOffsetEnabled(m_ui->offsetGroup->isChecked());

View file

@ -28,6 +28,8 @@
#include <Navaids/positioned.hxx>
#include <Airports/airports_fwd.hxx>
#include "QtLauncher_fwd.hxx"
namespace Ui {
class LocationWidget;
}
@ -49,6 +51,8 @@ public:
void setBaseLocation(FGPositionedRef ref);
void setAircraftType(LauncherAircraftType ty);
bool shouldStartPaused() const;
void setLocationOptions();
@ -87,8 +91,7 @@ private:
QToolButton* m_backButton;
FGPositionedList m_recentLocations;
LauncherAircraftType m_aircraftType;
};
#endif // LOCATIONWIDGET_H

View file

@ -864,7 +864,6 @@ void QtLauncher::onAircraftInstallFailed(QModelIndex index, QString errorMessage
void QtLauncher::onAircraftSelected(const QModelIndex& index)
{
m_selectedAircraft = index.data(AircraftURIRole).toUrl();
qDebug() << "selected aircraft is now" << m_selectedAircraft;
updateSelectedAircraft();
}
@ -910,6 +909,15 @@ void QtLauncher::updateSelectedAircraft()
int status = index.data(AircraftPackageStatusRole).toInt();
bool canRun = (status == PackageInstalled);
m_ui->runButton->setEnabled(canRun);
LauncherAircraftType aircraftType = Airplane;
if (index.data(AircraftIsHelicopterRole).toBool()) {
aircraftType = Helicopter;
} else if (index.data(AircraftIsSeaplaneRole).toBool()) {
aircraftType = Seaplane;
}
m_ui->location->setAircraftType(aircraftType);
} else {
m_ui->thumbnail->setPixmap(QPixmap());
m_ui->aircraftDescription->setText("");

View file

@ -0,0 +1,15 @@
#ifndef QTGUI_FWD_H
#define QTGUI_FWD_H
enum LauncherAircraftType
{
Airplane = 0,
Seaplane,
Helicopter,
Airship
};
extern QString fixNavaidName(QString s);
#endif // QTGUI_FWD_H