Example #1
0
vector<Feature*> ShapeFile::addFeatures(GraphicElement* pGraphicElement, GraphicObject* pObject,
                                        RasterElement* pGeoref, string& message)
{
   vector<Feature*> features;
   message.clear();

   if (pGraphicElement == NULL)
   {
      message = "Features cannot be added because the data element is invalid!";
      return features;
   }

   if (pGeoref == NULL || !pGeoref->isGeoreferenced())
   {
      message = "No georeferencing information can be found.";
      return features;
   }

   // Do not allow erase or toggle objects
   if (pObject != NULL)
   {
      if (pObject->getDrawMode() != DRAW)
      {
         message = "The " + pObject->getName() + " object is an erase or toggle object, so a feature "
            "will not be added.";
         return features;
      }
   }

   // Get the objects
   list<GraphicObject*> objects;
   if (pObject != NULL)
   {
      objects.push_back(pObject);
   }
   else
   {
      GraphicGroup* pGroup = pGraphicElement->getGroup();
      if (pGroup != NULL)
      {
         // If the element is displayed in an annotation layer, use the selected objects
         AnnotationLayer* pAnnotationLayer = dynamic_cast<AnnotationLayer*>(pGroup->getLayer());
         if (pAnnotationLayer != NULL)
         {
            pAnnotationLayer->getSelectedObjects(objects);
         }

         if (objects.empty())
         {
            objects = pGroup->getObjects();
         }
      }
   }

   // Create the features
   string elementName = pGraphicElement->getName();
   const DynamicObject* pMetadata = getSourceMetadata(*pGraphicElement);

   switch (mShape)
   {
   case ShapefileTypes::POINT_SHAPE:
      {
         if (dynamic_cast<AnnotationElement*>(pGraphicElement) != NULL)
         {
            if (objects.empty())
            {
               message = "Cannot create a shape file from an empty element.";
               return features;
            }

            // Get names of attributes which should be copied/exported.
            vector<string> attrNames;
            const DynamicObject* pAttributeMetadata = getSourceAttributeMetadata(*pGraphicElement);
            if (pAttributeMetadata != NULL)
            {
               pAttributeMetadata->getAttributeNames(attrNames);
            }

            for (list<GraphicObject*>::const_iterator it = objects.begin(); it != objects.end(); ++it)
            {
               GraphicObject* pCurrentObject = *it;
               if (pCurrentObject != NULL)
               {
                  // Do not allow erase or toggle objects
                  if (pCurrentObject->getDrawMode() != DRAW)
                  {
                     continue;
                  }

                  string objectName = pCurrentObject->getName();
                  vector<LocationType> vertices;
                  pCurrentObject->getRotatedExtents(vertices);

                  // Each point created from this object uses the same metadata.
                  int idx = -1;
                  if (pMetadata != NULL)
                  {
                     idx = getAttributeIndex(*pCurrentObject, *pMetadata);
                  }

                  LocationType pixel;
                  for (vector<LocationType>::const_iterator verticesIter = vertices.begin();
                     verticesIter != vertices.end();
                     ++verticesIter)
                  {
                     // Add the feature
                     SessionItem* pSessionItem = pGraphicElement;
                     if (pObject != NULL)
                     {
                        pSessionItem = pObject;
                     }

                     Feature* pFeature = new Feature(pSessionItem);
                     features.push_back(pFeature);
                     mFeatures.push_back(pFeature);
                     VERIFYNR(pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified)));

                     // Fields
                     pFeature->addField("Name", string());

                     if (!elementName.empty())
                     {
                        if (pObject != NULL)
                        {
                           pFeature->setFieldValue("Name", elementName + ": " + pObject->getName());
                        }
                        else
                        {
                           pFeature->setFieldValue("Name", elementName + ": " + objectName);
                        }
                     }

                     pixel.mX = verticesIter->mX + 0.5;
                     pixel.mY = verticesIter->mY + 0.5;
                     QString pixelName = "(" + QString::number(verticesIter->mX + 1) + ", " + 
                        QString::number(verticesIter->mY + 1) + ")";
                     pFeature->setFieldValue("Pixel", pixelName.toStdString());

                     if (idx > -1 && pAttributeMetadata != NULL)
                     {
                        copyMetadata(attrNames, idx, *pAttributeMetadata, *pFeature);
                     }

                     LocationType geo = pGeoref->convertPixelToGeocoord(*verticesIter);
                     pFeature->addVertex(geo.mY, geo.mX);
                  }
               }
            }
         }
         else
         {
            // The BitMaskIterator does not support negative extents and
            // the BitMask does not correctly handle the outside flag so
            // the BitMaskIterator is used for cases when the outside flag is true and
            // the BitMask is used for cases when the outside flag is false
            AoiElement* pAoiElement = dynamic_cast<AoiElement*>(pGraphicElement);
            VERIFYRV(pAoiElement != NULL, features);
            const BitMask* pMask = pAoiElement->getSelectedPoints();
            if (pObject != NULL)
            {
               pMask = pObject->getPixels();
               if (pMask == NULL)
               {
                  message = "The " + pObject->getName() + " object cannot be represented by the " +
                     StringUtilities::toDisplayString(mShape) + " shape type, so a feature will not be added.";
                  return features;
               }
            }

            if (pMask != NULL)
            {
               BitMaskIterator maskIt(pMask, pGeoref);
               if ((maskIt.getCount() > 0 && pMask->isOutsideSelected() == true) ||
                  (pMask->getCount() > 0 && pMask->isOutsideSelected() == false))
               {
                  // Add features for each selected pixel
                  int startColumn = 0;
                  int endColumn = 0;
                  int startRow = 0;
                  int endRow = 0;
                  if (pMask->isOutsideSelected() == true)
                  {
                     maskIt.getBoundingBox(startColumn, startRow, endColumn, endRow);
                  }
                  else
                  {
                     pMask->getBoundingBox(startColumn, startRow, endColumn, endRow);
                  }
                  LocationType pixel;
                  for (int i = startColumn; i <= endColumn; i++)
                  {
                     for (int j = startRow; j <= endRow; j++)
                     {
                        if ((maskIt.getPixel(i, j) && pMask->isOutsideSelected() == true) ||
                           (pMask->getPixel(i, j) && pMask->isOutsideSelected() == false))
                        {
                           // Add the feature
                           SessionItem* pSessionItem = pGraphicElement;
                           if (pObject != NULL)
                           {
                              pSessionItem = pObject;
                           }

                           Feature* pFeature = new Feature(pSessionItem);
                           features.push_back(pFeature);
                           mFeatures.push_back(pFeature);
                           VERIFYNR(pFeature->attach(SIGNAL_NAME(Subject, Modified),
                              Slot(this, &ShapeFile::shapeModified)));

                           pixel.mX = i + 0.5;
                           pixel.mY = j + 0.5;

                           // Fields
                           pFeature->addField("Name", string());
                           pFeature->addField("Pixel", string());

                           if (!elementName.empty())
                           {
                              if (pObject != NULL)
                              {
                                 pFeature->setFieldValue("Name", elementName + ": " + pObject->getName());
                              }
                              else
                              {
                                 pFeature->setFieldValue("Name", elementName);
                              }
                           }

                           QString pixelName = "(" + QString::number(i + 1) + ", " + QString::number(j + 1) + ")";
                           pFeature->setFieldValue("Pixel", pixelName.toStdString());

                           // Vertex
                           pixel = pGeoref->convertPixelToGeocoord(pixel);
                           pFeature->addVertex(pixel.mY, pixel.mX);    // Longitude as x-coord
                        }
                     }
                  }
               }
            }
         }
      }
      break;

   case ShapefileTypes::POLYLINE_SHAPE:
      {
         if (objects.empty())
         {
            message = "Cannot create a shape file from an empty element.";
            return features;
         }

         // Get names of attributes which should be copied/exported.
         vector<string> attrNames;
         const DynamicObject* pAttributeMetadata = getSourceAttributeMetadata(*pGraphicElement);
         if (pAttributeMetadata != NULL)
         {
            pAttributeMetadata->getAttributeNames(attrNames);
         }

         for (list<GraphicObject*>::const_iterator it = objects.begin(); it != objects.end(); ++it)
         {
            GraphicObject* pCurrentObject = *it;
            if (pCurrentObject != NULL)
            {
               GraphicObjectType objectType = pCurrentObject->getGraphicObjectType();
               if ((objectType == LINE_OBJECT) || (objectType == POLYLINE_OBJECT) ||
                  (objectType == HLINE_OBJECT) || (objectType == VLINE_OBJECT))
               {
                  // Do not allow erase or toggle objects
                  if (pCurrentObject->getDrawMode() != DRAW)
                  {
                     continue;
                  }

                  string objectName = pCurrentObject->getName();

                  vector<LocationType> vertices;
                  pCurrentObject->getRotatedExtents(vertices);
                  if (vertices.empty() == false)
                  {
                     Feature* pFeature = new Feature(pCurrentObject);
                     features.push_back(pFeature);
                     mFeatures.push_back(pFeature);
                     VERIFYNR(pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified)));

                     pFeature->addField("Name", string());
                     if (!elementName.empty())
                     {
                        pFeature->setFieldValue("Name", elementName + ": " + objectName);
                     }

                     // Copy attributes.
                     if ((pMetadata != NULL) && (pAttributeMetadata != NULL))
                     {
                        int idx = getAttributeIndex(*pCurrentObject, *pMetadata);
                        if (idx > -1)
                        {
                           copyMetadata(attrNames, idx, *pAttributeMetadata, *pFeature);
                        }
                     }

                     for (vector<LocationType>::const_iterator iter = vertices.begin(); iter != vertices.end(); ++iter)
                     {
                        LocationType geo = pGeoref->convertPixelToGeocoord(*iter);
                        pFeature->addVertex(geo.mY, geo.mX);
                     }
                  }
               }
            }
         }
      }
      break;

   case ShapefileTypes::POLYGON_SHAPE:
      {
         if (objects.empty())
         {
            message = "Cannot create a shape file from an empty element.";
            return features;
         }

         // Get names of attributes which should be copied/exported.
         vector<string> attrNames;
         const DynamicObject* pAttributeMetadata = getSourceAttributeMetadata(*pGraphicElement);
         if (pAttributeMetadata != NULL)
         {
            pAttributeMetadata->getAttributeNames(attrNames);
         }

         for (list<GraphicObject*>::const_iterator it = objects.begin(); it != objects.end(); ++it)
         {
            GraphicObject* pCurrentObject = *it;
            if (pCurrentObject != NULL)
            {
               GraphicObjectType objectType = pCurrentObject->getGraphicObjectType();
               if ((objectType == RECTANGLE_OBJECT) || (objectType == ROUNDEDRECTANGLE_OBJECT) ||
                  (objectType == ELLIPSE_OBJECT) || (objectType == TRIANGLE_OBJECT) ||
                  (objectType == POLYGON_OBJECT) || (objectType == ARC_OBJECT))
               {
                  // Do not allow erase or toggle objects
                  if (pCurrentObject->getDrawMode() != DRAW)
                  {
                     continue;
                  }

                  string objectName = pCurrentObject->getName();

                  vector<LocationType> vertices;
                  pCurrentObject->getRotatedExtents(vertices);
                  if (vertices.empty() == false)
                  {
                     // To represent non-polygon objects as polygons, add the first vertex as the last vertex
                     if (objectType != POLYGON_OBJECT)
                     {
                        vertices.push_back(vertices.front());
                     }

                     Feature* pFeature = new Feature(pCurrentObject);
                     features.push_back(pFeature);
                     mFeatures.push_back(pFeature);
                     VERIFYNR(pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified)));

                     pFeature->addField("Name", string());
                     if (!elementName.empty())
                     {
                        pFeature->setFieldValue("Name", elementName + ": " + objectName);
                     }

                     // Copy attributes.
                     if ((pMetadata != NULL) && (pAttributeMetadata != NULL))
                     {
                        int idx = getAttributeIndex(*pCurrentObject, *pMetadata);
                        if (idx > -1)
                        {
                           copyMetadata(attrNames, idx, *pAttributeMetadata, *pFeature);
                        }
                     }

                     for (vector<LocationType>::const_iterator iter = vertices.begin(); iter != vertices.end(); ++iter)
                     {
                        LocationType geo = pGeoref->convertPixelToGeocoord(*iter);
                        pFeature->addVertex(geo.mY, geo.mX);
                     }
                  }
               }
            }
         }
      }
      break;

   case ShapefileTypes::MULTIPOINT_SHAPE:
      {
         if (dynamic_cast<AnnotationElement*>(pGraphicElement) != NULL)
         {
            if (objects.empty())
            {
               message = "Cannot create a shape file from an empty element.";
               return features;
            }

            // Get names of attributes which should be copied/exported.
            vector<string> attrNames;
            const DynamicObject* pAttributeMetadata = getSourceAttributeMetadata(*pGraphicElement);
            if (pAttributeMetadata != NULL)
            {
               pAttributeMetadata->getAttributeNames(attrNames);
            }

            for (list<GraphicObject*>::const_iterator it = objects.begin(); it != objects.end(); ++it)
            {
               GraphicObject* pCurrentObject = *it;
               if (pCurrentObject != NULL)
               {
                  // Do not allow erase or toggle objects
                  if (pCurrentObject->getDrawMode() != DRAW)
                  {
                     continue;
                  }

                  Feature* pFeature = new Feature(pGraphicElement);
                  features.push_back(pFeature);
                  mFeatures.push_back(pFeature);
                  VERIFYNR(pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified)));

                  // Fields
                  pFeature->addField("Name", string());
                  if (!elementName.empty())
                  {
                     if (pObject != NULL)
                     {
                        pFeature->setFieldValue("Name", elementName + ": " + pObject->getName());
                     }
                     else
                     {
                        pFeature->setFieldValue("Name", elementName);
                     }
                  }

                  // Copy attributes.
                  if ((pMetadata != NULL) && (pAttributeMetadata != NULL))
                  {
                     int idx = getAttributeIndex(*pCurrentObject, *pMetadata);
                     if (idx > -1)
                     {
                        copyMetadata(attrNames, idx, *pAttributeMetadata, *pFeature);
                     }
                  }

                  vector<LocationType> vertices;
                  pCurrentObject->getRotatedExtents(vertices);
                  for (vector<LocationType>::const_iterator iter = vertices.begin(); iter != vertices.end(); ++iter)
                  {
                     LocationType geo = pGeoref->convertPixelToGeocoord(*iter);
                     pFeature->addVertex(geo.mY, geo.mX);
                  }
               }
            }
         }
         else
         {
            // The BitMaskIterator does not support negative extents and
            // the BitMask does not correctly handle the outside flag so
            // the BitMaskIterator is used for cases when the outside flag is true and
            // the BitMask is used for cases when the outside flag is false
            AoiElement* pAoiElement = dynamic_cast<AoiElement*>(pGraphicElement);
            VERIFYRV(pAoiElement != NULL, features);
            const BitMask* pMask = pAoiElement->getSelectedPoints();
            if (pObject != NULL)
            {
               pMask = pObject->getPixels();
               if (pMask == NULL)
               {
                  message = "The " + pObject->getName() + " object cannot be represented by the " +
                     StringUtilities::toDisplayString(mShape) + " shape type, so a feature will not be added.";
                  return features;
               }
            }

            if (pMask != NULL)
            {
               BitMaskIterator maskIt(pMask, pGeoref);
               if ((maskIt.getCount() > 0 && pMask->isOutsideSelected() == true) ||
                  (pMask->getCount() > 0 && pMask->isOutsideSelected() == false))
               {
                  // Add the feature
                  SessionItem* pSessionItem = pGraphicElement;
                  if (pObject != NULL)
                  {
                     pSessionItem = pObject;
                  }

                  Feature* pFeature = new Feature(pSessionItem);
                  features.push_back(pFeature);
                  mFeatures.push_back(pFeature);
                  VERIFYNR(pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified)));

                  // Fields
                  pFeature->addField("Name", string());
                  if (!elementName.empty())
                  {
                     if (pObject != NULL)
                     {
                        pFeature->setFieldValue("Name", elementName + ": " + pObject->getName());
                     }
                     else
                     {
                        pFeature->setFieldValue("Name", elementName);
                     }
                  }

                  // Vertices
                  int startColumn = 0;
                  int endColumn = 0;
                  int startRow = 0;
                  int endRow = 0;
                  if (pMask->isOutsideSelected() == true)
                  {
                     maskIt.getBoundingBox(startColumn, startRow, endColumn, endRow);
                  }
                  else
                  {
                     pMask->getBoundingBox(startColumn, startRow, endColumn, endRow);
                  }
                  LocationType pixel;
                  for (int i = startColumn; i <= endColumn; i++)
                  {
                     for (int j = startRow; j <= endRow; j++)
                     {
                        if ((maskIt.getPixel(i, j) && pMask->isOutsideSelected() == true) ||
                           (pMask->getPixel(i, j) && pMask->isOutsideSelected() == false))
                        {
                           pixel.mX = i + 0.5;
                           pixel.mY = j + 0.5;
                           pixel = pGeoref->convertPixelToGeocoord(pixel);

                           pFeature->addVertex(pixel.mY, pixel.mX);    // Longitude as x-coord
                        }
                     }
                  }
               }
            }
         }
      }
      break;

   default:
      message = "Shape type not recognized.";
      return features;
   }

   if (features.empty())
   {
      message = elementName + " has no objects that can be represented with the " +
         StringUtilities::toDisplayString(mShape) + " shape type, so no features can be added.";
   }

   return features;
}
bool ConnectedComponents::execute(PlugInArgList* pInArgList, PlugInArgList* pOutArgList)
{
   if (pInArgList == NULL)
   {
      return false;
   }
   mProgress = ProgressTracker(pInArgList->getPlugInArgValue<Progress>(Executable::ProgressArg()),
      "Labeling connected components", "app", "{aa2169d0-9c0a-4d41-9f1d-9a9e83ecf32b}");
   mpView = pInArgList->getPlugInArgValue<SpatialDataView>(Executable::ViewArg());
   AoiElement* pAoi = pInArgList->getPlugInArgValue<AoiElement>("AOI");
   if (pAoi == NULL && mpView != NULL)
   {
      Layer* pLayer = mpView->getActiveLayer();
      if (pLayer == NULL)
      {
         std::vector<Layer*> layers;
         mpView->getLayerList()->getLayers(AOI_LAYER, layers);
         if (!layers.empty())
         {
            pLayer = layers.front();
         }
      }
      pAoi = pLayer == NULL ? NULL : dynamic_cast<AoiElement*>(pLayer->getDataElement());
   }
   const BitMask* mpBitmask = (pAoi == NULL) ? NULL : pAoi->getSelectedPoints();
   if (mpBitmask == NULL)
   {
      mProgress.report("Must specify an AOI.", 0, ERRORS, true);
      return false;
   }
   if (mpBitmask->isOutsideSelected())
   {
      mProgress.report("Infinite AOIs can not be processed.", 0, ERRORS, true);
      return false;
   }

   // Get the extents and create the output element
   int x1 = 0;
   int x2 = 0;
   int y1 = 0;
   int y2 = 0;
   mpBitmask->getMinimalBoundingBox(x1, y1, x2, y2);
   if (x1 > x2)
   {
      std::swap(x1, x2);
   }
   if (y1 > y2)
   {
      std::swap(y1, y2);
   }
   if (x1 < 0 || y1 < 0)
   {
      mProgress.report("Negative pixel locations are not supported and will be ignored.", 1, WARNING, true);
      x1 = std::max(x1, 0);
      y1 = std::max(y1, 0);
      x2 = std::max(x2, 0);
      y2 = std::max(y2, 0);
   }
   // Include a 1 pixel border so we include the edge pixels
   x1--;
   x2++;
   y1--;
   y2++;
   unsigned int width = x2 - x1 + 1;
   unsigned int height = y2 - y1 + 1;

   mXOffset = x1;
   mYOffset = y1;

   mpLabels = static_cast<RasterElement*>(
      Service<ModelServices>()->getElement("Blobs", TypeConverter::toString<RasterElement>(), pAoi));
   if (mpLabels != NULL)
   {
      if (!isBatch())
      {
         Service<DesktopServices>()->showSuppressibleMsgDlg(
            getName(), "The \"Blobs\" element exists and will be deleted.", MESSAGE_INFO,
            "ConnectedComponents::DeleteExisting");
      }
      Service<ModelServices>()->destroyElement(mpLabels);
      mpLabels = NULL;
   }
   mpLabels = RasterUtilities::createRasterElement("Blobs", height, width, INT2UBYTES, true, pAoi);
   if (mpLabels == NULL)
   {
      mProgress.report("Unable to create label element.", 0, ERRORS, true);
      return false;
   }
   ModelResource<RasterElement> pLabels(mpLabels);

   try
   {
      cv::Mat data(height, width, CV_8UC1, cv::Scalar(0));
      for (unsigned int y = 0; y < height; ++y)
      {
         mProgress.report("Reading AOI data", 10 * y / height, NORMAL);
         for (unsigned int x = 0; x < width; ++x)
         {
            if (mpBitmask->getPixel(x + mXOffset, y + mYOffset))
            {
               data.at<unsigned char>(y, x) = 255;
            }
         }
      }
      mProgress.report("Finding contours", 15, NORMAL);
      std::vector<std::vector<cv::Point> > contours;
      std::vector<cv::Vec4i> hierarchy;
      cv::findContours(data, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
      cv::Mat labels(height, width, CV_16UC1, mpLabels->getRawData());
      mProgress.report("Filling blobs", 50, NORMAL);
      unsigned short lastLabel = 0;
      fillContours(labels, contours, hierarchy, lastLabel, 0, 0);

      // create a pseudocolor layer for display
      mProgress.report("Displaying results", 90, NORMAL);
      mpLabels->updateData();
      if (!createPseudocolor(lastLabel))
      {
         mProgress.report("Unable to create blob layer", 0, ERRORS, true);
         return false;
      }

      // add blob count to the metadata
      DynamicObject* pMeta = pLabels->getMetadata();
      VERIFY(pMeta);
      unsigned int numBlobs = static_cast<unsigned int>(lastLabel);
      pMeta->setAttribute("BlobCount", numBlobs);
      if (numBlobs == 0 && !isBatch())
      {
         // Inform the user that there were no blobs so they don't think there was an
         // error running the algorithm. No need to do this in batch since this is
         // represented in the metadata already.
         mProgress.report("No blobs were found.", 95, WARNING);
      }
      // update the output arg list
      if (pOutArgList != NULL)
      {
         pOutArgList->setPlugInArgValue("Blobs", pLabels.get());
         pOutArgList->setPlugInArgValue("Number of Blobs", &numBlobs);
      }
   }
   catch(const cv::Exception& exc)
   {
      mProgress.report(exc.what(), 0, ERRORS, true);
      return false;
   }

   pLabels.release();
   mProgress.report("Labeling connected components", 100, NORMAL);
   mProgress.upALevel();
   return true;
}
Example #3
0
vector<Feature*> ShapeFile::addFeatures(DataElement* pElement, RasterElement* pGeoref, string& message)
{
   vector<Feature*> features;
   message.clear();

   if (pElement == NULL)
   {
      message = "Features cannot be added because the data element is invalid!";
      return features;
   }

   if (pGeoref == NULL || !pGeoref->isGeoreferenced())
   {
      message = "No georeferencing information can be found.";
      return features;
   }

   // Create the features
   string elementName = pElement->getName();

   AoiElement* pAoi = dynamic_cast<AoiElement*>(pElement);
   if (pAoi != NULL)
   {
      if (mShape == POINT_SHAPE)
      {
         // The BitMaskIterator does not support negative extents and
         // the BitMask does not correctly handle the outside flag so
         // the BitMaskIterator is used for cases when the outside flag is true and
         // the BitMask is used for cases when the outside flag is false
         const BitMask* pMask = pAoi->getSelectedPoints();
         if (pMask != NULL)
         {
            BitMaskIterator maskIt(pMask, pGeoref);
            if ((maskIt.getCount() > 0 && pMask->isOutsideSelected() == true) ||
               (pMask->getCount() > 0 && pMask->isOutsideSelected() == false))
            {
               // Add features for each selected pixel
               int startColumn = 0;
               int endColumn = 0;
               int startRow = 0;
               int endRow = 0;
               if (pMask->isOutsideSelected() == true)
               {
                  maskIt.getBoundingBox(startColumn, startRow, endColumn, endRow);
               }
               else
               {
                  pMask->getBoundingBox(startColumn, startRow, endColumn, endRow);
               }
               LocationType pixel;
               for (int i = startColumn; i <= endColumn; i++)
               {
                  for (int j = startRow; j <= endRow; j++)
                  {
                     if ((maskIt.getPixel(i, j) && pMask->isOutsideSelected() == true) ||
                           (pMask->getPixel(i, j) && pMask->isOutsideSelected() == false))
                     {
                        // Add the feature
                        Feature* pFeature = new Feature(mShape);
                        if (pFeature != NULL)
                        {
                           features.push_back(pFeature);
                           mFeatures.push_back(pFeature);
                           pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified));

                           pixel.mX = i + 0.5;
                           pixel.mY = j + 0.5;

                           // Fields
                           pFeature->addField("Name", string());
                           pFeature->addField("Pixel", string());

                           if (!elementName.empty())
                           {
                              pFeature->setFieldValue("Name", elementName);
                           }

                           string pixelName = "";
                           if (!pixelName.empty())
                           {
                              pFeature->setFieldValue("Pixel", pixelName);
                           }

                           // Vertex
                           pixel = pGeoref->convertPixelToGeocoord(pixel);
                           pFeature->addVertex(pixel.mY, pixel.mX);    // Longitude as x-coord
                        }
                     }
                  }
               }
            }
         }
      }
      else if (mShape == POLYLINE_SHAPE)
      {
         GraphicGroup* pGroup = pAoi->getGroup();
         if (pGroup != NULL)
         {
            const list<GraphicObject*>& objects = pGroup->getObjects();
            if (objects.empty())
            {
               message = "Error Shape File 101: Cannot create a shape file from an empty AOI.";
               return features;
            }
            else if (objects.size() != 1)
            {
               message = "Error Shape File 102: Can only create a polyline shape file "
                  "from an AOI which contains a single object.";
               return features;
            }
            const PolylineObject* pObj = dynamic_cast<const PolylineObject*>(objects.front());
            if (pObj == NULL || pObj->getGraphicObjectType() != POLYLINE_OBJECT)
            {
               message = "Error Shape File 103: Can only create a polyline shape file "
                  "from an AOI which contains a single polyline.";
               return features;
            }
            const std::vector<LocationType>& vertices = pObj->getVertices();
            Feature* pFeature = new Feature(mShape);
            if (pFeature != NULL)
            {
               features.push_back(pFeature);
               mFeatures.push_back(pFeature);
               pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified));

               pFeature->addField("Name", string());
               if (!elementName.empty())
               {
                  pFeature->setFieldValue("Name", elementName);
               }

               for (vector<LocationType>::const_iterator iter = vertices.begin(); iter != vertices.end(); ++iter)
               {
                  LocationType geo = pGeoref->convertPixelToGeocoord(*iter);
                  pFeature->addVertex(geo.mY, geo.mX);
               }
            }
         }
      }
      else if (mShape == POLYGON_SHAPE)
      {
         GraphicGroup* pGroup = pAoi->getGroup();
         if (pGroup != NULL)
         {
            const list<GraphicObject*>& objects = pGroup->getObjects();
            if (objects.empty())
            {
               message = "Error Shape File 101: Cannot create a shape file from an empty AOI.";
               return features;
            }
            else if (objects.size() != 1)
            {
               message = "Error Shape File 102: Can only create a polygon shape file "
                  "from an AOI which contains a single object.";
               return features;
            }
            const PolygonObject* pObj = dynamic_cast<const PolygonObject*>(objects.front());
            if (pObj == NULL || pObj->getGraphicObjectType() != POLYGON_OBJECT)
            {
               message = "Error Shape File 103: Can only create a polygon shape file "
                  "from an AOI which contains a single polygon.";
               return features;
            }
            const std::vector<LocationType>& vertices = pObj->getVertices();
            Feature* pFeature = new Feature(mShape);
            if (pFeature != NULL)
            {
               features.push_back(pFeature);
               mFeatures.push_back(pFeature);
               pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified));

               pFeature->addField("Name", string());
               if (!elementName.empty())
               {
                  pFeature->setFieldValue("Name", elementName);
               }

               for (vector<LocationType>::const_iterator iter = vertices.begin(); iter != vertices.end(); ++iter)
               {
                  LocationType geo = pGeoref->convertPixelToGeocoord(*iter);
                  pFeature->addVertex(geo.mY, geo.mX);
               }
            }
         }
      }
      else if (mShape == MULTIPOINT_SHAPE)
      {
         // The BitMaskIterator does not support negative extents and
         // the BitMask does not correctly handle the outside flag so
         // the BitMaskIterator is used for cases when the outside flag is true and
         // the BitMask is used for cases when the outside flag is false
         const BitMask* pMask = pAoi->getSelectedPoints();
         if (pMask != NULL)
         {
            BitMaskIterator maskIt(pMask, pGeoref);
            if ((maskIt.getCount() > 0 && pMask->isOutsideSelected() == true) ||
               (pMask->getCount() > 0 && pMask->isOutsideSelected() == false))
            {
               // Add the feature
               Feature* pFeature = new Feature(mShape);
               if (pFeature != NULL)
               {
                  features.push_back(pFeature);
                  mFeatures.push_back(pFeature);
                  pFeature->attach(SIGNAL_NAME(Subject, Modified), Slot(this, &ShapeFile::shapeModified));

                  // Fields
                  pFeature->addField("Name", string());
                  if (!elementName.empty())
                  {
                     pFeature->setFieldValue("Name", elementName);
                  }

                  // Vertices
                  int startColumn = 0;
                  int endColumn = 0;
                  int startRow = 0;
                  int endRow = 0;
                  if (pMask->isOutsideSelected() == true)
                  {
                     maskIt.getBoundingBox(startColumn, startRow, endColumn, endRow);
                  }
                  else
                  {
                     pMask->getBoundingBox(startColumn, startRow, endColumn, endRow);
                  }
                  LocationType pixel;
                  for (int i = startColumn; i <= endColumn; i++)
                  {
                     for (int j = startRow; j <= endRow; j++)
                     {
                        if ((maskIt.getPixel(i, j) && pMask->isOutsideSelected() == true) ||
                           (pMask->getPixel(i, j) && pMask->isOutsideSelected() == false))
                        {
                           pixel.mX = i + 0.5;
                           pixel.mY = j + 0.5;
                           pixel = pGeoref->convertPixelToGeocoord(pixel);

                           pFeature->addVertex(pixel.mY, pixel.mX);    // Longitude as x-coord
                        }
                     }
                  }
               }
            }
         }
      }
      else
      {
         message = "Shape type not recognized.";
         return features;
      }
   }

   if (features.empty())
   {
      message = elementName + " has no selected points, so no features can be added.";
   }

   return features;
}