コード例 #1
0
void TiePointLayerImp::drawLabels(const vector<TiePoint>& points, double sceneSymbolSize,
                                  const int viewableBounds[4]) const
{
   double dRotation = 0.0;

   ViewImp* pView = dynamic_cast<ViewImp*>(getView());
   VERIFYNRV(pView != NULL);

   PerspectiveView* pPerspectiveView = dynamic_cast<PerspectiveView*>(pView);
   if (pPerspectiveView != NULL)
   {
      dRotation = pPerspectiveView->getRotation() * PI / 180.0;
   }

   double textScale = sceneSymbolSize / mSymbolSize;

   LocationType offset;
   sceneSymbolSize *= 1.2; // push the label off the symbol a little
   offset.mX = cos(dRotation) * sceneSymbolSize + 0.5;
   offset.mY = sin(dRotation) * sceneSymbolSize + 0.5;

   QFont font = QApplication::font();
   font.setBold(false);
   font.setPointSize(12);

   vector<TiePoint>::const_iterator pPoint;
   for (pPoint = points.begin(); pPoint != points.end(); ++pPoint)
   {
      LocationType point = getPoint(*pPoint);
      if (isInBounds(point, viewableBounds))
      {
         point += offset;

         QString strText;
         strText.sprintf("%d", pPoint-points.begin()+1);

         LocationType screenCoord;
         translateDataToScreen(point.mX, point.mY, screenCoord.mX, screenCoord.mY);

         int screenX = static_cast<int>(screenCoord.mX);
         int screenY = pView->height() - static_cast<int>(screenCoord.mY);
         pView->renderText(screenX, screenY, strText, font);
      }
   }
}
コード例 #2
0
ファイル: TextObjectImp.cpp プロジェクト: Siddharthk/opticks
QFont TextObjectImp::getScaledFont(double minSize, double maxSize)
{
   QFont scaledFont = getFont();

   GraphicLayer* pLayer = getLayer();
   if (pLayer != NULL)
   {
      // Scale the point size
      double pointSize = scaledFont.pointSizeF();

      PerspectiveView* pPerspectiveView = dynamic_cast<PerspectiveView*>(pLayer->getView());
      if (pPerspectiveView != NULL)
      {
         // Zoom percentage
         double zoomPercent = pPerspectiveView->getZoomPercentage();
         pointSize *= zoomPercent / 100.0;

         // Product DPI
         ProductView* pProductView = dynamic_cast<ProductView*>(pPerspectiveView);
         if (pProductView != NULL)
         {
            int dpi = pProductView->getDpi();
            pointSize *= dpi / 72.0;
         }
      }

      // Restrict to the minimum size
      if (minSize > 0.0)
      {
         pointSize = max(pointSize, minSize);
      }

      // Restrict to the maximum size
      if (maxSize > 0.0)
      {
         pointSize = min(pointSize, maxSize);
      }

      // Set the scaled point size in the font
      scaledFont.setPointSizeF(pointSize);
   }

   return scaledFont;
}
コード例 #3
0
ファイル: Kml.cpp プロジェクト: Tom-VdE/opticks
void Kml::generateGroundOverlayLayer(Layer* pLayer, bool visible, int order, const Layer* pGeoLayer, int frame)
{
   VERIFYNRV(pGeoLayer != NULL && pLayer != NULL);
   PerspectiveView* pView = dynamic_cast<PerspectiveView*>(pLayer->getView());
   VERIFYNRV(pView != NULL);

   // block adding current actions to the view's undo buffer
   UndoLock lock(pView);
   double originalAngle = pView->getRotation();

   // get rotation for north up
   double angle(0.0);
   RasterElement* pRaster = dynamic_cast<RasterElement*>(pGeoLayer->getDataElement());  // get the primary raster element
   VERIFYNRV(GeoAlgorithms::getAngleToNorth(pRaster, pView, angle) != false);
   pView->rotateTo(angle);

   // since north up only rotates, check for left-right reversed
   LocationType ll, ul, ur, lr;
   pView->getVisibleCorners(ll, ul, ur, lr);
   bool imageFlipped(ll.mX > ur.mX);
   if (imageFlipped)
   {
      pView->flipHorizontal();
   }
   pView->refresh();

   XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* pGroundOverlay = mXml.addElement("GroundOverlay");
   mXml.pushAddPoint(pGroundOverlay);
   string name = pLayer->getDisplayName();
   if (name.empty())
   {
      name = pLayer->getName();
   }
   if (frame >= 0)
   {
      name += " frame " + StringUtilities::toDisplayString(frame+1);
   }
   mXml.addText(name, mXml.addElement("name"));
   mXml.addText(pLayer->getDisplayText(), mXml.addElement("description"));
   mXml.addText(visible ? "1" : "0", mXml.addElement("visibility"));
   mXml.addText(QString::number(order).toAscii().data(), mXml.addElement("drawOrder"));
   QString layerId = QString::fromStdString(pLayer->getId());
   if (mExportImages)
   {
      QByteArray bytes(5 * 1024 * 1024, '\0');
      QBuffer buffer(&bytes);
      QString fileName = layerId;
      if (frame >= 0)
      {
         fileName += QString("/frame%1").arg(frame);
         mXml.pushAddPoint(mXml.addElement("TimeStamp"));
         QDateTime dt = QDateTime::currentDateTime();
         dt.setTime(QTime(0, 0, frame));
         mXml.addText(dt.toString(Qt::ISODate).toStdString(), mXml.addElement("when"));
         mXml.popAddPoint();
      }
      fileName += ".png";
      if (ImageHandler::getSessionItemImage(pLayer, buffer, "PNG", frame))
      {
         mImages[fileName] = bytes;
         mXml.pushAddPoint(mXml.addElement("Icon"));
         mXml.addText(fileName.toStdString(), mXml.addElement("href"));
         mXml.popAddPoint();
      }
   }
   else
   {
      layerId = QUrl::toPercentEncoding(layerId);
      QString imageUrl(QString("http://localhost:%1/images/%2.png").arg(KMLServer::getSettingKmlServerPort()).arg(
         layerId));
      if (frame >= 0)
      {
         imageUrl += QString("?frame=%1").arg(frame);
         mXml.pushAddPoint(mXml.addElement("TimeStamp"));
         QDateTime dt = QDateTime::currentDateTime();
         dt.setTime(QTime(0, 0, frame));
         mXml.addText(dt.toString(Qt::ISODate).toStdString(), mXml.addElement("when"));
         mXml.popAddPoint();
      }
      mXml.pushAddPoint(mXml.addElement("Icon"));
      mXml.addText(imageUrl.toStdString(), mXml.addElement("href"));
      mXml.popAddPoint();
   }
   generateBoundingBox(pGeoLayer);

   // add ExtendedData
   const DynamicObject* pMeta = pLayer->getDataElement()->getMetadata();
   VERIFYNRV(pMeta);
   const DataVariant& ked(pMeta->getAttributeByPath("KML/ExtendedData"));
   if (ked.isValid())
   {
      std::string kedString = ked.toXmlString();
      XmlReader reader(Service<MessageLogMgr>()->getLog(), false);
      XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* kedDoc = reader.parseString(ked.toXmlString());
      if (kedDoc != NULL)
      {
         XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pKedNode = kedDoc->getDocumentElement();
         if (pKedNode != NULL)
         {
            bool extraPop = false;
            if (std::string(A(pKedNode->getNodeName())) != "ExtendedData")
            {
               mXml.pushAddPoint(mXml.addElement("ExtendedData"));
               extraPop = true;
            }
            pKedNode = mXml.peekAddPoint()->getOwnerDocument()->importNode(pKedNode, true);
            mXml.peekAddPoint()->appendChild(pKedNode);
            if (extraPop)
            {
               mXml.popAddPoint();
            }
         }
      }
   }

   mXml.popAddPoint(); // GroundOverlay

   // restore original rotation
   if (imageFlipped)
   {
      pView->flipHorizontal();
   }
   pView->rotateTo(originalAngle);
   pView->refresh();
}
コード例 #4
0
ファイル: TextObjectImp.cpp プロジェクト: Siddharthk/opticks
void TextObjectImp::updateBoundingBox()
{
   // Get the width and height of the bounding box in screen pixels based on the scaled text image size
   int iWidth = 0;
   int iHeight = 0;

   string text = getSubstitutedText();
   if (text.empty() == false)
   {
      QString strMessage = QString::fromStdString(text);
      QFont scaledFont = getScaledFont();

      int iMaxSize = 0;
      glGetIntegerv(GL_MAX_TEXTURE_SIZE, &iMaxSize);

      int iAlignment = getTextAlignment();

      QFontMetrics ftMetrics(scaledFont);
      QRect boundingBox = ftMetrics.boundingRect(0, 0, iMaxSize, iMaxSize,
         iAlignment | Qt::TextWordWrap, strMessage);

      iWidth = boundingBox.width();
      iHeight = boundingBox.height();
   }

   // Get the current bounding box
   LocationType llCorner = getLlCorner();
   LocationType urCorner = getUrCorner();

   // Use the lower left corner as the anchor and compute the data coordinate of the upper right corner
   GraphicLayer* pLayer = getLayer();
   if (pLayer != NULL)
   {
      // Compute the upper right coordinate
      PerspectiveView* pView = dynamic_cast<PerspectiveView*>(pLayer->getView());
      if (pView != NULL)
      {
         double zoomFactor = 100.0 / pView->getZoomPercentage();
         double xScale = zoomFactor / pLayer->getXScaleFactor();
         double yScale = zoomFactor / pLayer->getYScaleFactor();

         urCorner.mX = llCorner.mX + (iWidth * xScale);
         urCorner.mY = llCorner.mY + (iHeight * yScale);
      }

      if (dynamic_cast<OrthographicView*>(pLayer->getView()) != NULL)
      {
         double dScreenX = 0.0;
         double dScreenY = 0.0;
         pLayer->translateDataToScreen(llCorner.mX, llCorner.mY, dScreenX, dScreenY);
         pLayer->translateScreenToData(dScreenX + iWidth, dScreenY + iHeight, urCorner.mX, urCorner.mY);
      }
   }
   else
   {
      urCorner.mX = llCorner.mX + iWidth;
      urCorner.mY = llCorner.mY + iHeight;
   }

   // Update the bounding box and selection handles
   setBoundingBox(llCorner, urCorner);
   updateHandles();

   mUpdateBoundingBox = false;
}
コード例 #5
0
ファイル: MovieExporter.cpp プロジェクト: Tom-VdE/opticks
ValidationResultType MovieExporter::validate(const PlugInArgList* pArgList, string& errorMessage) const
{
   /* validate some arguments */
   ValidationResultType result = ExporterShell::validate(pArgList, errorMessage);
   if (result != VALIDATE_SUCCESS)
   {
      return result;
   }
   View* pView = pArgList->getPlugInArgValue<View>(Exporter::ExportItemArg());
   if (pView == NULL)
   {
      errorMessage = "No view specified. Nothing to export.";
      return VALIDATE_FAILURE;
   }
   AnimationController* pController = pView->getAnimationController();
   if (pController == NULL)
   {
      errorMessage = "No animation is associated with this view. Nothing to export.";
      return VALIDATE_FAILURE;
   }

   /* validate the framerate */
   boost::rational<int> expectedFrameRate;

   // first, get the framerate from the arg list
   // next, try the option widget
   // next, get from the animation controller
   // finally, default to the config settings
   int framerateNum = 0;
   int framerateDen = 0;
   if (pArgList->getPlugInArgValue("Framerate Numerator", framerateNum) &&
      pArgList->getPlugInArgValue("Framerate Denominator", framerateDen))
   {
      try
      {
         expectedFrameRate.assign(framerateNum, framerateDen);
      }
      catch (const boost::bad_rational&)
      {
         errorMessage = "Invalid framerate specified";
         return VALIDATE_FAILURE;
      }
   }
   if (expectedFrameRate == 0)
   {
      if (mpOptionWidget.get() != NULL)
      {
         FramerateWidget* pFramerateWidget = mpOptionWidget->getFramerateWidget();
         VERIFYRV(pFramerateWidget != NULL, VALIDATE_FAILURE);

         expectedFrameRate = pFramerateWidget->getFramerate();
      }
      if (expectedFrameRate == 0)
      {
         expectedFrameRate = getFrameRate(pController);
      }
      if (expectedFrameRate == 0)
      {
         errorMessage = "No framerate specified";
         return VALIDATE_FAILURE;
      }
   }

   boost::rational<int> actualFrameRate = convertToValidFrameRate(expectedFrameRate);
   if (actualFrameRate != 0)
   {
      if (actualFrameRate < getFrameRate(pController))
      {
         errorMessage = "The selected output frame rate may not encode all the frames in the movie.  "
                        "Frames may be dropped.";
         result = VALIDATE_INFO;
      }
      if (actualFrameRate != expectedFrameRate)
      {
         QString msg = QString("The selected animation frame rate (%1/%2 fps) can not be represented in the "
            "selected movie format. A frame rate of %3/%4 fps is being used instead.")
            .arg(expectedFrameRate.numerator())
            .arg(expectedFrameRate.denominator())
            .arg(actualFrameRate.numerator())
            .arg(actualFrameRate.denominator());
         if (!errorMessage.empty())
         {
            errorMessage += "\n\n";
         }
         errorMessage += msg.toStdString();
         result = VALIDATE_INFO;
      }
   }

   // check the frame resolution
   int resolutionX(-1);
   int resolutionY(-1);
   bool fullResolution(false);
   bool fixedResolution(false);
   if (!pArgList->getPlugInArgValue("Resolution X", resolutionX) ||
       !pArgList->getPlugInArgValue("Resolution Y", resolutionY))
   {
      if (mpOptionWidget.get() != NULL)
      {
         ViewResolutionWidget* pResolutionWidget = mpOptionWidget->getResolutionWidget();
         VERIFYRV(pResolutionWidget != NULL, VALIDATE_FAILURE);

         switch(pResolutionWidget->getResolutionType())
         {
         case OptionsMovieExporter::VIEW_RESOLUTION:
            resolutionX = -1;
            resolutionY = -1;
            fullResolution = false;
            break;
         case OptionsMovieExporter::FULL_RESOLUTION:
            resolutionX = -1;
            resolutionY = -1;
            fullResolution = true;
            break;
         case OptionsMovieExporter::FIXED_RESOLUTION:
         {
            QSize resolution = pResolutionWidget->getResolution();
            resolutionX = resolution.width();
            resolutionY = resolution.height();
            fullResolution = false;
            fixedResolution = true;
            break;
         }
         default:
            break; // nothing
         }
      }
      else
      {
         switch(StringUtilities::fromXmlString<OptionsMovieExporter::ResolutionType>(
            OptionsMovieExporter::getSettingResolutionType()))
         {
         case OptionsMovieExporter::VIEW_RESOLUTION:
            resolutionX = -1;
            resolutionY = -1;
            fullResolution = false;
            break;
         case OptionsMovieExporter::FULL_RESOLUTION:
            resolutionX = -1;
            resolutionY = -1;
            fullResolution = true;
            break;
         case OptionsMovieExporter::FIXED_RESOLUTION:
            resolutionX = OptionsMovieExporter::getSettingWidth();
            resolutionY = OptionsMovieExporter::getSettingHeight();
            fullResolution = false;
            fixedResolution = true;
            break;
         default:
            break; // nothing
         }
      }
   }

   if (resolutionX <= 0 || resolutionY <= 0)
   {
      QWidget* pWidget = pView->getWidget();
      if (pWidget != NULL)
      {
         resolutionX = pWidget->width();
         resolutionY = pWidget->height();
         if (fullResolution)
         {
            PerspectiveView* pPersp = dynamic_cast<PerspectiveView*>(pView);
            if (pPersp != NULL && pPersp->getZoomPercentage() > 100.)
            {
               fullResolution = false;
               if (mpProgress != NULL)
               {
                  errorMessage += "Full resolution export will be smaller than "
                     "view export, export resolution will be changed to current view size.\n";
                  if (result == VALIDATE_SUCCESS)
                  {
                     result = VALIDATE_INFO;
                  }
               }
            }
            else
            {
               // translate to data coordinate
               double x1 = 0.0;
               double y1 = 0.0;
               double x2 = 0.0;
               double y2 = 0.0;
               pView->translateScreenToWorld(0.0, 0.0, x1, y1);
               pView->translateScreenToWorld(resolutionX, resolutionY, x2, y2);
               resolutionX = (x2 > x1) ? (x2 - x1 + 0.5) : (x1 - x2 + 0.5);
               resolutionY = (y2 > y1) ? (y2 - y1 + 0.5) : (y1 - y2 + 0.5);
            }
         }
      }
      else
      {
         errorMessage += "Can't determine output resolution.\n";
         return VALIDATE_FAILURE;
      }
   }

   int oldResX = resolutionX;
   int oldResY = resolutionY;
   if (!convertToValidResolution(resolutionX, resolutionY) ||
         (fixedResolution && (resolutionX != oldResX || resolutionY != oldResY)))
   {
      if (mpOptionWidget.get() != NULL)
      {
         ViewResolutionWidget* pResolutionWidget = mpOptionWidget->getResolutionWidget();
         VERIFYRV(pResolutionWidget != NULL, VALIDATE_FAILURE);
         pResolutionWidget->setResolution(QSize(resolutionX, resolutionY), pResolutionWidget->getResolutionType());
      }
      errorMessage +=
         "The export resolution does not meet the requirements of the the selected CODEC and will be adjusted.\n";
      if (result != VALIDATE_FAILURE)
      {
         result = VALIDATE_INFO;
      }
   }

   return result;
}
コード例 #6
0
ファイル: MovieExporter.cpp プロジェクト: Tom-VdE/opticks
bool MovieExporter::execute(PlugInArgList* pInArgList, PlugInArgList* pOutArgList)
{
   FileDescriptor* pFileDescriptor(NULL);
   string filename;
   View* pView(NULL);
   AnimationController* pController(NULL);
   FrameType eType;
   AVOutputFormat* pOutFormat(NULL);
   int resolutionX(-1);
   int resolutionY(-1);
   rational<int> framerate(0);
   unsigned int bitrate(0);
   double startExport(0.0);
   double stopExport(0.0);
   bool fullResolution(false);

   mpProgress = NULL;
   mpStep = StepResource("Export movie", "app", "2233BFC9-9C51-4e31-A8C5-2512925CBE6D");

   // get input arguments and log some useful info about them
   { // scope the MessageResource
      MessageResource pMsg("Input arguments", "app", "4551F478-E182-4b56-B88F-6682F0E3A2CF");

      mpProgress = pInArgList->getPlugInArgValue<Progress>(Executable::ProgressArg());
      pMsg->addBooleanProperty("Progress Present", (mpProgress != NULL));

      pFileDescriptor = pInArgList->getPlugInArgValue<FileDescriptor>(Exporter::ExportDescriptorArg());
      if (pFileDescriptor == NULL)
      {
         log_error("No file specified");
         return false;
      }
      pMsg->addProperty("Destination", pFileDescriptor->getFilename());
      filename = pFileDescriptor->getFilename().getFullPathAndName();

      pView = pInArgList->getPlugInArgValue<View>(Exporter::ExportItemArg());
      if (pView == NULL)
      {
         log_error("No view specified");
         return false;
      }

      pController = pView->getAnimationController();
      if (pController == NULL)
      {
         log_error("No animation controller specified");
         return false;
      }
      pMsg->addProperty("Animation Controller Name", pController->getName());

      pOutFormat = getOutputFormat();
      if (pOutFormat == NULL)
      {
         log_error("Can't determine output format or format not supported.");
         return false;
      }
      pMsg->addProperty("Format", pOutFormat->long_name);

      bool resolutionFromInputArgs(false);
      if (!pInArgList->getPlugInArgValue("Resolution X", resolutionX) ||
         !pInArgList->getPlugInArgValue("Resolution Y", resolutionY))
      {
         if (mpOptionWidget.get() != NULL)
         {
            ViewResolutionWidget* pResolutionWidget = mpOptionWidget->getResolutionWidget();
            VERIFY(pResolutionWidget != NULL);

            switch(pResolutionWidget->getResolutionType())
            {
            case OptionsMovieExporter::VIEW_RESOLUTION:
               resolutionX = -1;
               resolutionY = -1;
               fullResolution = false;
               break;
            case OptionsMovieExporter::FULL_RESOLUTION:
               resolutionX = -1;
               resolutionY = -1;
               fullResolution = true;
               break;
            case OptionsMovieExporter::FIXED_RESOLUTION:
            {
               QSize resolution = pResolutionWidget->getResolution();
               resolutionX = resolution.width();
               resolutionY = resolution.height();
               fullResolution = false;
               break;
            }
            default:
               break; // nothing
            }
         }
         else
         {
            switch(StringUtilities::fromXmlString<OptionsMovieExporter::ResolutionType>(
               OptionsMovieExporter::getSettingResolutionType()))
            {
            case OptionsMovieExporter::VIEW_RESOLUTION:
               resolutionX = -1;
               resolutionY = -1;
               fullResolution = false;
               break;
            case OptionsMovieExporter::FULL_RESOLUTION:
               resolutionX = -1;
               resolutionY = -1;
               fullResolution = true;
               break;
            case OptionsMovieExporter::FIXED_RESOLUTION:
               resolutionX = OptionsMovieExporter::getSettingWidth();
               resolutionY = OptionsMovieExporter::getSettingHeight();
               fullResolution = false;
               break;
            default:
               break; // nothing
            }
         }
      }
      else
      {
         resolutionFromInputArgs = true;
      }

      if (resolutionX <= 0 || resolutionY <= 0)
      {
         QWidget* pWidget = pView->getWidget();
         if (pWidget != NULL)
         {
            resolutionX = pWidget->width();
            resolutionY = pWidget->height();
            resolutionFromInputArgs = false;
            if (fullResolution)
            {
               PerspectiveView* pPersp = dynamic_cast<PerspectiveView*>(pView);
               if (pPersp != NULL && pPersp->getZoomPercentage() > 100.)
               {
                  fullResolution = false;
                  if (mpProgress != NULL)
                  {
                     mpProgress->updateProgress("Full resolution export will be smaller than "
                        "view export, changing export resolution to current view size.", 0, WARNING);
                  }
               }
               else
               {
                  // translate to data coordinate
                  double x1 = 0.0;
                  double y1 = 0.0;
                  double x2 = 0.0;
                  double y2 = 0.0;
                  pView->translateScreenToWorld(0.0, 0.0, x1, y1);
                  pView->translateScreenToWorld(resolutionX, resolutionY, x2, y2);
                  resolutionX = (x2 > x1) ? (x2 - x1 + 0.5) : (x1 - x2 + 0.5);
                  resolutionY = (y2 > y1) ? (y2 - y1 + 0.5) : (y1 - y2 + 0.5);
               }
            }
         }
         else
         {
            log_error("Can't determine output resolution.");
            return false;
         }
      }

      int oldResX = resolutionX;
      int oldResY = resolutionY;
      if (!convertToValidResolution(resolutionX, resolutionY) ||
            (resolutionFromInputArgs && (resolutionX != oldResX || resolutionY != oldResY)))
      {
         stringstream msg;
         msg << "The export resolution does not meet the requirements of the the selected CODEC. "
                "The input arguments were X resolution of "
             << oldResX << " and Y resolution of " << oldResY << "."
             << "The adjusted resolution was (" << resolutionX << ", " << resolutionY << ")";
         log_error(msg.str());
         return false;
      }

      pMsg->addProperty("Resolution", QString("%1 x %2").arg(resolutionX).arg(resolutionY).toStdString());

      int framerateNum = 0;
      int framerateDen = 0;
      // first, get the framerate from the arg list
      // next, try the option widget
      // next, get from the animation controller
      if (pInArgList->getPlugInArgValue("Framerate Numerator", framerateNum) &&
         pInArgList->getPlugInArgValue("Framerate Denominator", framerateDen))
      {
         try
         {
            framerate.assign(framerateNum, framerateDen);
         }
         catch (const boost::bad_rational&)
         {
            // Do nothing; the code below handles this case
         }
      }
      if (framerate == 0)
      {
         if (mpOptionWidget.get() != NULL)
         {
            FramerateWidget* pFramerateWidget = mpOptionWidget->getFramerateWidget();
            VERIFY(pFramerateWidget != NULL);

            framerate = pFramerateWidget->getFramerate();
         }
         else
         {
            framerate = getFrameRate(pController);
         }
      }

      if (framerate == 0)
      {
         log_error("No framerate specified");
         return false;
      }

      // Validate the framerate
      boost::rational<int> validFrameRate = convertToValidFrameRate(framerate);
      if (validFrameRate != framerate)
      {
         QString msg = QString("The selected animation frame rate (%1/%2 fps) can not be represented in the "
                               "selected movie format. A frame rate of %3/%4 fps is being used instead.")
                              .arg(framerate.numerator())
                              .arg(framerate.denominator())
                              .arg(validFrameRate.numerator())
                              .arg(validFrameRate.denominator());
         mpProgress->updateProgress(msg.toStdString(), 0, WARNING);

         framerate = validFrameRate;
      }

      pMsg->addProperty("Framerate",
         QString("%1/%2").arg(framerate.numerator()).arg(framerate.denominator()).toStdString());

      if (!pInArgList->getPlugInArgValue("Bitrate", bitrate))
      {
         if (mpOptionWidget.get() != NULL)
         {
            BitrateWidget* pBitrateWidget = mpOptionWidget->getBitrateWidget();
            VERIFY(pBitrateWidget != NULL);

            bitrate = pBitrateWidget->getBitrate();
         }
         else
         {
            bitrate = OptionsMovieExporter::getSettingBitrate();
         }
      }
      pMsg->addProperty("Bitrate", QString::number(bitrate).toStdString());

      eType = pController->getFrameType();
      if (!pInArgList->getPlugInArgValue("Start Export", startExport))
      {
         if (mpOptionWidget.get() != NULL)
         {
            AnimationFrameSubsetWidget* pSubsetWidget = mpOptionWidget->getSubsetWidget();
            VERIFY(pSubsetWidget != NULL);

            startExport = pSubsetWidget->getStartFrame();
         }
         else
         {
            if (pController->getBumpersEnabled())
            {
               startExport = pController->getStartBumper();
            }
            else
            {
               startExport = pController->getStartFrame();
            }
         }
      }
      else
      {
         // adjust to 0-based since the input arg uses 1-based
         --startExport;
      }

      if (!pInArgList->getPlugInArgValue("Stop Export", stopExport))
      {
         if (mpOptionWidget.get() != NULL)
         {
            AnimationFrameSubsetWidget* pSubsetWidget = mpOptionWidget->getSubsetWidget();
            VERIFY(pSubsetWidget != NULL);

            stopExport = pSubsetWidget->getStopFrame();
         }
         else
         {
            if (pController->getBumpersEnabled())
            {
               stopExport = pController->getStopBumper();
            }
            else
            {
               stopExport = pController->getStopFrame();
            }
         }
      }
      else
      {
         // adjust to 0-based since the input arg users 1-based
         --stopExport;
      }
      string valueType("Time");
      if (eType == FRAME_ID)
      {
         valueType = "Frame";
      }

      pMsg->addProperty("Start "+valueType, QString::number(startExport).toStdString());
      pMsg->addProperty("Stop "+valueType, QString::number(stopExport).toStdString());
   }

   AvFormatContextResource pFormat(pOutFormat);
   VERIFY(pFormat != NULL);
   snprintf(pFormat->filename, sizeof(pFormat->filename), "%s", filename.c_str());
   AvStreamResource pVideoStream(pFormat, pOutFormat->video_codec);
   if (pVideoStream == NULL)
   {
      log_error("Unable to create video stream.");
      return false;
   }

   /**
    * allow changing of:
    *    dia_size/
    */
   AVCodecContext* pCodecContext = pVideoStream->codec;
   if (!setAvCodecOptions(pCodecContext))
   {
      log_error("Unable to initialize CODEC options");
      return false;
   }
   // set time_base, width, height, and bitrate here since
   // they can be passed in via the input args
   pCodecContext->width = resolutionX;
   pCodecContext->height = resolutionY;
   pCodecContext->bit_rate = bitrate * 1000;
   // the AVCodecContext wants a time_base which is
   // the inverse of fps.
   pCodecContext->time_base.num = framerate.denominator();
   pCodecContext->time_base.den = framerate.numerator();

   if (av_set_parameters(pFormat, NULL) < 0)
   {
      log_error("Invalid output format parameters.");
      return false;
   }
   if (!open_video(pFormat, pVideoStream))
   {
      log_error("Unable to initialize video stream.");
      return false;
   }
   if (url_fopen(&pFormat->pb, filename.c_str(), URL_WRONLY) < 0)
   {
      log_error("Could not open the output file. Ensure the destination directory is writable.");
      return false;
   }
   av_write_header(pFormat);

   // calculate time interval
   if ((framerate < getFrameRate(pController)) && (mpProgress != NULL))
   {
      mpProgress->updateProgress("The selected output frame rate may not encode all the frames in the movie.  "
                                 "Frames may be dropped.", 0, WARNING);
   }

   // do not use the boost::rational<int> overloaded operator '/' since it truncates type double to int
   double interval = pController->getIntervalMultiplier() * framerate.denominator() / framerate.numerator();

   // export the frames
   AVFrame* pTmpPicture = alloc_picture(PIX_FMT_RGBA32, pCodecContext->width, pCodecContext->height);
   if (pTmpPicture == NULL)
   {
      QString msg("Unable to allocate frame buffer of size %1 x %2");
      log_error(msg.arg(pCodecContext->width).arg(pCodecContext->height).toStdString());
      return false;
   }
   QImage image(pTmpPicture->data[0], pCodecContext->width, pCodecContext->height, QImage::Format_ARGB32);

   // For frame id based animation, each band of the data set fills one second of animation. 
   // If the requested frame rate for export is 15 fps, then each band is replicated 15 times. The execution
   // loop uses a pseudo time value, video_pts, to walk through the animation. The interval between 
   // exported frames is the inverse of the frame rate, e.g., for 15 fps the interval is 0.06667.
   // To fully export the last requested frame, we need to add just under an extra pseudo second to
   // the end time - stopExport. If we added a full extra second, we would export one video frame of the band past
   // the last requested frame - could cause crash if the last request frame was the last band.
   if (eType == FRAME_ID)
   {
      stopExport += 0.99;
   }

   bool drawClassMarkings = fullResolution &&
      Service<ConfigurationSettings>()->getSettingDisplayClassificationMarkings();
   QString classText;
   QFont classFont;
   QColor classColor;
   QPoint classPositionTop;
   QPoint classPositionBottom;
   const int shadowOffset = 2;
   if (drawClassMarkings)
   {
      const int topMargin = 1;
      const int bottomMargin = 4;
      const int leftMargin = 5;
      const int rightMargin = 5;

      classText = QString::fromStdString(pView->getClassificationText());
      classColor = COLORTYPE_TO_QCOLOR(pView->getClassificationColor());
      pView->getClassificationFont(classFont);
      QFontMetrics fontMetrics(classFont);
      int classWidth = fontMetrics.width(classText);
      int classHeight = fontMetrics.ascent();
      int topX((pCodecContext->width / 2) - (classWidth / 2) + shadowOffset);
      int bottomX((pCodecContext->width / 2) - (classWidth / 2));
      // determine the classification position
      switch (pView->getClassificationPosition())
      {
      case TOP_LEFT_BOTTOM_LEFT:
         topX = leftMargin + shadowOffset;
         bottomX = leftMargin + shadowOffset;
         break;
      case TOP_LEFT_BOTTOM_RIGHT:
         topX = leftMargin + shadowOffset;
         bottomX = pCodecContext->width - rightMargin - classWidth + shadowOffset;
         break;
      case TOP_RIGHT_BOTTOM_LEFT:
         topX = pCodecContext->width - rightMargin - classWidth + shadowOffset;
         bottomX = leftMargin + shadowOffset;
         break;
      case TOP_RIGHT_BOTTOM_RIGHT:
         topX = pCodecContext->width - rightMargin - classWidth + shadowOffset;
         bottomX = pCodecContext->width - rightMargin - classWidth + shadowOffset;
         break;
      default:
         // nothing to do, markings centered by default
         break;
      }
      int screenY = 1 + classHeight;
      classPositionTop = QPoint(topX, 1 + classHeight);
      classPositionBottom = QPoint(bottomX, pCodecContext->height - 1);
   }

   // make sure controller is not running prior to export. Save current state and restore after export finished
   AnimationState savedAnimationState = pController->getAnimationState();
   pController->setAnimationState(STOP);

   for (double video_pts = startExport; video_pts <= stopExport; video_pts += interval)
   {
      if (isAborted() == true)
      {
         // reset resources to close output file so it can be deleted
         pVideoStream = AvStreamResource();
         pFormat = AvFormatContextResource(NULL);
         mpPicture = NULL;
         free(mpVideoOutbuf);
         mpVideoOutbuf = NULL;
         remove(filename.c_str());

         if (mpProgress != NULL)
         {
            mpProgress->updateProgress("Export aborted", 0, ABORT);
         }
         pController->setAnimationState(savedAnimationState);
         mpStep->finalize(Message::Abort);
         return false;
      }

      // generate the next frame
      pController->setCurrentFrame(video_pts);
      if (mpProgress != NULL)
      {
         double progressValue = (video_pts - startExport) / (stopExport - startExport) * 100.0;
         mpProgress->updateProgress("Saving movie", static_cast<int>(progressValue), NORMAL);
      }
      if (fullResolution)
      {
         QSize totalSize(pCodecContext->width, pCodecContext->height);
         QSize subImageSize(pView->getWidget()->width(), pView->getWidget()->height());
         // Make sure that the sub-image and the main image have the same aspect ratio to
         // minimize wasted tile space
         if (pCodecContext->width > pCodecContext->height)
         {
            subImageSize.setWidth(
               totalSize.width() / static_cast<float>(totalSize.height()) * subImageSize.height() + 0.5);
         }
         else
         {
            subImageSize.setHeight(
               totalSize.height() / static_cast<float>(totalSize.width()) * subImageSize.width() + 0.5);
         }
         // Remove pan and zoom limits so they don't interfere with the subimage grab
         // and restore them when done
         SpatialDataView* pSdv = dynamic_cast<SpatialDataView*>(pView);
         PanLimitType panLimit;
         if (pSdv != NULL)
         {
            panLimit = pSdv->getPanLimit();
            pSdv->setPanLimit(NO_LIMIT);
         }
         View::SubImageIterator* pSubImage(pView->getSubImageIterator(totalSize, subImageSize));
         if (pSubImage == NULL)
         {
            if (pSdv != NULL)
            {
               pSdv->setPanLimit(panLimit);
            }
            if (mpProgress != NULL)
            {
               mpProgress->updateProgress("Unable to render full scale data", 0, ERRORS);
            }
            pController->setAnimationState(savedAnimationState);
            mpStep->finalize(Message::Failure);
            return false;
         }
         QPainter outPainter(&image);
         QPoint origin(0, image.height() - subImageSize.height());
         while (pSubImage->hasNext())
         {
            QImage subImage;
            if (!pSubImage->next(subImage))
            {
               if (pSdv != NULL)
               {
                  pSdv->setPanLimit(panLimit);
               }
               if (mpProgress != NULL)
               {
                  mpProgress->updateProgress("An error occurred when generating the image", 0, ERRORS);
               }
               pController->setAnimationState(savedAnimationState);
               mpStep->finalize(Message::Failure);
               delete pSubImage;
               return false;
            }
            // copy this subimage to the output buffer
            outPainter.drawImage(origin, subImage);

            int newX = origin.x() + subImage.width();
            int newY = origin.y();
            if (newX >= totalSize.width())
            {
               newY -= subImage.height();
               newX = 0;
            }
            origin = QPoint(newX, newY);
         }
         delete pSubImage;
         if (drawClassMarkings)
         {
            outPainter.setFont(classFont);
            outPainter.setPen(Qt::black);
            outPainter.drawText(classPositionTop, classText);
            outPainter.drawText(classPositionBottom, classText);
            outPainter.setPen(classColor);
            outPainter.drawText(classPositionTop - QPoint(shadowOffset, shadowOffset), classText);
            outPainter.drawText(classPositionBottom - QPoint(shadowOffset, shadowOffset), classText);
         }
         if (pSdv != NULL)
         {
            pSdv->setPanLimit(panLimit);
         }
      }
      else
      {
         pView->getCurrentImage(image);
      }
      img_convert(reinterpret_cast<AVPicture*>(mpPicture),
         pCodecContext->pix_fmt,
         reinterpret_cast<AVPicture*>(pTmpPicture),
         PIX_FMT_RGBA32,
         pCodecContext->width,
         pCodecContext->height);
      if (!write_video_frame(pFormat, pVideoStream))
      {
         // reset resources to close output file so it can be deleted
         pVideoStream = AvStreamResource();
         pFormat = AvFormatContextResource(NULL);
         mpPicture = NULL;
         free(mpVideoOutbuf);
         mpVideoOutbuf = NULL;
         remove(filename.c_str());
         string msg = "Can't write frame.";
         log_error(msg.c_str());
         pController->setAnimationState(savedAnimationState);
         return false;
      }
   }
   for (int frame = 0; frame < pCodecContext->delay; ++frame)
   {
      write_video_frame(pFormat, pVideoStream);
   }

   av_write_trailer(pFormat);

   if (mpProgress != NULL)
   {
      mpProgress->updateProgress("Finished saving movie", 100, NORMAL);
   }

   free(mpVideoOutbuf);
   mpVideoOutbuf = NULL;
   pController->setAnimationState(savedAnimationState);
   mpStep->finalize(Message::Success);
   return true;
}