]> git.mxchange.org Git - flightgear.git/blobdiff - src/GUI/MapWidget.cxx
Canvas: Proper fix for OpenVG init handling.
[flightgear.git] / src / GUI / MapWidget.cxx
index 0e06b18b936907c33b4093f6701fc1d4ccd7861b..e92e7106dcdced946450a35910486636aac7ed6b 100644 (file)
@@ -8,7 +8,6 @@
 #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 <Airports/simple.hxx>
 #include <Airports/runways.hxx>
 #include <Main/fg_os.hxx>      // fgGetKeyModifiers()
+#include <Navaids/routePath.hxx>
 
 const char* RULER_LEGEND_KEY = "ruler-legend";
-  
+
 /* equatorial and polar earth radius */
 const float rec  = 6378137;          // earth radius, equator (?)
 const float rpol = 6356752.314f;      // earth radius, polar   (?)
 
 /************************************************************************
-  some trigonometric helper functions 
+  some trigonometric helper functions
   (translated more or less directly from Alexei Novikovs perl original)
 *************************************************************************/
 
@@ -63,8 +63,8 @@ static bool puBoxIntersect(const puBox& a, const puBox& b)
   int y0 = SG_MAX2(a.min[1], b.min[1]);
   int x1 = SG_MIN2(a.max[0], b.max[0]);
   int y1 = SG_MIN2(a.max[1], b.max[1]);
-  
-  return (x0 <= x1) && (y0 <= y1); 
+
+  return (x0 <= x1) && (y0 <= y1);
 }
 
 class MapData;
@@ -76,11 +76,11 @@ public:
   static const int HALIGN_LEFT = 1;
   static const int HALIGN_CENTER = 2;
   static const int HALIGN_RIGHT = 3;
-  
+
   static const int VALIGN_TOP = 1 << 4;
   static const int VALIGN_CENTER = 2 << 4;
   static const int VALIGN_BOTTOM = 3 << 4;
-  
+
   MapData(int priority) :
     _dirtyText(true),
     _age(0),
@@ -92,151 +92,151 @@ public:
     _dataVisible(false)
   {
   }
-  
+
   void setLabel(const std::string& label)
   {
     if (label == _label) {
       return; // common case, and saves invalidation
     }
-    
+
     _label = label;
     _dirtyText = true;
   }
-  
+
   void setText(const std::string &text)
   {
     if (_rawText == text) {
       return; // common case, and saves invalidation
     }
-    
+
     _rawText = text;
     _dirtyText = true;
   }
-  
+
   void setDataVisible(bool vis) {
     if (vis == _dataVisible) {
       return;
     }
-    
+
     if (_rawText.empty()) {
       vis = false;
     }
-    
+
     _dataVisible = vis;
     _dirtyText = true;
   }
-  
+
   static void setFont(puFont f)
   {
     _font = f;
     _fontHeight = f.getStringHeight();
     _fontDescender = f.getStringDescender();
   }
-  
+
   static void setPalette(puColor* pal)
   {
     _palette = pal;
   }
-  
+
   void setPriority(int pri)
   {
     _priority = pri;
   }
-  
+
   int priority() const
   { return _priority; }
-  
+
   void setAnchor(const SGVec2d& anchor)
   {
     _anchor = anchor;
   }
-  
+
   void setOffset(int direction, int px)
   {
     if ((_offsetPx == px) && (_offsetDir == direction)) {
       return;
     }
-    
+
     _dirtyOffset = true;
     _offsetDir = direction;
     _offsetPx = px;
   }
-  
+
   bool isClipped(const puBox& vis) const
   {
     validate();
     if ((_width < 1) || (_height < 1)) {
       return true;
     }
-    
+
     return !puBoxIntersect(vis, box());
   }
-  
+
   bool overlaps(const MapDataVec& l) const
   {
     validate();
     puBox b(box());
-        
+
     MapDataVec::const_iterator it;
     for (it = l.begin(); it != l.end(); ++it) {
       if (puBoxIntersect(b, (*it)->box())) {
         return true;
       }
     } // of list iteration
-    
+
     return false;
   }
-  
+
   puBox box() const
   {
     validate();
     return makePuBox(
-      _anchor.x() + _offset.x(), 
+      _anchor.x() + _offset.x(),
       _anchor.y() + _offset.y(),
       _width, _height);
   }
-  
+
   void draw()
   {
     validate();
-    
+
     int xx = _anchor.x() + _offset.x();
     int yy = _anchor.y() + _offset.y();
-    
+
     if (_dataVisible) {
       puBox box(makePuBox(0,0,_width, _height));
       int border = 1;
       box.draw(xx, yy, PUSTYLE_DROPSHADOW, _palette, FALSE, border);
-      
+
       // draw lines
       int lineHeight = _fontHeight;
       int xPos = xx + MARGIN;
       int yPos = yy + _height - (lineHeight + MARGIN);
       glColor3f(0.8, 0.8, 0.8);
-      
+
       for (unsigned int ln=0; ln<_lines.size(); ++ln) {
         _font.drawString(_lines[ln].c_str(), xPos, yPos);
         yPos -= lineHeight + LINE_LEADING;
       }
-    } else {      
+    } else {
       glColor3f(0.8, 0.8, 0.8);
       _font.drawString(_label.c_str(), xx, yy + _fontDescender);
     }
   }
-  
+
   void age()
   {
     ++_age;
   }
-  
+
   void resetAge()
   {
     _age = 0;
   }
-    
+
   bool isExpired() const
   { return (_age > 100); }
-  
+
   static bool order(MapData* a, MapData* b)
   {
     return a->_priority > b->_priority;
@@ -248,92 +248,92 @@ private:
       if (_dirtyOffset) {
         computeOffset();
       }
-      
+
       return;
     }
-    
+
     if (_dataVisible) {
       measureData();
     } else {
       measureLabel();
     }
-  
+
     computeOffset();
     _dirtyText = false;
   }
