Navaid diagram for launcher
- work in progress, needs labels
|
@ -76,6 +76,7 @@ FGAirport::FGAirport( PositionedID aGuid,
|
||||||
_has_metar(has_metar),
|
_has_metar(has_metar),
|
||||||
_dynamics(0),
|
_dynamics(0),
|
||||||
mTowerDataLoaded(false),
|
mTowerDataLoaded(false),
|
||||||
|
mHasTower(false),
|
||||||
mRunwaysLoaded(false),
|
mRunwaysLoaded(false),
|
||||||
mHelipadsLoaded(false),
|
mHelipadsLoaded(false),
|
||||||
mTaxiwaysLoaded(false),
|
mTaxiwaysLoaded(false),
|
||||||
|
@ -314,6 +315,20 @@ bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FGRunwayRef FGAirport::longestRunway() const
|
||||||
|
{
|
||||||
|
FGRunwayRef r;
|
||||||
|
loadRunways();
|
||||||
|
|
||||||
|
BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
|
||||||
|
if (!r || (r->lengthFt() < rwy->lengthFt())) {
|
||||||
|
r = rwy;
|
||||||
|
}
|
||||||
|
} // of runways iteration
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
FGRunwayList FGAirport::getRunways() const
|
FGRunwayList FGAirport::getRunways() const
|
||||||
{
|
{
|
||||||
|
@ -663,13 +678,14 @@ void FGAirport::validateTowerData() const
|
||||||
NavDataCache* cache = NavDataCache::instance();
|
NavDataCache* cache = NavDataCache::instance();
|
||||||
PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
|
PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
|
||||||
if (towers.empty()) {
|
if (towers.empty()) {
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "No towers defined for:" <<ident());
|
mHasTower = false;
|
||||||
mTowerPosition = geod(); // use airport position
|
mTowerPosition = geod(); // use airport position
|
||||||
// increase tower elevation by 20 metres above the field elevation
|
// increase tower elevation by 20 metres above the field elevation
|
||||||
mTowerPosition.setElevationM(geod().getElevationM() + 20.0);
|
mTowerPosition.setElevationM(geod().getElevationM() + 20.0);
|
||||||
} else {
|
} else {
|
||||||
FGPositionedRef tower = cache->loadById(towers.front());
|
FGPositionedRef tower = cache->loadById(towers.front());
|
||||||
mTowerPosition = tower->geod();
|
mTowerPosition = tower->geod();
|
||||||
|
mHasTower = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SGPath path;
|
SGPath path;
|
||||||
|
@ -681,6 +697,7 @@ void FGAirport::validateTowerData() const
|
||||||
SGPropertyNode_ptr rootNode = new SGPropertyNode;
|
SGPropertyNode_ptr rootNode = new SGPropertyNode;
|
||||||
readProperties(path.str(), rootNode);
|
readProperties(path.str(), rootNode);
|
||||||
const_cast<FGAirport*>(this)->readTowerData(rootNode);
|
const_cast<FGAirport*>(this)->readTowerData(rootNode);
|
||||||
|
mHasTower = true;
|
||||||
} catch (sg_exception& e){
|
} catch (sg_exception& e){
|
||||||
SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading twr XML failed:" << e.getFormattedMessage());
|
SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading twr XML failed:" << e.getFormattedMessage());
|
||||||
}
|
}
|
||||||
|
@ -723,6 +740,12 @@ void FGAirport::validateILSData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FGAirport::hasTower() const
|
||||||
|
{
|
||||||
|
validateTowerData();
|
||||||
|
return mHasTower;
|
||||||
|
}
|
||||||
|
|
||||||
void FGAirport::readILSData(SGPropertyNode* aRoot)
|
void FGAirport::readILSData(SGPropertyNode* aRoot)
|
||||||
{
|
{
|
||||||
NavDataCache* cache = NavDataCache::instance();
|
NavDataCache* cache = NavDataCache::instance();
|
||||||
|
|
|
@ -73,6 +73,8 @@ class FGAirport : public FGPositioned
|
||||||
*/
|
*/
|
||||||
void validateILSData();
|
void validateILSData();
|
||||||
|
|
||||||
|
bool hasTower() const;
|
||||||
|
|
||||||
SGGeod getTowerLocation() const;
|
SGGeod getTowerLocation() const;
|
||||||
|
|
||||||
void setMetar(bool value) { _has_metar = value; }
|
void setMetar(bool value) { _has_metar = value; }
|
||||||
|
@ -142,6 +144,8 @@ class FGAirport : public FGPositioned
|
||||||
*/
|
*/
|
||||||
bool hasHardRunwayOfLengthFt(double aLengthFt) const;
|
bool hasHardRunwayOfLengthFt(double aLengthFt) const;
|
||||||
|
|
||||||
|
FGRunwayRef longestRunway() const;
|
||||||
|
|
||||||
unsigned int numTaxiways() const;
|
unsigned int numTaxiways() const;
|
||||||
FGTaxiwayRef getTaxiwayByIndex(unsigned int aIndex) const;
|
FGTaxiwayRef getTaxiwayByIndex(unsigned int aIndex) const;
|
||||||
FGTaxiwayList getTaxiways() const;
|
FGTaxiwayList getTaxiways() const;
|
||||||
|
@ -319,6 +323,7 @@ private:
|
||||||
void loadProcedures() const;
|
void loadProcedures() const;
|
||||||
|
|
||||||
mutable bool mTowerDataLoaded;
|
mutable bool mTowerDataLoaded;
|
||||||
|
mutable bool mHasTower;
|
||||||
mutable SGGeod mTowerPosition;
|
mutable SGGeod mTowerPosition;
|
||||||
|
|
||||||
mutable bool mRunwaysLoaded;
|
mutable bool mRunwaysLoaded;
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
#include <Navaids/navrecord.hxx>
|
||||||
|
#include <Navaids/positioned.hxx>
|
||||||
|
#include <Airports/airport.hxx>
|
||||||
|
|
||||||
/* equatorial and polar earth radius */
|
/* equatorial and polar earth radius */
|
||||||
const float rec = 6378137; // earth radius, equator (?)
|
const float rec = 6378137; // earth radius, equator (?)
|
||||||
const float rpol = 6356752.314f; // earth radius, polar (?)
|
const float rpol = 6356752.314f; // earth radius, polar (?)
|
||||||
|
@ -62,6 +66,21 @@ QTransform BaseDiagram::transform() const
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseDiagram::extendRect(QRectF &r, const QPointF &p)
|
||||||
|
{
|
||||||
|
if (p.x() < r.left()) {
|
||||||
|
r.setLeft(p.x());
|
||||||
|
} else if (p.x() > r.right()) {
|
||||||
|
r.setRight(p.x());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.y() < r.top()) {
|
||||||
|
r.setTop(p.y());
|
||||||
|
} else if (p.y() > r.bottom()) {
|
||||||
|
r.setBottom(p.y());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BaseDiagram::paintEvent(QPaintEvent* pe)
|
void BaseDiagram::paintEvent(QPaintEvent* pe)
|
||||||
{
|
{
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
|
@ -79,9 +98,92 @@ void BaseDiagram::paintEvent(QPaintEvent* pe)
|
||||||
QTransform t(transform());
|
QTransform t(transform());
|
||||||
p.setTransform(t);
|
p.setTransform(t);
|
||||||
|
|
||||||
|
paintNavaids(&p);
|
||||||
|
|
||||||
paintContents(&p);
|
paintContents(&p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MapFilter : public FGPositioned::TypeFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MapFilter()
|
||||||
|
{
|
||||||
|
// addType(FGPositioned::FIX);
|
||||||
|
addType(FGPositioned::AIRPORT);
|
||||||
|
addType(FGPositioned::NDB);
|
||||||
|
addType(FGPositioned::VOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool pass(FGPositioned* aPos) const
|
||||||
|
{
|
||||||
|
bool ok = TypeFilter::pass(aPos);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void BaseDiagram::paintNavaids(QPainter* painter)
|
||||||
|
{
|
||||||
|
QTransform xf = painter->transform();
|
||||||
|
painter->setTransform(QTransform()); // reset to identity
|
||||||
|
QTransform invT = xf.inverted();
|
||||||
|
SGGeod topLeft = unproject(invT.map(QPointF(0,0)), m_projectionCenter);
|
||||||
|
|
||||||
|
double minRunwayLengthFt = (16 / m_scale) * SG_METER_TO_FEET;
|
||||||
|
// add 10nm fudge factor
|
||||||
|
double drawRangeNm = SGGeodesy::distanceNm(m_projectionCenter, topLeft) + 10.0;
|
||||||
|
//qDebug() << "draw range computed as:" << drawRangeNm;
|
||||||
|
|
||||||
|
MapFilter f;
|
||||||
|
FGPositionedList items = FGPositioned::findWithinRange(m_projectionCenter, drawRangeNm, &f);
|
||||||
|
|
||||||
|
// pass 0 - icons
|
||||||
|
|
||||||
|
FGPositionedList::const_iterator it;
|
||||||
|
for (it = items.begin(); it != items.end(); ++it) {
|
||||||
|
bool drawAsIcon = true;
|
||||||
|
|
||||||
|
if ((*it)->type() == FGPositioned::AIRPORT) {
|
||||||
|
FGAirport* apt = static_cast<FGAirport*>(it->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(*it);
|
||||||
|
QPointF loc = xf.map(project((*it)->geod()));
|
||||||
|
loc -= QPointF(pm.width() >> 1, pm.height() >> 1);
|
||||||
|
painter->drawPixmap(loc, pm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore transform
|
||||||
|
painter->setTransform(xf);
|
||||||
|
}
|
||||||
|
|
||||||
void BaseDiagram::mousePressEvent(QMouseEvent *me)
|
void BaseDiagram::mousePressEvent(QMouseEvent *me)
|
||||||
{
|
{
|
||||||
m_lastMousePos = me->pos();
|
m_lastMousePos = me->pos();
|
||||||
|
@ -132,9 +234,8 @@ void BaseDiagram::wheelEvent(QWheelEvent *we)
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseDiagram::paintContents(QPainter*)
|
void BaseDiagram::paintContents(QPainter* painter)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseDiagram::recomputeBounds(bool resetZoom)
|
void BaseDiagram::recomputeBounds(bool resetZoom)
|
||||||
|
@ -158,24 +259,14 @@ void BaseDiagram::doComputeBounds()
|
||||||
|
|
||||||
void BaseDiagram::extendBounds(const QPointF& p)
|
void BaseDiagram::extendBounds(const QPointF& p)
|
||||||
{
|
{
|
||||||
if (p.x() < m_bounds.left()) {
|
extendRect(m_bounds, p);
|
||||||
m_bounds.setLeft(p.x());
|
|
||||||
} else if (p.x() > m_bounds.right()) {
|
|
||||||
m_bounds.setRight(p.x());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.y() < m_bounds.top()) {
|
QPointF BaseDiagram::project(const SGGeod& geod, const SGGeod& center)
|
||||||
m_bounds.setTop(p.y());
|
|
||||||
} else if (p.y() > m_bounds.bottom()) {
|
|
||||||
m_bounds.setBottom(p.y());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF BaseDiagram::project(const SGGeod& geod) const
|
|
||||||
{
|
{
|
||||||
double r = earth_radius_lat(geod.getLatitudeRad());
|
double r = earth_radius_lat(geod.getLatitudeRad());
|
||||||
double ref_lat = m_projectionCenter.getLatitudeRad(),
|
double ref_lat = center.getLatitudeRad(),
|
||||||
ref_lon = m_projectionCenter.getLongitudeRad(),
|
ref_lon = center.getLongitudeRad(),
|
||||||
lat = geod.getLatitudeRad(),
|
lat = geod.getLatitudeRad(),
|
||||||
lon = geod.getLongitudeRad(),
|
lon = geod.getLongitudeRad(),
|
||||||
lonDiff = lon - ref_lon;
|
lonDiff = lon - ref_lon;
|
||||||
|
@ -206,3 +297,170 @@ QPointF BaseDiagram::project(const SGGeod& geod) const
|
||||||
|
|
||||||
return QPointF(x, -y) * r;
|
return QPointF(x, -y) * r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SGGeod BaseDiagram::unproject(const QPointF& xy, const SGGeod& center)
|
||||||
|
{
|
||||||
|
double r = earth_radius_lat(center.getLatitudeRad());
|
||||||
|
double lat = 0,
|
||||||
|
lon = 0,
|
||||||
|
ref_lat = center.getLatitudeRad(),
|
||||||
|
ref_lon = center.getLongitudeRad(),
|
||||||
|
rho = QVector2D(xy).length(),
|
||||||
|
c = rho/r;
|
||||||
|
|
||||||
|
if (rho == 0) {
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
|
||||||
|
double x = xy.x(), y = xy.y();
|
||||||
|
lat = asin( cos(c) * sin(ref_lat) + (y * sin(c) * cos(ref_lat)) / rho);
|
||||||
|
|
||||||
|
if (ref_lat == (90 * SG_DEGREES_TO_RADIANS)) // north pole
|
||||||
|
{
|
||||||
|
lon = ref_lon + atan(-x/y);
|
||||||
|
}
|
||||||
|
else if (ref_lat == -(90 * SG_DEGREES_TO_RADIANS)) // south pole
|
||||||
|
{
|
||||||
|
lon = ref_lon + atan(x/y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lon = ref_lon + atan(x* sin(c) / (rho * cos(ref_lat) * cos(c) - y * sin(ref_lat) * sin(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return SGGeod::fromRad(lon, lat);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF BaseDiagram::project(const SGGeod& geod) const
|
||||||
|
{
|
||||||
|
return project(geod, m_projectionCenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap BaseDiagram::iconForPositioned(const FGPositionedRef& pos)
|
||||||
|
{
|
||||||
|
// if airport type, check towered or untowered
|
||||||
|
|
||||||
|
bool isTowered = false;
|
||||||
|
if (FGAirport::isAirportType(pos)) {
|
||||||
|
FGAirport* apt = static_cast<FGAirport*>(pos.ptr());
|
||||||
|
isTowered = apt->hasTower();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pos->type()) {
|
||||||
|
case FGPositioned::VOR:
|
||||||
|
// check for VORTAC
|
||||||
|
|
||||||
|
if (static_cast<FGNavRecord*>(pos.ptr())->hasDME())
|
||||||
|
return QPixmap(":/vor-dme-icon");
|
||||||
|
|
||||||
|
return QPixmap(":/vor-icon");
|
||||||
|
|
||||||
|
case FGPositioned::AIRPORT:
|
||||||
|
return iconForAirport(static_cast<FGAirport*>(pos.ptr()));
|
||||||
|
|
||||||
|
case FGPositioned::HELIPORT:
|
||||||
|
return QPixmap(":/heliport-icon");
|
||||||
|
case FGPositioned::SEAPORT:
|
||||||
|
return QPixmap(isTowered ? ":/seaport-tower-icon" : ":/seaport-icon");
|
||||||
|
case FGPositioned::NDB:
|
||||||
|
return QPixmap(":/ndb-icon");
|
||||||
|
case FGPositioned::FIX:
|
||||||
|
return QPixmap(":/waypoint-icon");
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QPixmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap BaseDiagram::iconForAirport(FGAirport* apt)
|
||||||
|
{
|
||||||
|
if (!apt->hasHardRunwayOfLengthFt(1500)) {
|
||||||
|
return QPixmap(apt->hasTower() ? ":/airport-tower-icon" : ":/airport-icon");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apt->hasHardRunwayOfLengthFt(8500)) {
|
||||||
|
QPixmap result(32, 32);
|
||||||
|
result.fill(Qt::transparent);
|
||||||
|
{
|
||||||
|
QPainter p(&result);
|
||||||
|
p.setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
QRectF b = result.rect().adjusted(4, 4, -4, -4);
|
||||||
|
QVector<QLineF> lines = projectAirportRuwaysIntoRect(apt, b);
|
||||||
|
|
||||||
|
p.setPen(QPen(QColor(0x03, 0x83, 0xbf), 8));
|
||||||
|
p.drawLines(lines);
|
||||||
|
|
||||||
|
p.setPen(QPen(Qt::white, 2));
|
||||||
|
p.drawLines(lines);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap result(25, 25);
|
||||||
|
result.fill(Qt::transparent);
|
||||||
|
|
||||||
|
{
|
||||||
|
QPainter p(&result);
|
||||||
|
p.setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
|
||||||
|
p.setBrush(apt->hasTower() ? QColor(0x03, 0x83, 0xbf) :
|
||||||
|
QColor(0x9b, 0x5d, 0xa2));
|
||||||
|
p.drawEllipse(QPointF(13, 13), 10, 10);
|
||||||
|
|
||||||
|
FGRunwayRef r = apt->longestRunway();
|
||||||
|
|
||||||
|
p.setPen(QPen(Qt::white, 2));
|
||||||
|
p.translate(13, 13);
|
||||||
|
p.rotate(r->headingDeg());
|
||||||
|
p.drawLine(0, -8, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QLineF> BaseDiagram::projectAirportRuwaysWithCenter(FGAirportRef apt, const SGGeod& c)
|
||||||
|
{
|
||||||
|
QVector<QLineF> r;
|
||||||
|
|
||||||
|
const FGRunwayList& runways(apt->getRunwaysWithoutReciprocals());
|
||||||
|
FGRunwayList::const_iterator it;
|
||||||
|
|
||||||
|
for (it = runways.begin(); it != runways.end(); ++it) {
|
||||||
|
FGRunwayRef rwy = *it;
|
||||||
|
QPointF p1 = project(rwy->geod(), c);
|
||||||
|
QPointF p2 = project(rwy->end(), c);
|
||||||
|
r.append(QLineF(p1, p2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QLineF> BaseDiagram::projectAirportRuwaysIntoRect(FGAirportRef apt, const QRectF &bounds)
|
||||||
|
{
|
||||||
|
QVector<QLineF> r = projectAirportRuwaysWithCenter(apt, apt->geod());
|
||||||
|
|
||||||
|
QRectF extent;
|
||||||
|
Q_FOREACH(const QLineF& l, r) {
|
||||||
|
extendRect(extent, l.p1());
|
||||||
|
extendRect(extent, l.p2());
|
||||||
|
}
|
||||||
|
|
||||||
|
// find constraining scale factor
|
||||||
|
double ratioInX = bounds.width() / extent.width();
|
||||||
|
double ratioInY = bounds.height() / extent.height();
|
||||||
|
|
||||||
|
QTransform t;
|
||||||
|
t.translate(bounds.left(), bounds.top());
|
||||||
|
t.scale(std::min(ratioInX, ratioInY),
|
||||||
|
std::min(ratioInX, ratioInY));
|
||||||
|
t.translate(-extent.left(), -extent.top()); // move unscaled to 0,0
|
||||||
|
|
||||||
|
for (int i=0; i<r.size(); ++i) {
|
||||||
|
r[i] = t.map(r[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
|
@ -26,14 +26,20 @@
|
||||||
|
|
||||||
#include <simgear/math/sg_geodesy.hxx>
|
#include <simgear/math/sg_geodesy.hxx>
|
||||||
|
|
||||||
|
#include <Navaids/positioned.hxx>
|
||||||
|
#include <Airports/airport.hxx>
|
||||||
|
|
||||||
class BaseDiagram : public QWidget
|
class BaseDiagram : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
BaseDiagram(QWidget* pr);
|
BaseDiagram(QWidget* pr);
|
||||||
|
|
||||||
|
static QPixmap iconForPositioned(const FGPositionedRef &pos);
|
||||||
|
static QPixmap iconForAirport(FGAirport *apt);
|
||||||
|
|
||||||
|
static QVector<QLineF> projectAirportRuwaysIntoRect(FGAirportRef apt, const QRectF& bounds);
|
||||||
|
static QVector<QLineF> projectAirportRuwaysWithCenter(FGAirportRef apt, const SGGeod &c);
|
||||||
protected:
|
protected:
|
||||||
virtual void paintEvent(QPaintEvent* pe);
|
virtual void paintEvent(QPaintEvent* pe);
|
||||||
|
|
||||||
|
@ -60,6 +66,15 @@ protected:
|
||||||
QPointF m_panOffset, m_lastMousePos;
|
QPointF m_panOffset, m_lastMousePos;
|
||||||
int m_wheelAngleDeltaAccumulator;
|
int m_wheelAngleDeltaAccumulator;
|
||||||
bool m_didPan;
|
bool m_didPan;
|
||||||
|
|
||||||
|
static void extendRect(QRectF& r, const QPointF& p);
|
||||||
|
|
||||||
|
static QPointF project(const SGGeod &geod, const SGGeod ¢er);
|
||||||
|
|
||||||
|
static SGGeod unproject(const QPointF &xy, const SGGeod ¢er);
|
||||||
|
private:
|
||||||
|
void paintNavaids(QPainter *p);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // of GUI_BASEDIAGRAM_HXX
|
#endif // of GUI_BASEDIAGRAM_HXX
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
|
#include <QMovie>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
#include "AirportDiagram.hxx"
|
#include "AirportDiagram.hxx"
|
||||||
#include "NavaidDiagram.hxx"
|
#include "NavaidDiagram.hxx"
|
||||||
|
@ -202,6 +204,10 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role == Qt::DecorationRole) {
|
||||||
|
return AirportDiagram::iconForPositioned(pos);
|
||||||
|
}
|
||||||
|
|
||||||
if (role == Qt::EditRole) {
|
if (role == Qt::EditRole) {
|
||||||
return QString::fromStdString(pos->ident());
|
return QString::fromStdString(pos->ident());
|
||||||
}
|
}
|
||||||
|
@ -228,7 +234,6 @@ Q_SIGNALS:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
||||||
void onSearchResultsPoll()
|
void onSearchResultsPoll()
|
||||||
{
|
{
|
||||||
PositionedIDVec newIds = m_search->results();
|
PositionedIDVec newIds = m_search->results();
|
||||||
|
@ -267,7 +272,8 @@ LocationWidget::LocationWidget(QWidget *parent) :
|
||||||
QIcon historyIcon(":/history-icon");
|
QIcon historyIcon(":/history-icon");
|
||||||
m_ui->searchHistory->setIcon(historyIcon);
|
m_ui->searchHistory->setIcon(historyIcon);
|
||||||
|
|
||||||
m_ui->searchIcon->setPixmap(QPixmap(":/search-icon"));
|
QByteArray format;
|
||||||
|
m_ui->searchIcon->setMovie(new QMovie(":/spinner", format, this));
|
||||||
|
|
||||||
m_searchModel = new NavSearchModel;
|
m_searchModel = new NavSearchModel;
|
||||||
m_ui->searchResultsList->setModel(m_searchModel);
|
m_ui->searchResultsList->setModel(m_searchModel);
|
||||||
|
@ -310,8 +316,9 @@ LocationWidget::LocationWidget(QWidget *parent) :
|
||||||
this, SLOT(onOffsetDataChanged()));
|
this, SLOT(onOffsetDataChanged()));
|
||||||
|
|
||||||
m_backButton = new QToolButton(this);
|
m_backButton = new QToolButton(this);
|
||||||
m_backButton->setGeometry(0, 0, 32, 32);
|
m_backButton->setGeometry(0, 0, 64, 32);
|
||||||
m_backButton->setIcon(QIcon(":/search-icon"));
|
m_backButton->setText("<< Back");
|
||||||
|
//m_backButton->setIcon(QIcon(":/search-icon"));
|
||||||
m_backButton->raise();
|
m_backButton->raise();
|
||||||
|
|
||||||
connect(m_backButton, &QAbstractButton::clicked,
|
connect(m_backButton, &QAbstractButton::clicked,
|
||||||
|
@ -374,6 +381,9 @@ void LocationWidget::setLocationOptions()
|
||||||
{
|
{
|
||||||
flightgear::Options* opt = flightgear::Options::sharedInstance();
|
flightgear::Options* opt = flightgear::Options::sharedInstance();
|
||||||
|
|
||||||
|
std::string altStr = QString::number(m_ui->altitudeSpinbox->value()).toStdString();
|
||||||
|
std::string vcStr = QString::number(m_ui->airspeedSpinbox->value()).toStdString();
|
||||||
|
|
||||||
if (m_locationIsLatLon) {
|
if (m_locationIsLatLon) {
|
||||||
// bypass the options mechanism because converting to deg:min:sec notation
|
// bypass the options mechanism because converting to deg:min:sec notation
|
||||||
// just to parse back again is nasty.
|
// just to parse back again is nasty.
|
||||||
|
@ -381,6 +391,9 @@ void LocationWidget::setLocationOptions()
|
||||||
fgSetDouble("/position/latitude-deg", m_geodLocation.getLatitudeDeg());
|
fgSetDouble("/position/latitude-deg", m_geodLocation.getLatitudeDeg());
|
||||||
fgSetDouble("/sim/presets/longitude-deg", m_geodLocation.getLongitudeDeg());
|
fgSetDouble("/sim/presets/longitude-deg", m_geodLocation.getLongitudeDeg());
|
||||||
fgSetDouble("/position/longitude-deg", m_geodLocation.getLongitudeDeg());
|
fgSetDouble("/position/longitude-deg", m_geodLocation.getLongitudeDeg());
|
||||||
|
|
||||||
|
opt->addOption("altitude", altStr);
|
||||||
|
opt->addOption("vc", vcStr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,36 +409,80 @@ void LocationWidget::setLocationOptions()
|
||||||
int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt();
|
int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt();
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
// explicit runway choice
|
// explicit runway choice
|
||||||
opt->addOption("runway", apt->getRunwayByIndex(index)->ident());
|
FGRunwayRef runway = apt->getRunwayByIndex(index);
|
||||||
|
opt->addOption("runway", runway->ident());
|
||||||
|
|
||||||
|
// set nav-radio 1 based on selected runway
|
||||||
|
if (runway->ILS()) {
|
||||||
|
double mhz = runway->ILS()->get_freq() / 100.0;
|
||||||
|
QString navOpt = QString("%1:%2").arg(runway->headingDeg()).arg(mhz);
|
||||||
|
opt->addOption("nav1", navOpt.toStdString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_ui->onFinalCheckbox->isChecked()) {
|
if (m_ui->onFinalCheckbox->isChecked()) {
|
||||||
opt->addOption("glideslope", "3.0");
|
opt->addOption("glideslope", "3.0");
|
||||||
opt->addOption("offset-distance", "10.0"); // in nautical miles
|
double offsetNm = m_ui->approachDistanceSpin->value();
|
||||||
|
opt->addOption("offset-distance", QString::number(offsetNm).toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (m_ui->parkingRadio->isChecked()) {
|
} else if (m_ui->parkingRadio->isChecked()) {
|
||||||
// parking selection
|
// parking selection
|
||||||
opt->addOption("parkpos", m_ui->parkingCombo->currentText().toStdString());
|
opt->addOption("parkpos", m_ui->parkingCombo->currentText().toStdString());
|
||||||
}
|
}
|
||||||
// of location is an airport
|
// of location is an airport
|
||||||
}
|
} else {
|
||||||
|
// location is a navaid
|
||||||
|
// note setting the ident here is ambigious, we really only need and
|
||||||
|
// want the 'navaid-id' property. However setting the 'real' option
|
||||||
|
// gives a better UI experience (eg existing Position in Air dialog)
|
||||||
FGPositioned::Type ty = m_location->type();
|
FGPositioned::Type ty = m_location->type();
|
||||||
switch (ty) {
|
switch (ty) {
|
||||||
case FGPositioned::VOR:
|
case FGPositioned::VOR:
|
||||||
case FGPositioned::NDB:
|
opt->addOption("vor", m_location->ident());
|
||||||
case FGPositioned::FIX:
|
setNavRadioOption();
|
||||||
// set disambiguation property
|
break;
|
||||||
globals->get_props()->setIntValue("/sim/presets/navaid-id",
|
|
||||||
static_cast<int>(m_location->guid()));
|
|
||||||
|
|
||||||
// we always set 'fix', but really this is just to force positionInit
|
case FGPositioned::NDB:
|
||||||
// code to check for the navaid-id value above.
|
opt->addOption("ndb", m_location->ident());
|
||||||
|
setNavRadioOption();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FGPositioned::FIX:
|
||||||
opt->addOption("fix", m_location->ident());
|
opt->addOption("fix", m_location->ident());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opt->addOption("altitude", altStr);
|
||||||
|
opt->addOption("vc", vcStr);
|
||||||
|
|
||||||
|
// set disambiguation property
|
||||||
|
globals->get_props()->setIntValue("/sim/presets/navaid-id",
|
||||||
|
static_cast<int>(m_location->guid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocationWidget::setNavRadioOption()
|
||||||
|
{
|
||||||
|
flightgear::Options* opt = flightgear::Options::sharedInstance();
|
||||||
|
|
||||||
|
if (m_location->type() == FGPositioned::VOR) {
|
||||||
|
FGNavRecordRef nav(static_cast<FGNavRecord*>(m_location.ptr()));
|
||||||
|
double mhz = nav->get_freq() / 100.0;
|
||||||
|
int heading = 0; // add heading support
|
||||||
|
QString navOpt = QString("%1:%2").arg(heading).arg(mhz);
|
||||||
|
opt->addOption("nav1", navOpt.toStdString());
|
||||||
|
} else {
|
||||||
|
FGNavRecordRef nav(static_cast<FGNavRecord*>(m_location.ptr()));
|
||||||
|
int khz = nav->get_freq() / 100;
|
||||||
|
int heading = 0;
|
||||||
|
QString adfOpt = QString("%1:%2").arg(heading).arg(khz);
|
||||||
|
qDebug() << "ADF opt is:" << adfOpt;
|
||||||
|
opt->addOption("adf1", adfOpt.toStdString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocationWidget::onSearch()
|
void LocationWidget::onSearch()
|
||||||
|
@ -446,7 +503,9 @@ void LocationWidget::onSearch()
|
||||||
|
|
||||||
if (m_searchModel->isSearchActive()) {
|
if (m_searchModel->isSearchActive()) {
|
||||||
m_ui->searchStatusText->setText(QString("Searching for '%1'").arg(search));
|
m_ui->searchStatusText->setText(QString("Searching for '%1'").arg(search));
|
||||||
|
qDebug() << "setting icon visible";
|
||||||
m_ui->searchIcon->setVisible(true);
|
m_ui->searchIcon->setVisible(true);
|
||||||
|
m_ui->searchIcon->movie()->start();
|
||||||
} else if (m_searchModel->rowCount(QModelIndex()) == 1) {
|
} else if (m_searchModel->rowCount(QModelIndex()) == 1) {
|
||||||
setBaseLocation(m_searchModel->itemAtRow(0));
|
setBaseLocation(m_searchModel->itemAtRow(0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,7 @@ private:
|
||||||
|
|
||||||
void onOffsetEnabledToggled(bool on);
|
void onOffsetEnabledToggled(bool on);
|
||||||
void onBackToSearch();
|
void onBackToSearch();
|
||||||
|
void setNavRadioOption();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LOCATIONWIDGET_H
|
#endif // LOCATIONWIDGET_H
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QStackedWidget" name="stack">
|
<widget class="QStackedWidget" name="stack">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>1</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="airportPage">
|
<widget class="QWidget" name="airportPage">
|
||||||
<layout class="QGridLayout" name="gridLayout" columnstretch="1,0,0,0">
|
<layout class="QGridLayout" name="gridLayout" columnstretch="1,0,0,0">
|
||||||
|
@ -292,6 +292,9 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="searchPage">
|
<widget class="QWidget" name="searchPage">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
|
@ -342,7 +345,7 @@
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="searchStatusText">
|
<widget class="QLabel" name="searchStatusText">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -355,6 +358,12 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="searchIcon">
|
<widget class="QLabel" name="searchIcon">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>16</width>
|
||||||
|
<height>16</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>TextLabel</string>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
@ -82,8 +82,6 @@ void NavaidDiagram::paintContents(QPainter *painter)
|
||||||
SGGeod offsetGeod = SGGeodesy::direct(m_geod, m_offsetBearingDeg, d);
|
SGGeod offsetGeod = SGGeodesy::direct(m_geod, m_offsetBearingDeg, d);
|
||||||
QPointF offset = project(offsetGeod);
|
QPointF offset = project(offsetGeod);
|
||||||
|
|
||||||
qDebug() << base << offset;
|
|
||||||
|
|
||||||
QPen pen(Qt::green);
|
QPen pen(Qt::green);
|
||||||
pen.setCosmetic(true);
|
pen.setCosmetic(true);
|
||||||
painter->setPen(pen);
|
painter->setPen(pen);
|
||||||
|
|
|
@ -787,6 +787,7 @@ void QtLauncher::onRun()
|
||||||
bool startPaused = m_ui->startPausedCheck->isChecked() ||
|
bool startPaused = m_ui->startPausedCheck->isChecked() ||
|
||||||
m_ui->location->shouldStartPaused();
|
m_ui->location->shouldStartPaused();
|
||||||
if (startPaused) {
|
if (startPaused) {
|
||||||
|
qDebug() << "will start paused";
|
||||||
opt->addOption("enable-freeze", "");
|
opt->addOption("enable-freeze", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
src/GUI/airport-icon.png
Normal file
After Width: | Height: | Size: 834 B |
BIN
src/GUI/airport-tower-icon.png
Normal file
After Width: | Height: | Size: 826 B |
BIN
src/GUI/heliport-icon.png
Normal file
After Width: | Height: | Size: 847 B |
Before Width: | Height: | Size: 8.7 KiB |
BIN
src/GUI/ndb-icon.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
|
@ -1,9 +1,19 @@
|
||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file alias="history-icon">history-icon.png</file>
|
<file alias="history-icon">history-icon.png</file>
|
||||||
<file alias="search-icon">large-search-icon.png</file>
|
|
||||||
<file alias="right-arrow-icon">arrow-right-icon.png</file>
|
<file alias="right-arrow-icon">arrow-right-icon.png</file>
|
||||||
<file alias="left-arrow-icon">arrow-left-icon.png</file>
|
<file alias="left-arrow-icon">arrow-left-icon.png</file>
|
||||||
<file alias="app-icon-large">../../icons/128x128/apps/flightgear.png</file>
|
<file alias="app-icon-large">../../icons/128x128/apps/flightgear.png</file>
|
||||||
|
<file alias="spinner">spinner.gif</file>
|
||||||
|
<file alias="heliport-icon">heliport-icon.png</file>
|
||||||
|
<file alias="seaport-icon">seaport-icon.png</file>
|
||||||
|
<file alias="vor-icon">vor-icon.png</file>
|
||||||
|
<file alias="vortac-icon">vortac-icon.png</file>
|
||||||
|
<file alias="waypoint-icon">waypoint-icon.png</file>
|
||||||
|
<file alias="ndb-icon">ndb-icon.png</file>
|
||||||
|
<file alias="airport-icon">airport-icon.png</file>
|
||||||
|
<file alias="airport-tower-icon">airport-tower-icon.png</file>
|
||||||
|
<file alias="vor-dme-icon">vor-dme-icon.png</file>
|
||||||
|
<file alias="seaport-tower-icon">seaport-tower-icon.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
BIN
src/GUI/seaport-icon.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/GUI/seaport-tower-icon.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
src/GUI/spinner.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/GUI/vor-dme-icon.png
Normal file
After Width: | Height: | Size: 754 B |
BIN
src/GUI/vor-icon.png
Normal file
After Width: | Height: | Size: 758 B |
BIN
src/GUI/vortac-icon.png
Normal file
After Width: | Height: | Size: 828 B |
BIN
src/GUI/waypoint-icon.png
Normal file
After Width: | Height: | Size: 565 B |
|
@ -353,8 +353,19 @@ static void fgSetDistOrAltFromGlideSlope() {
|
||||||
|
|
||||||
|
|
||||||
// Set current_options lon/lat given an airport id and heading (degrees)
|
// Set current_options lon/lat given an airport id and heading (degrees)
|
||||||
static bool fgSetPosFromNAV( const string& id, const double& freq, FGPositioned::Type type )
|
static bool fgSetPosFromNAV( const string& id,
|
||||||
|
const double& freq,
|
||||||
|
FGPositioned::Type type,
|
||||||
|
PositionedID guid)
|
||||||
{
|
{
|
||||||
|
FGNavRecord* nav = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if (guid != 0) {
|
||||||
|
nav = FGPositioned::loadById<FGNavRecord>(guid);
|
||||||
|
if (!nav)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
FGNavList::TypeFilter filter(type);
|
FGNavList::TypeFilter filter(type);
|
||||||
const nav_list_type navlist = FGNavList::findByIdentAndFreq( id.c_str(), freq, &filter );
|
const nav_list_type navlist = FGNavList::findByIdentAndFreq( id.c_str(), freq, &filter );
|
||||||
|
|
||||||
|
@ -381,7 +392,10 @@ static bool fgSetPosFromNAV( const string& id, const double& freq, FGPositioned:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FGNavRecord *nav = navlist[0];
|
// nav list must be of length 1
|
||||||
|
nav = navlist[0];
|
||||||
|
}
|
||||||
|
|
||||||
fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg"));
|
fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -429,7 +443,7 @@ static bool fgSetPosFromCarrier( const string& carrier, const string& posid ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set current_options lon/lat given an airport id and heading (degrees)
|
// Set current_options lon/lat given a fix ident and GUID
|
||||||
static bool fgSetPosFromFix( const string& id, PositionedID guid )
|
static bool fgSetPosFromFix( const string& id, PositionedID guid )
|
||||||
{
|
{
|
||||||
FGPositioned* fix = NULL;
|
FGPositioned* fix = NULL;
|
||||||
|
@ -558,14 +572,14 @@ bool initPosition()
|
||||||
|
|
||||||
if ( !set_pos && !vor.empty() ) {
|
if ( !set_pos && !vor.empty() ) {
|
||||||
// a VOR is requested
|
// a VOR is requested
|
||||||
if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR ) ) {
|
if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR, navaidId ) ) {
|
||||||
set_pos = true;
|
set_pos = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !set_pos && !ndb.empty() ) {
|
if ( !set_pos && !ndb.empty() ) {
|
||||||
// an NDB is requested
|
// an NDB is requested
|
||||||
if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB ) ) {
|
if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB, navaidId ) ) {
|
||||||
set_pos = true;
|
set_pos = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|