#include <algorithm> // for std::sort
#include <plib/puAux.h>
-#include <simgear/route/waypoint.hxx>
#include <simgear/sg_inlines.h>
#include <simgear/misc/strutils.hxx>
#include <simgear/magvar/magvar.hxx>
#include <Navaids/navrecord.hxx>
#include <Navaids/navlist.hxx>
#include <Navaids/fix.hxx>
-#include <Airports/simple.hxx>
+#include <Airports/airport.hxx>
#include <Airports/runways.hxx>
#include <Main/fg_os.hxx> // fgGetKeyModifiers()
#include <Navaids/routePath.hxx>
+#include <Aircraft/FlightHistory.hxx>
+#include <AIModel/AIAircraft.hxx>
+#include <AIModel/AIFlightPlan.hxx>
const char* RULER_LEGEND_KEY = "ruler-legend";
///////////////////////////////////////////////////////////////////////////
-const int MAX_ZOOM = 16;
+const int MAX_ZOOM = 12;
const int SHOW_DETAIL_ZOOM = 8;
+const int SHOW_DETAIL2_ZOOM = 5;
const int CURSOR_PAN_STEP = 32;
MapWidget::MapWidget(int x, int y, int maxX, int maxY) :
_route = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
_gps = fgGetNode("/instrumentation/gps");
- _zoom = 6;
_width = maxX - x;
_height = maxY - y;
-
+ _hasPanned = false;
+ _projection = PROJECTION_SAMSON_FLAMSTEED;
+ _magneticHeadings = false;
+
MapData::setFont(legendFont);
MapData::setPalette(colour);
MapWidget::~MapWidget()
{
delete _magVar;
+ clearData();
}
void MapWidget::setProperty(SGPropertyNode_ptr prop)
{
_root = prop;
+ int zoom = _root->getIntValue("zoom", -1);
+ if (zoom < 0) {
+ _root->setIntValue("zoom", 6); // default zoom
+ }
+
+// expose MAX_ZOOM to the UI
+ _root->setIntValue("max-zoom", MAX_ZOOM);
_root->setBoolValue("centre-on-aircraft", true);
_root->setBoolValue("draw-data", false);
+ _root->setBoolValue("draw-flight-history", false);
_root->setBoolValue("magnetic-headings", true);
}
void MapWidget::pan(const SGVec2d& delta)
{
+ _hasPanned = true;
_projectionCenter = unproject(-delta);
}
+int MapWidget::zoom() const
+{
+ int z = _root->getIntValue("zoom");
+ SG_CLAMP_RANGE(z, 0, MAX_ZOOM);
+ return z;
+}
+
void MapWidget::zoomIn()
{
- if (_zoom <= 0) {
+ if (zoom() >= MAX_ZOOM) {
return;
}
- --_zoom;
- SG_LOG(SG_GENERAL, SG_INFO, "zoom is now:" << _zoom);
+ _root->setIntValue("zoom", zoom() + 1);
}
void MapWidget::zoomOut()
{
- if (_zoom >= MAX_ZOOM) {
+ if (zoom() <= 0) {
return;
}
- ++_zoom;
- SG_LOG(SG_GENERAL, SG_INFO, "zoom is now:" << _zoom);
+ _root->setIntValue("zoom", zoom() - 1);
}
void MapWidget::draw(int dx, int dy)
{
- _aircraft = SGGeod::fromDeg(fgGetDouble("/position/longitude-deg"),
- fgGetDouble("/position/latitude-deg"));
- _magneticHeadings = _root->getBoolValue("magnetic-headings");
-
- if (_root->getBoolValue("centre-on-aircraft")) {
+ _aircraft = globals->get_aircraft_position();
+
+ bool mag = _root->getBoolValue("magnetic-headings");
+ if (mag != _magneticHeadings) {
+ clearData(); // flush cached data text, since it often includes heading
+ _magneticHeadings = mag;
+ }
+
+ if (_hasPanned) {
+ _root->setBoolValue("centre-on-aircraft", false);
+ _hasPanned = false;
+ }
+ else if (_root->getBoolValue("centre-on-aircraft")) {
_projectionCenter = _aircraft;
}
_upHeading = 0.0;
}
+ _cachedZoom = MAX_ZOOM - zoom();
SGGeod topLeft = unproject(SGVec2d(_width/2, _height/2));
// compute draw range, including a fudge factor for ILSs and other 'long'
// symbols
drawAirports();
drawNavaids();
+ drawPOIs();
drawTraffic();
drawGPSData();
drawNavRadio(fgGetNode("/instrumentation/nav[0]", false));
drawNavRadio(fgGetNode("/instrumentation/nav[1]", false));
paintAircraftLocation(_aircraft);
+ drawFlightHistory();
paintRoute();
paintRuler();
double dist, az, az2;
SGGeodesy::inverse(_aircraft, _clickGeod, az, az2, dist);
- if (_magneticHeadings) {
- az -= _magVar->get_magvar();
- SG_NORMALIZE_RANGE(az, 0.0, 360.0);
- }
-
char buffer[1024];
::snprintf(buffer, 1024, "%03d/%.1fnm",
- SGMiscd::roundToInt(az), dist * SG_METER_TO_NM);
+ displayHeading(az), dist * SG_METER_TO_NM);
MapData* d = getOrCreateDataForKey((void*) RULER_LEGEND_KEY);
d->setLabel(buffer);
return;
}
- RoutePath path(_route->waypts());
+ RoutePath path(_route->flightPlan());
// first pass, draw the actual lines
glLineWidth(2.0);
} // of second waypoint iteration
}
+void MapWidget::drawFlightHistory()
+{
+ FGFlightHistory* history = (FGFlightHistory*) globals->get_subsystem("history");
+ if (!history || !_root->getBoolValue("draw-flight-history")) {
+ return;
+ }
+
+ // first pass, draw the actual lines
+ glLineWidth(2.0);
+
+ SGGeodVec gv(history->pathForHistory());
+ glColor4f(0.0, 0.0, 1.0, 0.7);
+
+ glBegin(GL_LINE_STRIP);
+ for (unsigned int i=0; i<gv.size(); ++i) {
+ SGVec2d p = project(gv[i]);
+ glVertex2d(p.x(), p.y());
+ }
+
+ glEnd();
+}
+
/**
* Round a SGGeod to an arbitrary precision.
* For example, passing precision of 0.5 will round to the nearest 0.5 of
{
_heliports = nd->getBoolValue("show-heliports", false);
_hardRunwaysOnly = nd->getBoolValue("hard-surfaced-airports", true);
- _minLengthFt = nd->getDoubleValue("min-runway-length-ft", 2000.0);
+ _minLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 2000);
}
virtual FGPositioned::Type maxType() const {
void MapWidget::drawAirports()
{
MapAirportFilter af(_root);
- FGPositioned::List apts = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &af);
+ bool partial = false;
+ FGPositionedList apts = FGPositioned::findWithinRangePartial(_projectionCenter, _drawRangeNm, &af, partial);
for (unsigned int i=0; i<apts.size(); ++i) {
drawAirport((FGAirport*) apts[i].get());
}
virtual bool pass(FGPositioned* aPos) const {
if (_fixes && (aPos->type() == FGPositioned::FIX)) {
// ignore fixes which end in digits - expirmental
- if (isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) {
+ if (aPos->ident().length() > 4 && isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) {
return false;
}
}
}
virtual FGPositioned::Type minType() const {
- return _fixes ? FGPositioned::FIX : FGPositioned::VOR;
+ return _fixes ? FGPositioned::FIX : FGPositioned::NDB;
}
virtual FGPositioned::Type maxType() const {
- return _navaids ? FGPositioned::NDB : FGPositioned::FIX;
+ return _navaids ? FGPositioned::VOR : FGPositioned::FIX;
}
private:
NavaidFilter f(fixes, _root->getBoolValue("draw-navaids"));
if (f.minType() <= f.maxType()) {
- FGPositioned::List navs = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &f);
+ FGPositionedList navs = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &f);
glLineWidth(1.0);
for (unsigned int i=0; i<navs.size(); ++i) {
} // of navaids || fixes are drawn test
}
+void MapWidget::drawPOIs()
+{
+ FGPositioned::TypeFilter f(FGPositioned::COUNTRY);
+ f.addType(FGPositioned::CITY);
+ f.addType(FGPositioned::TOWN);
+ FGPositionedList poi = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &f);
+
+ glLineWidth(1.0);
+ for (unsigned int i=0; i<poi.size(); ++i) {
+ FGPositioned::Type ty = poi[i]->type();
+ if (ty == FGPositioned::COUNTRY) {
+ drawCountries((FGNavRecord*) poi[i].get());
+ } else if (ty == FGPositioned::CITY) {
+ drawCities((FGNavRecord*) poi[i].get());
+ } else if (ty == FGPositioned::TOWN) {
+ drawTowns((FGNavRecord*) poi[i].get());
+ }
+ } // of navaid iteration
+}
+
void MapWidget::drawNDB(bool tuned, FGNavRecord* ndb)
{
SGVec2d pos = project(ndb->geod());
glColor3f(0.0, 0.0, 1.0);
}
- circleAt(pos, 6, 8);
+ circleAt(pos, 6, 9);
+ circleAt(pos, 8, 1);
+
+ if (vor->hasDME())
+ squareAt(pos, 9);
if (validDataForKey(vor)) {
setAnchorForKey(vor, pos);
glColor3f(0.0, 0.0, 0.0);
circleAt(pos, 3, 6);
- if (_zoom > SHOW_DETAIL_ZOOM) {
+ if (_cachedZoom > SHOW_DETAIL_ZOOM) {
return; // hide fix labels beyond a certain zoom level
}
// identify the tuned station - unfortunately we don't get lat/lon directly,
// need to do the frequency search again
double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0);
- FGNavRecord* nav = globals->get_navlist()->findByFreq(mhz, _aircraft);
+
+ FGNavRecord* nav = FGNavList::findByFreq(mhz, _aircraft,
+ FGNavList::navFilter());
if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) {
// mismatch between navradio selection logic and ours!
return;
void MapWidget::drawTunedLocalizer(SGPropertyNode_ptr radio)
{
double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0);
- FGNavRecord* loc = globals->get_loclist()->findByFreq(mhz, _aircraft);
+ FGNavRecord* loc = FGNavList::findByFreq(mhz, _aircraft, FGNavList::locFilter());
if (!loc || (loc->ident() != radio->getStringValue("nav-id"))) {
// mismatch between navradio selection logic and ours!
return;
}
}
+void MapWidget::drawCountries(FGNavRecord* rec)
+{
+ if (_cachedZoom < 9) {
+ return; // hide labels beyond a certain zoom level
+ }
+
+ SGVec2d pos = project(rec->geod());
+ glColor3f(1.0, 1.0, 0.0);
+
+ circleAt(pos, 4, 10);
+
+ if (validDataForKey(rec)) {
+ setAnchorForKey(rec, pos);
+ return;
+ }
+
+ char buffer[1024];
+ ::snprintf(buffer, 1024, "%s",
+ rec->name().c_str());
+
+ MapData* d = createDataForKey(rec);
+ d->setPriority(200);
+ d->setLabel(rec->ident());
+ d->setText(buffer);
+ d->setOffset(MapData::HALIGN_CENTER | MapData::VALIGN_BOTTOM, 10);
+ d->setAnchor(pos);
+
+}
+
+void MapWidget::drawCities(FGNavRecord* rec)
+{
+ if (_cachedZoom > SHOW_DETAIL_ZOOM) {
+ return; // hide labels beyond a certain zoom level
+ }
+
+ SGVec2d pos = project(rec->geod());
+ glColor3f(0.0, 1.0, 0.0);
+
+ circleAt(pos, 4, 8);
+
+ if (validDataForKey(rec)) {
+ setAnchorForKey(rec, pos);
+ return;
+ }
+
+ char buffer[1024];
+ ::snprintf(buffer, 1024, "%s",
+ rec->name().c_str());
+
+ MapData* d = createDataForKey(rec);
+ d->setPriority(40);
+ d->setLabel(rec->ident());
+ d->setText(buffer);
+ d->setOffset(MapData::HALIGN_CENTER | MapData::VALIGN_BOTTOM, 10);
+ d->setAnchor(pos);
+
+}
+
+void MapWidget::drawTowns(FGNavRecord* rec)
+{
+ if (_cachedZoom > SHOW_DETAIL2_ZOOM) {
+ return; // hide labels beyond a certain zoom level
+ }
+
+ SGVec2d pos = project(rec->geod());
+ glColor3f(0.2, 1.0, 0.0);
+
+ circleAt(pos, 4, 5);
+
+ if (validDataForKey(rec)) {
+ setAnchorForKey(rec, pos);
+ return;
+ }
+
+ char buffer[1024];
+ ::snprintf(buffer, 1024, "%s",
+ rec->name().c_str());
+
+ MapData* d = createDataForKey(rec);
+ d->setPriority(40);
+ d->setLabel(rec->ident());
+ d->setText(buffer);
+ d->setOffset(MapData::HALIGN_CENTER | MapData::VALIGN_BOTTOM, 10);
+ d->setAnchor(pos);
+
+}
+
/*
void MapWidget::drawObstacle(FGPositioned* obs)
{
// draw tower location
SGVec2d towerPos = project(apt->getTowerLocation());
- if (_zoom <= SHOW_DETAIL_ZOOM) {
+ if (_cachedZoom <= SHOW_DETAIL_ZOOM) {
glColor3f(1.0, 1.0, 1.0);
glLineWidth(1.0);
d->setAnchor(towerPos);
}
- if (_zoom > SHOW_DETAIL_ZOOM) {
+ if (_cachedZoom > SHOW_DETAIL_ZOOM) {
return;
}
- for (unsigned int r=0; r<apt->numRunways(); ++r) {
- FGRunway* rwy = apt->getRunwayByIndex(r);
- if (!rwy->isReciprocal()) {
- drawRunwayPre(rwy);
- }
+ FGRunwayList runways(apt->getRunwaysWithoutReciprocals());
+
+ for (unsigned int r=0; r<runways.size(); ++r) {
+ drawRunwayPre(runways[r]);
}
- for (unsigned int r=0; r<apt->numRunways(); ++r) {
- FGRunway* rwy = apt->getRunwayByIndex(r);
- if (!rwy->isReciprocal()) {
- drawRunway(rwy);
- }
+ for (unsigned int r=0; r<runways.size(); ++r) {
+ FGRunway* rwy = runways[r];
+ drawRunway(rwy);
- if (rwy->ILS()) {
- drawILS(false, rwy);
- }
- } // of runway iteration
+ if (rwy->ILS()) {
+ drawILS(false, rwy);
+ }
+
+ if (rwy->reciprocalRunway()) {
+ FGRunway* recip = rwy->reciprocalRunway();
+ if (recip->ILS()) {
+ drawILS(false, recip);
+ }
+ }
+ }
+
+ for (unsigned int r=0; r<apt->numHelipads(); ++r) {
+ FGHelipad* hp = apt->getHelipadByIndex(r);
+ drawHelipad(hp);
+ } // of runway iteration
}
bool needHardSurface = _root->getBoolValue("hard-surfaced-airports", true);
double minLength = _root->getDoubleValue("min-runway-length-ft", 2000.0);
- int score = 0;
- unsigned int numRunways(apt->numRunways());
- for (unsigned int r=0; r<numRunways; ++r) {
- FGRunway* rwy = apt->getRunwayByIndex(r);
- if (rwy->isReciprocal()) {
- continue;
- }
+ FGRunwayList runways(apt->getRunwaysWithoutReciprocals());
+ int score = 0;
+ for (unsigned int r=0; r<runways.size(); ++r) {
+ FGRunway* rwy = runways[r];
if (needHardSurface && !rwy->isHardSurface()) {
continue;
}
setAnchorForKey(rwy, (p1 + p2) * 0.5);
return;
}
-
+
char buffer[1024];
- ::snprintf(buffer, 1024, "%s/%s\n%3.0f/%3.0f\n%.0f'",
+ ::snprintf(buffer, 1024, "%s/%s\n%03d/%03d\n%.0f'",
rwy->ident().c_str(),
rwy->reciprocalRunway()->ident().c_str(),
- rwy->headingDeg(),
- rwy->reciprocalRunway()->headingDeg(),
+ displayHeading(rwy->headingDeg()),
+ displayHeading(rwy->reciprocalRunway()->headingDeg()),
rwy->lengthFt());
MapData* d = createDataForKey(rwy);
}
char buffer[1024];
- ::snprintf(buffer, 1024, "%s\n%s\n%3.2fMHz",
- loc->name().c_str(), loc->ident().c_str(),loc->get_freq()/100.0);
+ ::snprintf(buffer, 1024, "%s\n%s\n%03d - %3.2fMHz",
+ loc->ident().c_str(), loc->name().c_str(),
+ displayHeading(radial),
+ loc->get_freq()/100.0);
MapData* d = createDataForKey(loc);
d->setPriority(40);
return;
}
- if (_zoom > SHOW_DETAIL_ZOOM) {
+ if (_cachedZoom > SHOW_DETAIL_ZOOM) {
return;
}
} // of ai/models iteration
}
+void MapWidget::drawHelipad(FGHelipad* hp)
+{
+ SGVec2d pos = project(hp->geod());
+ glLineWidth(1.0);
+ glColor3f(1.0, 0.0, 1.0);
+ circleAt(pos, 16, 5.0);
+
+ if (validDataForKey(hp)) {
+ setAnchorForKey(hp, pos);
+ return;
+ }
+
+ char buffer[1024];
+ ::snprintf(buffer, 1024, "%s\n%03d\n%.0f'",
+ hp->ident().c_str(),
+ displayHeading(hp->headingDeg()),
+ hp->lengthFt());
+
+ MapData* d = createDataForKey(hp);
+ d->setText(buffer);
+ d->setLabel(hp->ident());
+ d->setPriority(40);
+ d->setOffset(MapData::HALIGN_CENTER | MapData::VALIGN_BOTTOM, 8);
+ d->setAnchor(pos);
+}
+
void MapWidget::drawAIAircraft(const SGPropertyNode* model, const SGGeod& pos, double hdg)
{
drawLine(p, project(advance));
}
-
- if (validDataForKey((void*) model)) {
- setAnchorForKey((void*) model, p);
- return;
- }
+
+ // try to access the flight-plan of the aircraft. There are several layers
+ // of potential NULL-ness here, so we have to be defensive at each stage.
+ std::string originICAO, destinationICAO;
+ FGAIManager* aiManager = static_cast<FGAIManager*>(globals->get_subsystem("ai-model"));
+ FGAIBasePtr aircraft = aiManager ? aiManager->getObjectFromProperty(model) : NULL;
+ if (aircraft) {
+ FGAIAircraft* p = static_cast<FGAIAircraft*>(aircraft.get());
+ if (p->GetFlightPlan()) {
+ originICAO = p->GetFlightPlan()->departureAirport()->ident();
+ destinationICAO = p->GetFlightPlan()->arrivalAirport()->ident();
+ }
+ }
// draw callsign / altitude / speed
-
-
- char buffer[1024];
- ::snprintf(buffer, 1024, "%s\n%d'\n%dkts",
- model->getStringValue("callsign", "<>"),
- static_cast<int>(pos.getElevationFt() / 50.0) * 50,
- speedKts);
-
- MapData* d = createDataForKey((void*) model);
- d->setText(buffer);
- d->setLabel(model->getStringValue("callsign", "<>"));
- d->setPriority(speedKts > 5 ? 60 : 10); // low priority for parked aircraft
- d->setOffset(MapData::VALIGN_CENTER | MapData::HALIGN_LEFT, 10);
- d->setAnchor(p);
-
+ int altFt50 = static_cast<int>(pos.getElevationFt() / 50.0) * 50;
+ std::ostringstream ss;
+ ss << model->getStringValue("callsign", "<>");
+ if (speedKts > 1) {
+ ss << "\n" << altFt50 << "' " << speedKts << "kts";
+ }
+
+ if (!originICAO.empty() || ! destinationICAO.empty()) {
+ ss << "\n" << originICAO << " -> " << destinationICAO;
+ }
+
+ MapData* d = getOrCreateDataForKey((void*) model);
+ d->setText(ss.str().c_str());
+ d->setLabel(model->getStringValue("callsign", "<>"));
+ d->setPriority(speedKts > 5 ? 60 : 10); // low priority for parked aircraft
+ d->setOffset(MapData::VALIGN_CENTER | MapData::HALIGN_LEFT, 10);
+ d->setAnchor(p);
}
void MapWidget::drawAIShip(const SGPropertyNode* model, const SGGeod& pos, double hdg)
drawLine(p, project(advance));
}
- if (validDataForKey((void*) model)) {
- setAnchorForKey((void*) model, p);
- return;
- }
-
// draw callsign / speed
char buffer[1024];
::snprintf(buffer, 1024, "%s\n%dkts",
model->getStringValue("name", "<>"),
speedKts);
- MapData* d = createDataForKey((void*) model);
+ MapData* d = getOrCreateDataForKey((void*) model);
d->setText(buffer);
d->setLabel(model->getStringValue("name", "<>"));
d->setPriority(speedKts > 2 ? 30 : 10); // low priority for slow moving ships
SGVec2d MapWidget::project(const SGGeod& geod) const
{
- // Sanson-Flamsteed projection, relative to the projection center
+ SGVec2d p;
double r = earth_radius_lat(geod.getLatitudeRad());
- double lonDiff = geod.getLongitudeRad() - _projectionCenter.getLongitudeRad(),
- latDiff = geod.getLatitudeRad() - _projectionCenter.getLatitudeRad();
-
- SGVec2d p = SGVec2d(cos(geod.getLatitudeRad()) * lonDiff, latDiff) * r * currentScale();
-
+
+ switch (_projection) {
+ case PROJECTION_SAMSON_FLAMSTEED:
+ {
+ // Sanson-Flamsteed projection, relative to the projection center
+ double lonDiff = geod.getLongitudeRad() - _projectionCenter.getLongitudeRad(),
+ latDiff = geod.getLatitudeRad() - _projectionCenter.getLatitudeRad();
+
+ p = SGVec2d(cos(geod.getLatitudeRad()) * lonDiff, latDiff) * r * currentScale();
+ break;
+ }
+
+ case PROJECTION_ORTHO_AZIMUTH:
+ {
+ // http://mathworld.wolfram.com/OrthographicProjection.html
+ double cosTheta = cos(geod.getLatitudeRad());
+ double sinDLambda = sin(geod.getLongitudeRad() - _projectionCenter.getLongitudeRad());
+ double cosDLambda = cos(geod.getLongitudeRad() - _projectionCenter.getLongitudeRad());
+ double sinTheta1 = sin(_projectionCenter.getLatitudeRad());
+ double sinTheta = sin(geod.getLatitudeRad());
+ double cosTheta1 = cos(_projectionCenter.getLatitudeRad());
+
+ p = SGVec2d(cosTheta * sinDLambda,
+ (cosTheta1 * sinTheta) - (sinTheta1 * cosTheta * cosDLambda)) * r * currentScale();
+ break;
+ }
+
+ case PROJECTION_SPHERICAL:
+ {
+ SGVec3d cartCenter = SGVec3d::fromGeod(_projectionCenter);
+ SGVec3d cartPt = SGVec3d::fromGeod(geod) - cartCenter;
+
+ // rotate relative to projection center
+ SGQuatd orient = SGQuatd::fromLonLat(_projectionCenter);
+ cartPt = orient.rotateBack(cartPt);
+ return SGVec2d(cartPt.y(), cartPt.x()) * currentScale();
+ break;
+ }
+ } // of projection mode switch
+
+
// rotate as necessary
double cost = cos(_upHeading * SG_DEGREES_TO_RADIANS),
sint = sin(_upHeading * SG_DEGREES_TO_RADIANS);
SGVec2d ur(cost * p.x() - sint * p.y(),
sint * p.x() + cost * p.y());
- double r = earth_radius_lat(_projectionCenter.getLatitudeRad());
- SGVec2d unscaled = ur * (1.0 / (currentScale() * r));
+
- double lat = unscaled.y() + _projectionCenter.getLatitudeRad();
- double lon = (unscaled.x() / cos(lat)) + _projectionCenter.getLongitudeRad();
-
- return SGGeod::fromRad(lon, lat);
+ switch (_projection) {
+ case PROJECTION_SAMSON_FLAMSTEED:
+ {
+ double r = earth_radius_lat(_projectionCenter.getLatitudeRad());
+ SGVec2d unscaled = ur * (1.0 / (currentScale() * r));
+ double lat = unscaled.y() + _projectionCenter.getLatitudeRad();
+ double lon = (unscaled.x() / cos(lat)) + _projectionCenter.getLongitudeRad();
+ return SGGeod::fromRad(lon, lat);
+ }
+
+ case PROJECTION_ORTHO_AZIMUTH:
+ {
+ double r = earth_radius_lat(_projectionCenter.getLatitudeRad());
+ SGVec2d unscaled = ur * (1.0 / (currentScale() * r));
+
+ double phi = length(p);
+ double c = asin(phi);
+ double sinTheta1 = sin(_projectionCenter.getLatitudeRad());
+ double cosTheta1 = cos(_projectionCenter.getLatitudeRad());
+
+ double lat = asin(cos(c) * sinTheta1 + ((unscaled.y() * sin(c) * cosTheta1) / phi));
+ double lon = _projectionCenter.getLongitudeRad() +
+ atan((unscaled.x()* sin(c)) / (phi * cosTheta1 * cos(c) - unscaled.y() * sinTheta1 * sin(c)));
+ return SGGeod::fromRad(lon, lat);
+ }
+
+ case PROJECTION_SPHERICAL:
+ {
+ SGVec2d unscaled = ur * (1.0 / currentScale());
+ SGQuatd orient = SGQuatd::fromLonLat(_projectionCenter);
+ SGVec3d cartCenter = SGVec3d::fromGeod(_projectionCenter);
+ SGVec3d cartPt = orient.rotate(SGVec3d(unscaled.x(), unscaled.y(), 0.0));
+ return SGGeod::fromCart(cartPt + cartCenter);
+ }
+ } // of projection mode switch
}
double MapWidget::currentScale() const
{
- return 1.0 / pow(2.0, _zoom);
+ return 1.0 / pow(2.0, _cachedZoom);
}
void MapWidget::circleAt(const SGVec2d& center, int nSides, double r)
{
glBegin(GL_LINE_LOOP);
double advance = (SGD_PI * 2) / nSides;
- glVertex2d(center.x(), center.y() + r);
+ glVertex2d(center.x() +r, center.y());
double t=advance;
for (int i=1; i<nSides; ++i) {
- glVertex2d(center.x() + (sin(t) * r), center.y() + (cos(t) * r));
+ glVertex2d(center.x() + (cos(t) * r), center.y() + (sin(t) * r));
t += advance;
}
glEnd();
}
+void MapWidget::squareAt(const SGVec2d& center, double r)
+{
+ glBegin(GL_LINE_LOOP);
+ glVertex2d(center.x() + r, center.y() + r);
+ glVertex2d(center.x() + r, center.y() - r);
+ glVertex2d(center.x() - r, center.y() - r);
+ glVertex2d(center.x() - r, center.y() + r);
+ glEnd();
+}
+
void MapWidget::circleAtAlt(const SGVec2d& center, int nSides, double r, double r2)
{
glBegin(GL_LINE_LOOP);
d->resetAge();
return d;
}
+
+void MapWidget::clearData()
+{
+ KeyDataMap::iterator it = _mapData.begin();
+ for (; it != _mapData.end(); ++it) {
+ delete it->second;
+ }
+
+ _mapData.clear();
+}
+
+int MapWidget::displayHeading(double h) const
+{
+ if (_magneticHeadings) {
+ h -= _magVar->get_magvar() * SG_RADIANS_TO_DEGREES;
+ }
+
+ SG_NORMALIZE_RANGE(h, 0.0, 360.0);
+ return SGMiscd::roundToInt(h);
+}