-  
+
   void measureData() const
   {
     _lines = simgear::strutils::split(_rawText, "\n");
   // measure text to find width and height
     _width = -1;
     _height = 0;
-       
+
     for (unsigned int ln=0; ln<_lines.size(); ++ln) {
       _height += _fontHeight;
       if (ln > 0) {
         _height += LINE_LEADING;
       }
-      
+
       int lw = _font.getStringWidth(_lines[ln].c_str());
       _width = std::max(_width, lw);
     } // of line measurement
-       
+
     if ((_width < 1) || (_height < 1)) {
       // will be clipped
       return;
     }
-  
+
     _height += MARGIN * 2;
     _width += MARGIN * 2;
   }
-  
+
   void measureLabel() const
   {
     if (_label.empty()) {
       _width = _height = -1;
       return;
     }
-    
+
     _height = _fontHeight;
     _width = _font.getStringWidth(_label.c_str());
   }
-  
+
   void computeOffset() const
   {
     _dirtyOffset = false;
     if ((_width <= 0) || (_height <= 0)) {
       return;
     }
-    
+
     int hOffset = 0;
     int vOffset = 0;
-        
+
     switch (_offsetDir & 0x0f) {
     default:
     case HALIGN_LEFT:
       hOffset = _offsetPx;
       break;
-      
+
     case HALIGN_CENTER:
       hOffset = -(_width>>1);
       break;
-      
+
     case HALIGN_RIGHT:
       hOffset = -(_offsetPx + _width);
       break;
     }
-    
+
     switch (_offsetDir & 0xf0) {
     default:
     case VALIGN_TOP:
       vOffset = -(_offsetPx + _height);
       break;
-      
+
     case VALIGN_CENTER:
       vOffset = -(_height>>1);
       break;
-      
+
     case VALIGN_BOTTOM:
       vOffset = _offsetPx;
       break;
@@ -341,10 +341,10 @@ private:
 
     _offset = SGVec2d(hOffset, vOffset);
   }
-  
+
   static const int LINE_LEADING = 3;
        static const int MARGIN = 3;
-  
+
   mutable bool _dirtyText;
   mutable bool _dirtyOffset;
   int _age;
@@ -358,7 +358,7 @@ private:
   int _offsetPx;
   mutable SGVec2d _offset;
   bool _dataVisible;
-  
+
   static puFont _font;
   static puColor* _palette;
   static int _fontHeight;
@@ -372,34 +372,43 @@ int MapData::_fontDescender = 0;
 
 ///////////////////////////////////////////////////////////////////////////
 
-const int MAX_ZOOM = 16;
+const int MAX_ZOOM = 12;
 const int SHOW_DETAIL_ZOOM = 8;
 const int CURSOR_PAN_STEP = 32;
 
-MapWidget::MapWidget(int x, int y, int maxX, int maxY) : 
+MapWidget::MapWidget(int x, int y, int maxX, int maxY) :
   puObject(x,y,maxX, maxY)
 {
   _route = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
   _gps = fgGetNode("/instrumentation/gps");
-  
-  _zoom = 6;
+
   _width = maxX - x;
   _height = maxY - y;
+  _hasPanned = false;
+  _orthoAzimuthProject = false;
   
   MapData::setFont(legendFont);
   MapData::setPalette(colour);
-  
+
   _magVar = new SGMagVar();
 }
 
 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("magnetic-headings", true);
@@ -416,29 +425,29 @@ void MapWidget::setSize(int w, int h)
 
 void MapWidget::doHit( int button, int updown, int x, int y )
 {
-  puObject::doHit(button, updown, x, y);  
-  if (updown == PU_DRAG) {    
+  puObject::doHit(button, updown, x, y);
+  if (updown == PU_DRAG) {
     handlePan(x, y);
     return;
   }
-  
+
   if (button == 3) { // mouse-wheel up
     zoomIn();
   } else if (button == 4) { // mouse-wheel down
     zoomOut();
   }
-  
+
   if (button != active_mouse_button) {
     return;
   }
-  
+
   _hitLocation = SGVec2d(x - abox.min[0], y - abox.min[1]);
-  
+
   if (updown == PU_UP) {
     puDeactivateWidget();
   } else if (updown == PU_DOWN) {
     puSetActiveWidget(this, x, y);
-    
+
     if (fgGetKeyModifiers() & KEYMOD_CTRL) {
       _clickGeod = unproject(_hitLocation - SGVec2d(_width>>1, _height>>1));
     }
@@ -457,7 +466,7 @@ int MapWidget::checkKey (int key, int updown )
   if ((updown == PU_UP) || !isVisible () || !isActive () || (window != puGetWindow())) {
     return FALSE ;
   }
-  
+
   switch (key)
   {
 
@@ -468,24 +477,24 @@ int MapWidget::checkKey (int key, int updown )
   case PU_KEY_DOWN:
     pan(SGVec2d(0, CURSOR_PAN_STEP));
     break ;
-  
+
   case PU_KEY_LEFT:
     pan(SGVec2d(CURSOR_PAN_STEP, 0));
     break;
-    
+
   case PU_KEY_RIGHT:
     pan(SGVec2d(-CURSOR_PAN_STEP, 0));
     break;
-  
+
   case '-':
     zoomOut();
-    
+
     break;
-    
+
   case '=':
     zoomIn();
     break;
-  
+
   default :
     return FALSE;
   }
@@ -495,91 +504,106 @@ int MapWidget::checkKey (int key, int updown )
 
 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"), 
+  _aircraft = SGGeod::fromDeg(fgGetDouble("/position/longitude-deg"),
     fgGetDouble("/position/latitude-deg"));
-  _magneticHeadings = _root->getBoolValue("magnetic-headings");
+    
+  bool mag = _root->getBoolValue("magnetic-headings");
+  if (mag != _magneticHeadings) {
+    clearData(); // flush cached data text, since it often includes heading
+    _magneticHeadings =  mag;
+  }
   
-  if (_root->getBoolValue("centre-on-aircraft")) {
+  if (_hasPanned) {
+      _root->setBoolValue("centre-on-aircraft", false);
+      _hasPanned = false;
+  }
+  else if (_root->getBoolValue("centre-on-aircraft")) {
     _projectionCenter = _aircraft;
-    _root->setBoolValue("centre-on-aircraft", false);
   }
-  
+
   double julianDate = globals->get_time_params()->getJD();
   _magVar->update(_projectionCenter, julianDate);
 
-  SGGeod topLeft = unproject(SGVec2d(_width/2, _height/2));
-  // compute draw range, including a fudge factor for ILSs and other 'long'
-  // symbols
-  _drawRangeNm = SGGeodesy::distanceNm(_projectionCenter, topLeft) + 10.0;
-
   bool aircraftUp = _root->getBoolValue("aircraft-heading-up");
   if (aircraftUp) {
     _upHeading = fgGetDouble("/orientation/heading-deg");
   } else {
     _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
+  _drawRangeNm = SGGeodesy::distanceNm(_projectionCenter, topLeft) + 10.0;
+
 // drawing operations
   GLint sx = (int) abox.min[0],
     sy = (int) abox.min[1];
   glScissor(dx + sx, dy + sy, _width, _height);
   glEnable(GL_SCISSOR_TEST);
-  
+
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
   // cetere drawing about the widget center (which is also the
   // projection centre)
   glTranslated(dx + sx + (_width/2), dy + sy + (_height/2), 0.0);
-  
+
   drawLatLonGrid();
-  
+
   if (aircraftUp) {
     int textHeight = legendFont.getStringHeight() + 5;
-    
+
     // draw heading line
     SGVec2d loc = project(_aircraft);
     glColor3f(1.0, 1.0, 1.0);
     drawLine(loc, SGVec2d(loc.x(), (_height / 2) - textHeight));
-    
+
     int displayHdg;
     if (_magneticHeadings) {
       displayHdg = (int) fgGetDouble("/orientation/heading-magnetic-deg");
     } else {
       displayHdg = (int) _upHeading;
     }
-    
+
     double y = (_height / 2) - textHeight;
     char buf[16];
     ::snprintf(buf, 16, "%d", displayHdg);
     int sw = legendFont.getStringWidth(buf);
     legendFont.drawString(buf, loc.x() - sw/2, y);
   }
-  
+
   drawAirports();
   drawNavaids();
   drawTraffic();
@@ -589,9 +613,9 @@ void MapWidget::draw(int dx, int dy)
   paintAircraftLocation(_aircraft);
   paintRoute();
   paintRuler();
-  
+
   drawData();
-  
+
   glPopMatrix();
   glDisable(GL_SCISSOR_TEST);
 }
@@ -601,138 +625,131 @@ void MapWidget::paintRuler()
   if (_clickGeod == SGGeod()) {
     return;
   }
-  
+
   SGVec2d acftPos = project(_aircraft);
   SGVec2d clickPos = project(_clickGeod);
-  
+
   glColor4f(0.0, 1.0, 1.0, 0.6);
   drawLine(acftPos, clickPos);
-  
+
   circleAtAlt(clickPos, 8, 10, 5);
-  
+
   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);
   d->setAnchor(clickPos);
   d->setOffset(MapData::VALIGN_TOP | MapData::HALIGN_CENTER, 15);
   d->setPriority(20000);
 
-  
+
 }
 
 void MapWidget::paintAircraftLocation(const SGGeod& aircraftPos)
 {
   SGVec2d loc = project(aircraftPos);
-  
+
   double hdg = fgGetDouble("/orientation/heading-deg");
-  
+
   glLineWidth(2.0);
   glColor4f(1.0, 1.0, 0.0, 1.0);
   glPushMatrix();
   glTranslated(loc.x(), loc.y(), 0.0);
   glRotatef(hdg - _upHeading, 0.0, 0.0, -1.0);
-  
+
   const SGVec2d wingspan(12, 0);
   const SGVec2d nose(0, 8);
   const SGVec2d tail(0, -14);
   const SGVec2d tailspan(4,0);
-  
+
   drawLine(-wingspan, wingspan);
   drawLine(nose, tail);
   drawLine(tail - tailspan, tail + tailspan);
-  
+
   glPopMatrix();
   glLineWidth(1.0);
 }
 
 void MapWidget::paintRoute()
 {
-  if (_route->size() < 2) {
+  if (_route->numWaypts() < 2) {
     return;
   }
-  
-// first pass, draw the actual line
+
+  RoutePath path(_route->flightPlan());
+
+// first pass, draw the actual lines
   glLineWidth(2.0);
-  glBegin(GL_LINE_STRIP);
-  
-  SGVec2d prev = project(_route->get_waypoint(0).get_target());
-  glVertex2d(prev.x(), prev.y());
-  
-  for (int w=1; w < _route->size(); ++w) {
-    
-    SGVec2d p = project(_route->get_waypoint(w).get_target());
-    
-    if (w < _route->currentWaypoint()) {
+
+  for (int w=0; w<_route->numWaypts(); ++w) {
+    SGGeodVec gv(path.pathForIndex(w));
+    if (gv.empty()) {
+      continue;
+    }
+
+    if (w < _route->currentIndex()) {
       glColor4f(0.5, 0.5, 0.5, 0.7);
     } else {
       glColor4f(1.0, 0.0, 1.0, 1.0);
     }
-    
-    glVertex2d(p.x(), p.y());
-    
+
+    flightgear::WayptRef wpt(_route->wayptAtIndex(w));
+    if (wpt->flag(flightgear::WPT_MISS)) {
+      glEnable(GL_LINE_STIPPLE);
+      glLineStipple(1, 0x00FF);
+    }
+
+    glBegin(GL_LINE_STRIP);
+    for (unsigned int i=0; i<gv.size(); ++i) {
+      SGVec2d p = project(gv[i]);
+      glVertex2d(p.x(), p.y());
+    }
+
+    glEnd();
+    glDisable(GL_LINE_STIPPLE);
   }
-  glEnd();
-  
+
   glLineWidth(1.0);
 // second pass, draw waypoint symbols and data
-  for (int w=0; w < _route->size(); ++w) {
-    const SGWayPoint& wpt(_route->get_waypoint(w));
-    SGVec2d p = project(wpt.get_target());
+  for (int w=0; w < _route->numWaypts(); ++w) {
+    flightgear::WayptRef wpt(_route->wayptAtIndex(w));
+    SGGeod g = path.positionForIndex(w);
+    if (g == SGGeod()) {
+      continue; // Vectors or similar
+    }
+
+    SGVec2d p = project(g);
     glColor4f(1.0, 0.0, 1.0, 1.0);
     circleAtAlt(p, 8, 12, 5);
-    
+
     std::ostringstream legend;
-    legend << wpt.get_id();
-    if (wpt.get_target_alt() > -9990.0) {
-      legend << '\n' << SGMiscd::roundToInt(wpt.get_target_alt()) << '\'';
+    legend << wpt->ident();
+    if (wpt->altitudeRestriction() != flightgear::RESTRICT_NONE) {
+      legend << '\n' << SGMiscd::roundToInt(wpt->altitudeFt()) << '\'';
     }
-    
-    if (wpt.get_speed() > 0.0) {
-      legend << '\n' << SGMiscd::roundToInt(wpt.get_speed()) << "Kts";
+
+    if (wpt->speedRestriction() == flightgear::SPEED_RESTRICT_MACH) {
+      legend << '\n' << wpt->speedMach() << "M";
+    } else if (wpt->speedRestriction() != flightgear::RESTRICT_NONE) {
+      legend << '\n' << SGMiscd::roundToInt(wpt->speedKts()) << "Kts";
     }
-       
+
     MapData* d = getOrCreateDataForKey(reinterpret_cast<void*>(w * 2));
     d->setText(legend.str());
-    d->setLabel(wpt.get_id());
+    d->setLabel(wpt->ident());
     d->setAnchor(p);
     d->setOffset(MapData::VALIGN_TOP | MapData::HALIGN_CENTER, 15);
-    d->setPriority(w < _route->currentWaypoint() ? 9000 : 12000);
-        
-    if (w > 0) {
-      SGVec2d legMid = (prev + p) * 0.5;
-      std::ostringstream legLegend;
-      
-      double track = wpt.get_track();
-      if (_magneticHeadings) {
-        track -= _magVar->get_magvar(); // show magnetic track for leg
-      }
-      
-      legLegend << SGMiscd::roundToInt(track) << " " 
-        << SGMiscd::roundToInt(wpt.get_distance() * SG_METER_TO_NM) << "Nm";
-        
-      MapData* ld = getOrCreateDataForKey(reinterpret_cast<void*>(w * 2 + 1));
-      ld->setText(legLegend.str());
-      ld->setAnchor(legMid);
-      ld->setOffset(MapData::VALIGN_TOP | MapData::HALIGN_CENTER, 15);
-      ld->setPriority(w < _route->currentWaypoint() ? 8000 : 11000);
-    } // of draw leg data
-    
-    prev = p;
+    d->setPriority(w < _route->currentIndex() ? 9000 : 12000);
+
   } // of second waypoint iteration
 }
 
 /**
- * Round a SGGeod to an arbitrary precision. 
+ * Round a SGGeod to an arbitrary precision.
  * For example, passing precision of 0.5 will round to the nearest 0.5 of
  * a degree in both lat and lon - passing in 3.0 rounds to the nearest 3 degree
  * multiple, and so on.
@@ -741,7 +758,7 @@ static SGGeod roundGeod(double precision, const SGGeod& g)
 {
   double lon = SGMiscd::round(g.getLongitudeDeg() / precision);
   double lat = SGMiscd::round(g.getLatitudeDeg() / precision);
-  
+
   return SGGeod::fromDeg(lon * precision, lat * precision);
 }
 
@@ -751,13 +768,13 @@ bool MapWidget::drawLineClipped(const SGVec2d& a, const SGVec2d& b)
     minY = SGMiscd::min(a.y(), b.y()),
     maxX = SGMiscd::max(a.x(), b.x()),
     maxY = SGMiscd::max(a.y(), b.y());
-  
+
   int hh = _height >> 1, hw = _width >> 1;
-  
+
   if ((maxX < -hw) || (minX > hw) || (minY > hh) || (maxY < -hh)) {
     return false;
   }
-  
+
   glVertex2dv(a.data());
   glVertex2dv(b.data());
   return true;
@@ -770,11 +787,11 @@ SGVec2d MapWidget::gridPoint(int ix, int iy)
        if (it != _gridCache.end()) {
                return it->second;
        }
-       
+
        SGGeod gp = SGGeod::fromDeg(
     _gridCenter.getLongitudeDeg() + ix * _gridSpacing,
                _gridCenter.getLatitudeDeg() + iy * _gridSpacing);
-               
+
        SGVec2d proj = project(gp);
        _gridCache[key] = proj;
        return proj;
@@ -785,7 +802,7 @@ void MapWidget::drawLatLonGrid()
   _gridSpacing = 1.0;
   _gridCenter = roundGeod(_gridSpacing, _projectionCenter);
   _gridCache.clear();
-  
+
   int ix = 0;
   int iy = 0;
 
@@ -796,7 +813,7 @@ void MapWidget::drawLatLonGrid()
     didDraw = false;
     ++ix;
     ++iy;
-    
+
     for (int x = -ix; x < ix; ++x) {
       didDraw |= drawLineClipped(gridPoint(x, -iy), gridPoint(x+1, -iy));
       didDraw |= drawLineClipped(gridPoint(x, iy), gridPoint(x+1, iy));
@@ -804,62 +821,62 @@ void MapWidget::drawLatLonGrid()
       didDraw |= drawLineClipped(gridPoint(x, iy), gridPoint(x, iy - 1));
 
     }
-    
+
     for (int y = -iy; y < iy; ++y) {
       didDraw |= drawLineClipped(gridPoint(-ix, y), gridPoint(-ix, y+1));
       didDraw |= drawLineClipped(gridPoint(-ix, y), gridPoint(-ix + 1, y));
       didDraw |= drawLineClipped(gridPoint(ix, y), gridPoint(ix, y+1));
       didDraw |= drawLineClipped(gridPoint(ix, y), gridPoint(ix - 1, y));
     }
-    
+
     if (ix > 30) {
       break;
     }
   } while (didDraw);
-  
+
   glEnd();
 }
 
 void MapWidget::drawGPSData()
 {
   std::string gpsMode = _gps->getStringValue("mode");
-  
+
   SGGeod wp0Geod = SGGeod::fromDeg(
-        _gps->getDoubleValue("wp/wp[0]/longitude-deg"), 
+        _gps->getDoubleValue("wp/wp[0]/longitude-deg"),
         _gps->getDoubleValue("wp/wp[0]/latitude-deg"));
-        
+
   SGGeod wp1Geod = SGGeod::fromDeg(
-        _gps->getDoubleValue("wp/wp[1]/longitude-deg"), 
+        _gps->getDoubleValue("wp/wp[1]/longitude-deg"),
         _gps->getDoubleValue("wp/wp[1]/latitude-deg"));
-  
+
 // draw track line
   double gpsTrackDeg = _gps->getDoubleValue("indicated-track-true-deg");
   double gpsSpeed = _gps->getDoubleValue("indicated-ground-speed-kt");
   double az2;
-  
+
   if (gpsSpeed > 3.0) { // only draw track line if valid
     SGGeod trackRadial;
     SGGeodesy::direct(_aircraft, gpsTrackDeg, _drawRangeNm * SG_NM_TO_METER, trackRadial, az2);
-    
+
     glColor4f(1.0, 1.0, 0.0, 1.0);
     glEnable(GL_LINE_STIPPLE);
     glLineStipple(1, 0x00FF);
     drawLine(project(_aircraft), project(trackRadial));
     glDisable(GL_LINE_STIPPLE);
   }
-  
+
   if (gpsMode == "dto") {
     SGVec2d wp0Pos = project(wp0Geod);
     SGVec2d wp1Pos = project(wp1Geod);
-    
+
     glColor4f(1.0, 0.0, 1.0, 1.0);
     drawLine(wp0Pos, wp1Pos);
-    
+
   }
-    
+
   if (_gps->getBoolValue("scratch/valid")) {
     // draw scratch data
-    
+
   }
 }
 
@@ -870,18 +887,18 @@ public:
   {
     _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 {
     return _heliports ? FGPositioned::HELIPORT : FGPositioned::AIRPORT;
   }
-       
+
   virtual bool passAirport(FGAirport* aApt) const {
     if (_hardRunwaysOnly) {
       return aApt->hasHardRunwayOfLengthFt(_minLengthFt);
     }
-    
+
     return true;
   }
 
@@ -907,26 +924,26 @@ public:
     _fixes(fixesEnabled),
     _navaids(navaidsEnabled)
   {}
-  
-  virtual bool pass(FGPositioned* aPos) const { 
+
+  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;
       }
     }
-    
+
     return true;
   }
-   
+
   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:
   bool _fixes, _navaids;
 };
@@ -935,10 +952,10 @@ void MapWidget::drawNavaids()
 {
   bool fixes = _root->getBoolValue("draw-fixes");
   NavaidFilter f(fixes, _root->getBoolValue("draw-navaids"));
-    
+
   if (f.minType() <= f.maxType()) {
     FGPositioned::List navs = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &f);
-    
+
     glLineWidth(1.0);
     for (unsigned int i=0; i<navs.size(); ++i) {
       FGPositioned::Type ty = navs[i]->type();
@@ -956,28 +973,28 @@ void MapWidget::drawNavaids()
 void MapWidget::drawNDB(bool tuned, FGNavRecord* ndb)
 {
   SGVec2d pos = project(ndb->geod());
-  
+
   if (tuned) {
     glColor3f(0.0, 1.0, 1.0);
   } else {
     glColor3f(0.0, 0.0, 0.0);
   }
-  
+
   glEnable(GL_LINE_STIPPLE);
   glLineStipple(1, 0x00FF);
   circleAt(pos, 20, 6);
   circleAt(pos, 20, 10);
   glDisable(GL_LINE_STIPPLE);
-  
+
   if (validDataForKey(ndb)) {
     setAnchorForKey(ndb, pos);
     return;
   }
-  
+
   char buffer[1024];
        ::snprintf(buffer, 1024, "%s\n%s %3.0fKhz",
                ndb->name().c_str(), ndb->ident().c_str(),ndb->get_freq()/100.0);
-       
+
   MapData* d = createDataForKey(ndb);
   d->setPriority(40);
   d->setLabel(ndb->ident());
@@ -995,19 +1012,19 @@ void MapWidget::drawVOR(bool tuned, FGNavRecord* vor)
   } else {
     glColor3f(0.0, 0.0, 1.0);
   }
-  
+
   circleAt(pos, 6, 8);
-  
+
   if (validDataForKey(vor)) {
     setAnchorForKey(vor, pos);
     return;
   }
-  
+
   char buffer[1024];
        ::snprintf(buffer, 1024, "%s\n%s %6.3fMhz",
                vor->name().c_str(), vor->ident().c_str(),
     vor->get_freq() / 100.0);
-       
+
   MapData* d = createDataForKey(vor);
   d->setText(buffer);
   d->setLabel(vor->ident());
@@ -1021,8 +1038,8 @@ void MapWidget::drawFix(FGFix* fix)
   SGVec2d pos = project(fix->geod());
   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
   }
 
@@ -1030,7 +1047,7 @@ void MapWidget::drawFix(FGFix* fix)
     setAnchorForKey(fix, pos);
     return;
   }
-  
+
   MapData* d = createDataForKey(fix);
   d->setLabel(fix->ident());
   d->setPriority(20);
@@ -1040,66 +1057,68 @@ void MapWidget::drawFix(FGFix* fix)
 
 void MapWidget::drawNavRadio(SGPropertyNode_ptr radio)
 {
-  if (!radio || radio->getBoolValue("slaved-to-gps", false) 
+  if (!radio || radio->getBoolValue("slaved-to-gps", false)
         || !radio->getBoolValue("in-range", false)) {
     return;
   }
-  
+
   if (radio->getBoolValue("nav-loc", false)) {
     drawTunedLocalizer(radio);
   }
-  
+
   // 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;
   }
-  
+
   glLineWidth(1.0);
   drawVOR(true, nav);
-  
+
   SGVec2d pos = project(nav->geod());
   SGGeod range;
   double az2;
   double trueRadial = radio->getDoubleValue("radials/target-radial-deg");
   SGGeodesy::direct(nav->geod(), trueRadial, nav->get_range() * SG_NM_TO_METER, range, az2);
   SGVec2d prange = project(range);
-  
+
   SGVec2d norm = normalize(prange - pos);
   SGVec2d perp(norm.y(), -norm.x());
-  
+
   circleAt(pos, 64, length(prange - pos));
   drawLine(pos, prange);
-  
+
 // draw to/from arrows
   SGVec2d midPoint = (pos + prange) * 0.5;
   if (radio->getBoolValue("from-flag")) {
     norm = -norm;
     perp = -perp;
   }
-  
+
   int sz = 10;
   SGVec2d arrowB = midPoint - (norm * sz) + (perp * sz);
   SGVec2d arrowC = midPoint - (norm * sz) - (perp * sz);
   drawLine(midPoint, arrowB);
   drawLine(arrowB, arrowC);
   drawLine(arrowC, midPoint);
-  
+
   drawLine(pos, (2 * pos) - prange); // reciprocal radial
 }
 
 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;
   }
-  
+
   if (loc->runway()) {
     drawILS(true, loc->runway());
   }
@@ -1119,18 +1138,18 @@ void MapWidget::drawAirport(FGAirport* apt)
 {
        // 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);
-    
+
     drawLine(towerPos + SGVec2d(3, 0), towerPos + SGVec2d(3, 10));
     drawLine(towerPos + SGVec2d(-3, 0), towerPos + SGVec2d(-3, 10));
     drawLine(towerPos + SGVec2d(-6, 20), towerPos + SGVec2d(-3, 10));
     drawLine(towerPos + SGVec2d(6, 20), towerPos + SGVec2d(3, 10));
     drawLine(towerPos + SGVec2d(-6, 20), towerPos + SGVec2d(6, 20));
   }
-  
+
   if (validDataForKey(apt)) {
     setAnchorForKey(apt, towerPos);
   } else {
@@ -1145,8 +1164,8 @@ void MapWidget::drawAirport(FGAirport* apt)
     d->setOffset(MapData::VALIGN_TOP | MapData::HALIGN_CENTER, 6);
     d->setAnchor(towerPos);
   }
-  
-  if (_zoom > SHOW_DETAIL_ZOOM) {
+
+  if (_cachedZoom > SHOW_DETAIL_ZOOM) {
     return;
   }
 
@@ -1156,25 +1175,25 @@ void MapWidget::drawAirport(FGAirport* apt)
                        drawRunwayPre(rwy);
                }
   }
-  
+
        for (unsigned int r=0; r<apt->numRunways(); ++r) {
                FGRunway* rwy = apt->getRunwayByIndex(r);
                if (!rwy->isReciprocal()) {
                        drawRunway(rwy);
                }
-               
+
                if (rwy->ILS()) {
                        drawILS(false, rwy);
                }
        } // of runway iteration
-       
+
 }
 
 int MapWidget::scoreAirportRunways(FGAirport* apt)
 {
   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) {
@@ -1186,11 +1205,11 @@ int MapWidget::scoreAirportRunways(FGAirport* apt)
     if (needHardSurface && !rwy->isHardSurface()) {
       continue;
     }
-    
+
     if (rwy->lengthFt() < minLength) {
       continue;
     }
-    
+
     int scoreLength = SGMiscd::roundToInt(rwy->lengthFt() / 200.0);
     score += scoreLength;
   } // of runways iteration
@@ -1202,7 +1221,7 @@ void MapWidget::drawRunwayPre(FGRunway* rwy)
 {
   SGVec2d p1 = project(rwy->begin());
        SGVec2d p2 = project(rwy->end());
-       
+
   glLineWidth(4.0);
   glColor3f(1.0, 0.0, 1.0);
        drawLine(p1, p2);
@@ -1210,32 +1229,32 @@ void MapWidget::drawRunwayPre(FGRunway* rwy)
 
 void MapWidget::drawRunway(FGRunway* rwy)
 {
-       // line for runway 
+       // line for runway
        // optionally show active, stopway, etc
        // in legend, show published heading and length
        // and threshold elevation
-       
+
   SGVec2d p1 = project(rwy->begin());
        SGVec2d p2 = project(rwy->end());
   glLineWidth(2.0);
   glColor3f(1.0, 1.0, 1.0);
   SGVec2d inset = normalize(p2 - p1) * 2;
-  
+
        drawLine(p1 + inset, p2 - inset);
-       
+
   if (validDataForKey(rwy)) {
     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);
   d->setText(buffer);
   d->setLabel(rwy->ident() + "/" + rwy->reciprocalRunway()->ident());
@@ -1250,7 +1269,7 @@ void MapWidget::drawILS(bool tuned, FGRunway* rwy)
   // using LOC transmitter position would be more accurate, but
   // is visually cluttered
        // arrow width is based upon the computed localizer width
-       
+
        FGNavRecord* loc = rwy->ILS();
        double halfBeamWidth = loc->localizerWidth() * 0.5;
        SGVec2d t = project(rwy->threshold());
@@ -1259,17 +1278,17 @@ void MapWidget::drawILS(bool tuned, FGRunway* rwy)
        double radial = loc->get_multiuse();
   SG_NORMALIZE_RANGE(radial, 0.0, 360.0);
        double az2;
-       
+
 // compute the three end points at the widge end of the arrow
        SGGeodesy::direct(loc->geod(), radial, -rangeM, locEnd, az2);
        SGVec2d endCentre = project(locEnd);
-       
+
        SGGeodesy::direct(loc->geod(), radial + halfBeamWidth, -rangeM * 1.1, locEnd, az2);
        SGVec2d endR = project(locEnd);
-       
+
        SGGeodesy::direct(loc->geod(), radial - halfBeamWidth, -rangeM * 1.1, locEnd, az2);
        SGVec2d endL = project(locEnd);
-       
+
 // outline two triangles
   glLineWidth(1.0);
   if (tuned) {
@@ -1277,7 +1296,7 @@ void MapWidget::drawILS(bool tuned, FGRunway* rwy)
   } else {
     glColor3f(0.0, 0.0, 1.0);
        }
-  
+
   glBegin(GL_LINE_LOOP);
                glVertex2dv(t.data());
                glVertex2dv(endCentre.data());
@@ -1288,6 +1307,24 @@ void MapWidget::drawILS(bool tuned, FGRunway* rwy)
                glVertex2dv(endCentre.data());
                glVertex2dv(endR.data());
        glEnd();
+
+       if (validDataForKey(loc)) {
+    setAnchorForKey(loc, endR);
+    return;
+  }
+
+       char buffer[1024];
+       ::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);
+  d->setLabel(loc->ident());
+  d->setText(buffer);
+  d->setOffset(MapData::HALIGN_CENTER | MapData::VALIGN_BOTTOM, 10);
+  d->setAnchor(endR);
 }
 
 void MapWidget::drawTraffic()
@@ -1295,17 +1332,17 @@ void MapWidget::drawTraffic()
   if (!_root->getBoolValue("draw-traffic")) {
     return;
   }
-  
-  if (_zoom > SHOW_DETAIL_ZOOM) {
+
+  if (_cachedZoom > SHOW_DETAIL_ZOOM) {
     return;
   }
-  
+
   const SGPropertyNode* ai = fgGetNode("/ai/models", true);
 
   for (int i = 0; i < ai->nChildren(); ++i) {
     const SGPropertyNode *model = ai->getChild(i);
     // skip bad or dead entries
-    if (!model || model->getIntValue("id", -1) < 0) {
+    if (!model || model->getIntValue("id", -1) == -1) {
       continue;
     }
 
@@ -1314,14 +1351,14 @@ void MapWidget::drawTraffic()
       model->getDoubleValue("position/longitude-deg"),
       model->getDoubleValue("position/latitude-deg"),
       model->getDoubleValue("position/altitude-ft"));
-      
+
     double dist = SGGeodesy::distanceNm(_projectionCenter, pos);
     if (dist > _drawRangeNm) {
       continue;
     }
-    
+
     double heading = model->getDoubleValue("orientation/true-heading-deg");
-    if ((name == "aircraft") || (name == "multiplayer") || 
+    if ((name == "aircraft") || (name == "multiplayer") ||
         (name == "wingman") || (name == "tanker")) {
       drawAIAircraft(model, pos, heading);
     } else if ((name == "ship") || (name == "carrier") || (name == "escort")) {
@@ -1338,7 +1375,7 @@ void MapWidget::drawAIAircraft(const SGPropertyNode* model, const SGGeod& pos, d
   glColor3f(0.0, 0.0, 0.0);
   glLineWidth(2.0);
   circleAt(p, 4, 6.0); // black diamond
-  
+
 // draw heading vector
   int speedKts = static_cast<int>(model->getDoubleValue("velocities/true-airspeed-kt"));
   if (speedKts > 1) {
@@ -1346,29 +1383,23 @@ void MapWidget::drawAIAircraft(const SGPropertyNode* model, const SGGeod& pos, d
 
     const double dt = 15.0 / (3600.0); // 15 seconds look-ahead
     double distanceM = speedKts * SG_NM_TO_METER * dt;
-    
+
     SGGeod advance;
     double az2;
     SGGeodesy::direct(pos, hdg, distanceM, advance, az2);
-    
+
     drawLine(p, project(advance));
   }
-    
-  if (validDataForKey((void*) model)) {
-    setAnchorForKey((void*) model, p);
-    return;
-  }
-  
-  // draw callsign / altitude / speed
 
-  
+
+  // 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);
+
+  MapData* d = getOrCreateDataForKey((void*) model);
   d->setText(buffer);
   d->setLabel(model->getStringValue("callsign", "<>"));
   d->setPriority(speedKts > 5 ? 60 : 10); // low priority for parked aircraft
@@ -1379,20 +1410,69 @@ void MapWidget::drawAIAircraft(const SGPropertyNode* model, const SGGeod& pos, d
 
 void MapWidget::drawAIShip(const SGPropertyNode* model, const SGGeod& pos, double hdg)
 {
+  SGVec2d p = project(pos);
+
+  glColor3f(0.0, 0.0, 0.5);
+  glLineWidth(2.0);
+  circleAt(p, 4, 6.0); // blue diamond (to differentiate from aircraft.
+
+// draw heading vector
+  int speedKts = static_cast<int>(model->getDoubleValue("velocities/speed-kts"));
+  if (speedKts > 1) {
+    glLineWidth(1.0);
+
+    const double dt = 15.0 / (3600.0); // 15 seconds look-ahead
+    double distanceM = speedKts * SG_NM_TO_METER * dt;
+
+    SGGeod advance;
+    double az2;
+    SGGeodesy::direct(pos, hdg, distanceM, advance, az2);
+
+    drawLine(p, project(advance));
+  }
 
+  // draw callsign / speed
+  char buffer[1024];
+       ::snprintf(buffer, 1024, "%s\n%dkts",
+               model->getStringValue("name", "<>"),
+    speedKts);
+
+  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
+  d->setOffset(MapData::VALIGN_CENTER | MapData::HALIGN_LEFT, 10);
+  d->setAnchor(p);
 }
 
 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();
+  if (_orthoAzimuthProject) {
+    // 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();
+    
+  } else {
+    // 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();
+      
+  }
   
 // rotate as necessary
-  double cost = cos(_upHeading * SG_DEGREES_TO_RADIANS), 
+  double cost = cos(_upHeading * SG_DEGREES_TO_RADIANS),
     sint = sin(_upHeading * SG_DEGREES_TO_RADIANS);
   double rx = cost * p.x() - sint * p.y();
   double ry = sint * p.x() + cost * p.y();
@@ -1402,23 +1482,34 @@ SGVec2d MapWidget::project(const SGGeod& geod) const
 SGGeod MapWidget::unproject(const SGVec2d& p) const
 {
   // unrotate, if necessary
-  double cost = cos(-_upHeading * SG_DEGREES_TO_RADIANS), 
+  double cost = cos(-_upHeading * SG_DEGREES_TO_RADIANS),
     sint = sin(-_upHeading * SG_DEGREES_TO_RADIANS);
-  SGVec2d ur(cost * p.x() - sint * p.y(), 
+  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);
+
+  if (_orthoAzimuthProject) {
+      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);
+  } else {
+      double lat = unscaled.y() + _projectionCenter.getLatitudeRad();
+      double lon = (unscaled.x() / cos(lat)) + _projectionCenter.getLongitudeRad();
+      return SGGeod::fromRad(lon, lat);
+  }
 }
 
 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)
@@ -1461,25 +1552,25 @@ void MapWidget::drawLegendBox(const SGVec2d& pos, const std::string& t)
        std::vector<std::string> lines(simgear::strutils::split(t, "\n"));
        const int LINE_LEADING = 4;
        const int MARGIN = 4;
-       
+
 // measure
        int maxWidth = -1, totalHeight = 0;
        int lineHeight = legendFont.getStringHeight();
-       
+
        for (unsigned int ln=0; ln<lines.size(); ++ln) {
                totalHeight += lineHeight;
                if (ln > 0) {
                        totalHeight += LINE_LEADING;
                }
-               
+
                int lw = legendFont.getStringWidth(lines[ln].c_str());
                maxWidth = std::max(maxWidth, lw);
        } // of line measurement
-       
+
        if (maxWidth < 0) {
                return; // all lines are empty, don't draw
        }
-       
+
        totalHeight += MARGIN * 2;
 
 // draw box
@@ -1490,12 +1581,12 @@ void MapWidget::drawLegendBox(const SGVec2d& pos, const std::string& t)
        box.max[1] = 0;
        int border = 1;
        box.draw (pos.x(), pos.y(), PUSTYLE_DROPSHADOW, colour, FALSE, border);
-       
+
 // draw lines
        int xPos = pos.x() + MARGIN;
        int yPos = pos.y() - (lineHeight + MARGIN);
        glColor3f(0.8, 0.8, 0.8);
-  
+
        for (unsigned int ln=0; ln<lines.size(); ++ln) {
                legendFont.drawString(lines[ln].c_str(), xPos, yPos);
                yPos -= lineHeight + LINE_LEADING;
@@ -1505,27 +1596,27 @@ void MapWidget::drawLegendBox(const SGVec2d& pos, const std::string& t)
 void MapWidget::drawData()
 {
   std::sort(_dataQueue.begin(), _dataQueue.end(), MapData::order);
-  
-  int hw = _width >> 1, 
+
+  int hw = _width >> 1,
     hh = _height >> 1;
   puBox visBox(makePuBox(-hw, -hh, _width, _height));
-  
+
   unsigned int d = 0;
   int drawn = 0;
   std::vector<MapData*> drawQueue;
-  
+
   bool drawData = _root->getBoolValue("draw-data");
   const int MAX_DRAW_DATA = 25;
   const int MAX_DRAW = 50;
-  
+
   for (; (d < _dataQueue.size()) && (drawn < MAX_DRAW); ++d) {
     MapData* md = _dataQueue[d];
     md->setDataVisible(drawData);
-    
+
     if (md->isClipped(visBox)) {
       continue;
     }
-    
+
     if (md->overlaps(drawQueue)) {
       if (drawData) { // overlapped with data, let's try just the label
         md->setDataVisible(false);
@@ -1536,20 +1627,20 @@ void MapWidget::drawData()
         continue;
       }
     } // of overlaps case
-    
+
     drawQueue.push_back(md);
     ++drawn;
     if (drawData && (drawn >= MAX_DRAW_DATA)) {
       drawData = false;
     }
   }
-    
+
   // draw lowest-priority first, so higher-priorty items appear on top
   std::vector<MapData*>::reverse_iterator r;
   for (r = drawQueue.rbegin(); r!= drawQueue.rend(); ++r) {
     (*r)->draw();
   }
-  
+
   _dataQueue.clear();
   KeyDataMap::iterator it = _mapData.begin();
   for (; it != _mapData.end(); ) {
@@ -1570,7 +1661,7 @@ bool MapWidget::validDataForKey(void* key)
   if (it == _mapData.end()) {
     return false; // no valid data for the key!
   }
-  
+
   it->second->resetAge(); // mark data as valid this frame
   _dataQueue.push_back(it->second);
   return true;
@@ -1582,7 +1673,7 @@ void MapWidget::setAnchorForKey(void* key, const SGVec2d& anchor)
   if (it == _mapData.end()) {
     throw sg_exception("no valid data for key!");
   }
-  
+
   it->second->setAnchor(anchor);
 }
 
@@ -1592,7 +1683,7 @@ MapData* MapWidget::getOrCreateDataForKey(void* key)
   if (it == _mapData.end()) {
     return createDataForKey(key);
   }
-  
+
   it->second->resetAge(); // mark data as valid this frame
   _dataQueue.push_back(it->second);
   return it->second;
@@ -1604,10 +1695,30 @@ MapData* MapWidget::createDataForKey(void* key)
   if (it != _mapData.end()) {
     throw sg_exception("duplicate data requested for key!");
   }
-  
+
   MapData* d =  new MapData(0);
   _mapData[key] = d;
   _dataQueue.push_back(d);
   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);
+}