void EastArrowObjectImp::orient()
{
   if (isOriented() == true)
   {
      return;
   }

   GraphicLayer* pLayer = NULL;
   pLayer = getLayer();
   if (pLayer == NULL)
   {
      return;
   }

   View* pView = NULL;
   pView = pLayer->getView();
   if (pView == NULL)
   {
      return;
   }

   SpatialDataView* pSpatialDataView = NULL;
   if (pView->isKindOf("SpatialDataView") == true)
   {
      pSpatialDataView = static_cast<SpatialDataView*> (pView);
   }
   else if (pView->isKindOf("ProductView") == true)
   {
      ProductView* pProductView = static_cast<ProductView*> (pView);

      GraphicLayer* pLayoutLayer = NULL;
      pLayoutLayer = pProductView->getLayoutLayer();
      if (pLayoutLayer == pLayer)
      {
         list<GraphicObject*> viewObjects;
         pLayoutLayer->getObjects(VIEW_OBJECT, viewObjects);

         list<GraphicObject*>::iterator iter = viewObjects.begin();
         while (iter != viewObjects.end())
         {
            GraphicObject* pObject = *iter;
            if (pObject != NULL)
            {
               View* pObjectView = pObject->getObjectView();
               if (pObjectView != NULL)
               {
                  if (pObjectView->isKindOf("SpatialDataView") == true)
                  {
                     pSpatialDataView = static_cast<SpatialDataView*> (pObjectView);
                  }
               }
            }

            ++iter;
         }
      }
   }

   if (pSpatialDataView == NULL)
   {
      return;
   }

   LayerList* pLayerList = pSpatialDataView->getLayerList();
   VERIFYNRV(pLayerList != NULL);
   RasterElement* pRaster = pLayerList->getPrimaryRasterElement();
   VERIFYNRV(pRaster != NULL);
   if (!pRaster->isGeoreferenced())
   {
      return;
   }

   // Calculate the angle of the object relative to the pixel coordinates
   updateHandles();

   LocationType pixelStart = mHandles[7];

   ProductView* pProductView = dynamic_cast<ProductView*> (pView);
   if (pProductView != NULL)
   {
      // Convert to the screen coordinate system
      double dScreenX = 0;
      double dScreenY = 0;
      pLayer->translateDataToWorld(pixelStart.mX, pixelStart.mY, pixelStart.mX, pixelStart.mY);
      pProductView->translateWorldToScreen(pixelStart.mX, pixelStart.mY, dScreenX, dScreenY);
      
      // Convert to the spatial data view coordinate system
      pSpatialDataView->translateScreenToWorld(dScreenX,
         dScreenY, pixelStart.mX, pixelStart.mY);
      pLayer->translateWorldToData(pixelStart.mX, pixelStart.mY, pixelStart.mX, pixelStart.mY);
   }

   double dAngle;
   if (GeoAlgorithms::getAngleToNorth(pRaster, dAngle, pixelStart) == false)
   {
      return;
   }

   // Update the angle if the object is in the layout layer
   if (pProductView != NULL)
   {
      // Rotation
      dAngle -= pSpatialDataView->getRotation();

      // Pitch
      double dPitch = pSpatialDataView->getPitch();
      if (dPitch > 0.0)
      {
         dAngle *= -1.0;
      }
   }

   // Rotate the object
   setRotation(dAngle);

   // Update the orientation flag
   DirectionalArrowObjectImp::orient();
}
void MeasurementObjectImp::draw(double zoomFactor) const
{
   const_cast<MeasurementObjectImp*>(this)->updateGeoreferenceAttachment();

   // Verify that factor values are valid
   if ((mArrowRelStartLoc < 0.0) || (mArrowRelStartLoc > mArrowRelEndLoc) || (mArrowRelStartLoc > 1.0) ||
      (mArrowRelEndLoc < 0.0) || (mArrowRelEndLoc > 1.0) || (mBarEndLength < 0))
   {
      return;
   }
    
   // Draw the main line
   LineObjectImp::draw(zoomFactor);
   
   // Get the current view 
   SpatialDataView* pView = NULL;

   MeasurementLayerImp* pLayer = dynamic_cast<MeasurementLayerImp*>(getLayer());
   if (pLayer != NULL)
   {
      pView = dynamic_cast<SpatialDataView*>(pLayer->getView());
   }

   if (pView == NULL)
   {
      return;
   }

   // Misc Variables
   LocationType junkLocation;    // junk variable used to call methods that require unneeded parameters
   double startPerc = 0.0;       // The relative start location along a line to start drawing
   double lineLength = 0;        // The length of the main line
   double pixelSize = 0;         // The number of screen pixels in a scene pixel
   double lineWidth = 0;         // The width of the main line
   double sqrtLineWidth = 0;     // The square root of the line width
   LocationType llCorner;        // lower left corner of annotation bounding box
   LocationType urCorner;        // upper right corner of annotation bounding box
   ColorType textColor;          // The color to draw the text
   ColorType lineColor;          // The color to draw the line
   ColorType fillColor;          // The color to draw the stippled line

   // Misc Calculations
   pixelSize = DrawUtil::getPixelSize(junkLocation.mX, junkLocation.mY, junkLocation.mX, junkLocation.mY);
   llCorner = getLlCorner();
   urCorner = getUrCorner();
   lineWidth = getLineWidth();
   sqrtLineWidth = sqrt(lineWidth);
   textColor = getTextColor();
   lineLength = sqrt(pow(abs(urCorner.mX - llCorner.mX), 2) + pow(abs(urCorner.mY - llCorner.mY), 2));
   lineColor = getLineColor();
   fillColor = getFillColor();

   // Get text font info (used for all text, set to italic if using inaccurate extrapolation)
   QFont font = getFont();
   font.setItalic(mUsingInaccurateGeocoords);

   // Calculate arrow info (line only)
   LocationType arrowStartPoint; // The start point of the arrow line
   LocationType arrowEndPoint;   // The end point of the arrow line
   double arrowOffset = 0;       // Normal offset from main line to arrow
   double arrowLength = 0;       // The length of the arrow line (in pixels)
   arrowOffset = (pixelSize == 0.0) ? 0.0 : mArrowOffset * sqrtLineWidth / pixelSize;
   DrawUtil::getParallelLine(llCorner, urCorner, arrowOffset, mArrowRelStartLoc, mArrowRelEndLoc,
      arrowStartPoint, arrowEndPoint);
   arrowLength = sqrt(pow(abs(arrowEndPoint.mX - arrowStartPoint.mX), 2) + 
      pow(abs(arrowEndPoint.mY - arrowStartPoint.mY), 2));

   // Calculate arrow head info (Only half arrow head is drawn)
   LocationType arrowHeadBasePoint; // Location of arrow head base point
   double arrowHeadOffset = 0;      // Perpendicular offset from arrow line to arrow head base
   arrowHeadOffset = (pixelSize == 0.0) ? 0.0 : (mArrowHeadOffset * sqrtLineWidth) / pixelSize;
   
   // Adjust arrow head size if arrow length becomes smaller then arrow head length.
   while (arrowHeadOffset > arrowLength)
   {  // Since arrow head is at 45 degree angle, arrowHeadOffset is same as arrow head length

      // Adjust size of arrow head
      if (arrowHeadOffset >= 1)
      {
         arrowHeadOffset -= 1;
      }
      else if (arrowHeadOffset > .2)
      {
         arrowHeadOffset = arrowHeadOffset - .1;
      }
      else
      {
         break;
      }
      arrowHeadOffset = (arrowHeadOffset < 0) ? 0.0 : arrowHeadOffset;
   }
   
   // Get arrow head base point coordinates from calculated arrow head info
   startPerc = (arrowLength == 0.0) ? 0.0 : 1 - (arrowHeadOffset/arrowLength);
   startPerc = (startPerc < 0.0) ? 0.0 : startPerc;
   startPerc = (startPerc > 1.0) ? 1.0 : startPerc;
   DrawUtil::getParallelLine(arrowStartPoint, arrowEndPoint, arrowHeadOffset, startPerc, 1.0f,
                             arrowHeadBasePoint, junkLocation);

   // End bar coordinates
   LocationType barStartPoint1;
   LocationType barStartPoint2;     // The points that make up the start bar-end (ll corner)
   LocationType barEndPoint1;
   LocationType barEndPoint2;       // The points that make up the end bar-end (ur corner)
   double barLength = 0;            // Bar-ends length
   barLength = (pixelSize == 0.0) ? 0.0 : (mBarEndLength * sqrtLineWidth) / pixelSize;
   DrawUtil::getPerpendicularLine(llCorner, urCorner, barLength, barStartPoint1, barStartPoint2);
   DrawUtil::getPerpendicularLine(urCorner, llCorner, barLength, barEndPoint1, barEndPoint2);

   // Calculate text info
   double textOffset = 0;     // Perpendicular offset from text to text anchor
   int maxTextureSize = 0;    // The max allowable texture size
   textOffset = (pixelSize == 0.0) ? 0.0 : (mTextOffset * sqrtLineWidth) / pixelSize;
   glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
   QFontMetrics ftMetrics(font);
  
   double viewRotation = pView->getRotation();
   viewRotation = GeoConversions::convertDegToRad(viewRotation);

   if (mDrawnDistanceUnit != pLayer->getDistanceUnits() ||
      mDrawnGeocoord != pLayer->getGeocoordType() ||
      mDrawnDmsFormat != pLayer->getGeoFormat())
   {
      refreshGeoInformation(); // only modifies cache stuff
   }

   if (pLayer->getDisplayEndPoints())
   {
      // Calculate start and end text info
      bool startLocDrawTop = false; // Whether to draw the start lat/lon text on the top of the specified point
      bool endLocDrawTop = true;    // Whether to draw the end lat/lon text on the top of the specified point
      if (urCorner.mY < llCorner.mY)
      {
         startLocDrawTop = true;
         endLocDrawTop = false;
      }

      // Calculate start and end location text info
      LocationType startLocPoint;   // The location to display the "start location" text
      LocationType endLocPoint;     // The location to display the "end location" text
      startLocPoint = llCorner;
      endLocPoint = urCorner;
      startLocPoint.mY += textOffset;
      endLocPoint.mY += textOffset;

      if (!mStartLocText.isEmpty())
      {
         DrawUtil::drawRotatedText(mStartLocTextTexture, mStartLocText, font,
            textColor, startLocPoint, viewRotation, startLocDrawTop);
      }
      if (!mEndLocText.isEmpty())
      {
         DrawUtil::drawRotatedText(mEndLocTextTexture, mEndLocText, font,
            textColor, endLocPoint, viewRotation, endLocDrawTop);
      }
   }
   
   if (pLayer->getDisplayBearing())
   {
      // Calculate bearing text info
      LocationType bearingTextStartPoint; // The location to display the bearing text
      LocationType bearingTextEndPoint;   // The pseudo end location of the baring text (only used to calculate angle)
      double bearingTextTheta = 0;        // Angle (in radians) of bearing text
      bool bearingDrawTop = false;        // The vertical origin to draw text from (True = top, False = bottom)
      QRect bearingTextBoundingBox;       // Bounding box surrounding the bearing text
      bearingTextBoundingBox = ftMetrics.boundingRect(0, 0, maxTextureSize, maxTextureSize, 
         Qt::AlignLeft | Qt::TextWordWrap, mBearingText);
      if (arrowEndPoint.mX < arrowStartPoint.mX)   // To the left of the origin (text underneath line)
      {
         startPerc = ((pixelSize == 0.0) || (arrowLength == 0.0)) ? 0.0 :
            1.0 - (bearingTextBoundingBox.width()/pixelSize)/arrowLength;
         startPerc = (startPerc < 0.0) ? 0.0 : startPerc;
         startPerc = (startPerc > 1.0) ? 1.0 : startPerc;
         DrawUtil::getParallelLine(arrowEndPoint, arrowStartPoint, (-1) * textOffset, startPerc, 1.0f,
            bearingTextStartPoint, bearingTextEndPoint);
         bearingDrawTop = true;
      } 
      else   // To the right of the origin (text on top of the line)
      {
         DrawUtil::getParallelLine(arrowStartPoint, arrowEndPoint, textOffset, 0.0f, 1.0f,
            bearingTextStartPoint, bearingTextEndPoint);
         bearingDrawTop = false;
      }
      bearingTextTheta = ((bearingTextEndPoint.mX - bearingTextStartPoint.mX) == 0.0) ? 0.0 :
         atan((bearingTextEndPoint.mY - bearingTextStartPoint.mY) /
         (bearingTextEndPoint.mX - bearingTextStartPoint.mX));

      if (!mBearingText.isEmpty())
      {
         DrawUtil::drawRotatedText(mBearingTextTexture, mBearingText, font,
            textColor, bearingTextStartPoint, bearingTextTheta, bearingDrawTop);
      }  
   }
   
   if (pLayer->getDisplayDistance())
   {
      // Calculate distance text info
      LocationType distanceTextStartPoint;   // The location to display the distance text
      LocationType distanceTextEndPoint;     // The pseudo end location of the distance text (only used to calculate
                                             // angle)
      double distanceTextTheta = 0;          // Angle (in radians) of distnace text
      bool distanceDrawTop = false;          // The vertical origin to draw text from (True = top, False = bottom)
      double distanceTextWidth = 0;          // The width of the ditance text bounding box (in screen pixels)
      QRect distanceTextBoundingBox;         // Bounding box that surrounds the distance text
      distanceTextBoundingBox = ftMetrics.boundingRect(0, 0, maxTextureSize, maxTextureSize, 
         Qt::AlignLeft | Qt::TextWordWrap, mDistanceText);
      distanceTextWidth = distanceTextBoundingBox.width();
      if ((pixelSize == 0.0) || (lineLength == 0.0))
      {
         startPerc = 0.0;
      } 
      else
      {
         if (urCorner.mX < llCorner.mX)
         {
            startPerc = .5 + (distanceTextWidth/pixelSize/2)/lineLength;
            distanceDrawTop = false;
         }
         else
         {
            startPerc = .5 - (distanceTextWidth/pixelSize/2)/lineLength;
            distanceDrawTop = true;
         }
      }
      startPerc = (startPerc < 0.0) ? 0.0 : startPerc;
      startPerc = (startPerc > 1.0) ? 1.0 : startPerc;
      DrawUtil::getParallelLine(llCorner, urCorner, (-1) * textOffset, startPerc, 1.0f, distanceTextStartPoint,
         distanceTextEndPoint);
      if (lineLength < (distanceTextBoundingBox.width()/pixelSize))
      {
         if (urCorner.mX < llCorner.mX)
         {
            DrawUtil::getParallelLine(llCorner, urCorner, (-1) * textOffset, 0.0f, 1.0f, distanceTextEndPoint,
               distanceTextStartPoint);
         } 
         else
         {
            DrawUtil::getParallelLine(llCorner, urCorner, (-1) * textOffset, 0.0f, 1.0f, distanceTextStartPoint,
               distanceTextEndPoint);
         }

      }
      distanceTextTheta = ((distanceTextEndPoint.mX - distanceTextStartPoint.mX) == 0.0) ? 0.0 :
         atan((distanceTextEndPoint.mY - distanceTextStartPoint.mY) /
         (distanceTextEndPoint.mX - distanceTextStartPoint.mX));

      if (!mDistanceText.isEmpty())
      {
         DrawUtil::drawRotatedText(mDistanceTextTexture, mDistanceText, font,
            textColor, distanceTextStartPoint, distanceTextTheta, distanceDrawTop);
      }
   }

   // GL options
   glLineWidth(lineWidth);
   #if defined(WIN_API)
      glEnable(GL_LINE_SMOOTH);
   #else
      if (lineWidth == 1.0)
      {
         glEnable(GL_LINE_SMOOTH);
      }
      else
      {
         glDisable(GL_LINE_SMOOTH);
      }
   #endif
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glColor3ub(lineColor.mRed, lineColor.mGreen, lineColor.mBlue);
   
   // Draw the end bar
   glBegin(GL_LINE_STRIP);
   glVertex2d(barEndPoint1.mX, barEndPoint1.mY);
   glVertex2d(barEndPoint2.mX, barEndPoint2.mY);
   glEnd();

   // Draw the start bar
   glBegin(GL_LINE_STRIP);
   glVertex2d(barStartPoint1.mX, barStartPoint1.mY);
   glVertex2d(barStartPoint2.mX, barStartPoint2.mY);
   glEnd();

   // Draw the arrow if bearing is displayed
   if (pLayer->getDisplayBearing())
   {
      glBegin(GL_LINE_STRIP);
      glVertex2d(arrowStartPoint.mX, arrowStartPoint.mY);
      glVertex2d(arrowEndPoint.mX, arrowEndPoint.mY);
      glVertex2d(arrowHeadBasePoint.mX, arrowHeadBasePoint.mY);
      glEnd();
   }

   // Set GL options to draw stippled line (Main line)
   glEnable(GL_LINE_STIPPLE);
   glLineStipple(3, 0xf0f0);
   glColor3ub(fillColor.mRed, fillColor.mGreen, fillColor.mBlue);

   // Draw the stippled line (on top of regular line)
   glBegin(GL_LINE_STRIP);
   glVertex2d(llCorner.mX, llCorner.mY);
   glVertex2d(urCorner.mX, urCorner.mY);
   glEnd();

   // Reset GL options
   glDisable(GL_BLEND);
   glDisable(GL_TEXTURE_2D);
   glDisable(GL_LINE_SMOOTH);
   glDisable(GL_LINE_STIPPLE);
   glLineWidth(1);
}