Simplify feature geometry for a shapefile
SimplifyShapefile.cpp
// Copyright 2011 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

  
#include "SimplifyShapefile.h"

// Description: 
//   This utility creates a new simplified Shapefile based on the input.
//   A simplified shapefile's geometry is topologically consistent.
//   For example, a simplified polygon shapefile has no overlapping rings, 
//     all exterior and interior rings have the proper orientation, and all 
//     non-closed polygons have been closed.
//   All indexes on the input Shapefile are recreated on the output Shapefile
//
//   How to use:
//     SimplifyShapefile "Input Shapefile" "Output Shapefile"
//   Example:  
//     Windows:
//     SimplifyShapefile.exe "c:\data\roads.shp" "c:\data\road3.shp" 
//     Unix:
//     ./SimplifyShapefile /mycomp/data/roads /mycomp/data/roadsimplify

int main(int argc, char **argv)
{
  cerr << "ArcObjects C++ SDK Developer Sample - SimplifyShapefile" 
       << endl;

  if (argc != 3)
  {
    cerr << "Usage: SimplifyShapefile [input shapefile] [output shapefile]"
         << endl;
    AoExit(0);
  }
  
  char* input = argv[1];
  char* output = argv[2];

  if (!InitializeApp())
  {
    AoExit(0);
  }

  // Process arguments
  CComBSTR dataPath;
  CComBSTR dataName;
  CComBSTR outPath;
  CComBSTR outName;
  HRESULT hr = GetParentDirFromFullPath(input, &dataPath);
  if (FAILED(hr) || dataPath.Length() <= 0)
  {
    std::cerr << "Cannot get input path." << std::endl;
    ShutdownApp();
    AoExit(0);
  }
  hr = GetFileFromFullPath(input, &dataName);
  if (FAILED(hr) || dataName.Length() <= 0)
  {
    std::cerr << "Cannot get input file name." << std::endl;
    ShutdownApp();
    AoExit(0);
  }
  hr = GetParentDirFromFullPath(output, &outPath);
  if (FAILED(hr) || outPath.Length() <= 0)
  {
    std::cerr << "Cannot get output path." << std::endl;
    ShutdownApp();
    AoExit(0);
  }
  hr = GetFileFromFullPath(output, &outName);
  if (FAILED(hr) || outName.Length() <= 0)
  {
    std::cerr << "Cannot get output file name." << std::endl;
    ShutdownApp();
    AoExit(0);
  }

  Simplify(dataPath, dataName, outPath, outName);
  cerr << "Done!" << endl;;
  
  ShutdownApp();
  AoExit(0);
}

HRESULT Simplify(BSTR dataPath, BSTR dataName, BSTR outPath, BSTR outName)
{
  // Open the feature class from the input shapefile
  IWorkspaceFactoryPtr ipShapeWkspFac(CLSID_ShapefileWorkspaceFactory);
  IWorkspacePtr ipWksp;
  HRESULT hr = ipShapeWkspFac->OpenFromFile(dataPath, 0, &ipWksp);
  if (FAILED(hr) || ipWksp == 0)
  {
    wcerr << L"Cannot open workspace " << (BSTR) dataPath << endl;
    return E_FAIL;
  }
  IFeatureClassPtr ipDataFC;
  hr = ((IFeatureWorkspacePtr) ipWksp)->OpenFeatureClass(dataName, &ipDataFC);
  if (FAILED(hr) || ipDataFC == 0)
  {
    wcerr << L"Cannot open feature class " << (BSTR) dataName << endl;
    return E_FAIL;
  }

  // Check that the feature class has shapes. If not, exit.
  long totalFeatureCount;
  ipDataFC->FeatureCount(0, &totalFeatureCount);
  if (totalFeatureCount == 0)
  {
    cerr << "No features found in shapefile. Exiting." << endl;
    return E_FAIL;
  }

  // Create the output shapefile
  IFeatureClassPtr ipOutFC;
  CreateOutShapefile(ipDataFC, outPath, outName, &ipOutFC);
  if (ipOutFC == 0)
  {
    cerr << "Couldn't create new shapefile." << endl;
    return E_FAIL;
  }

  // Do the actual processing.
  printf("Simplifying shapefile......\n");
  
  // Create an insert cursor
  IFeatureCursorPtr ipInsertFeatureCursor;
  ipOutFC->Insert(VARIANT_TRUE, &ipInsertFeatureCursor);
  IFeatureBufferPtr ipInsertFeatureBuffer;
  ipOutFC->CreateFeatureBuffer(&ipInsertFeatureBuffer);
  
  // Loop through all the features in the feature class, correct each
  //   one, and write it out to the new shapefile.
  IFeatureCursorPtr ipFeatureCursor;
  ipDataFC->Search(0, VARIANT_TRUE, &ipFeatureCursor);

  IFeaturePtr ipFeature;
  ipFeatureCursor->NextFeature(&ipFeature);
  
  int featureCount = 0;
  int emptyFeatureCount = 0;
  while(ipFeature != 0)
  {
    // Update progress information
    featureCount += 1;
    cerr << featureCount << " of " << totalFeatureCount 
         << " Features processed." << endl;
      
    // If the feature has an invalid shape, create a new empty one
    IGeometryPtr ipGeom;
    ipFeature->get_Shape(&ipGeom);
    if (ipGeom == 0)
    {
      CreateNewGeom(ipOutFC, ipGeom);
      ipFeature->putref_Shape(ipGeom);
    }
      
    // Simplify each feature and insert into new feature class
    ((ITopologicalOperator2Ptr) ipGeom)->put_IsKnownSimple(VARIANT_FALSE);
    ((ITopologicalOperator2Ptr) ipGeom)->Simplify();
    InsertFeature(ipInsertFeatureCursor, ipInsertFeatureBuffer,
                  ipFeature, (IGeometry2Ptr) ipGeom);
            
    // Count the number of empty features
    VARIANT_BOOL vbIsEmpty;
    ((IGeometry2Ptr) ipGeom)->get_IsEmpty(&vbIsEmpty);
    if (vbIsEmpty)
      emptyFeatureCount += 1;
      
    // Get the next feature
    ipFeatureCursor->NextFeature(&ipFeature);
  }

  ipInsertFeatureBuffer = 0;
  ipInsertFeatureCursor = 0;

  cerr << endl << "Operation completed successfully." << endl;
  return S_OK;
}

// Create a new shapefile for the output
HRESULT CreateOutShapefile(IFeatureClass* pDataFC, 
                           BSTR outPath, BSTR outName,
                           IFeatureClass** ppOutFC)
{
  IWorkspaceFactoryPtr ipOutWkspFac(CLSID_ShapefileWorkspaceFactory);
  IWorkspacePtr ipOutWksp;
  HRESULT hr = ipOutWkspFac->OpenFromFile(outPath, 0, &ipOutWksp);
  if (FAILED(hr) || ipOutWksp == 0)
  {
    wcerr << L"Couldn't open output workspace " << (BSTR) outPath << endl;
    return E_FAIL;
  }

  // Copy the fields
  IFieldsPtr ipFields;
  pDataFC->get_Fields(&ipFields); 
  IClonePtr ipClone(ipFields);
  IClonePtr ipCloned;
  ipClone->Clone(&ipCloned);

  CComBSTR bstr_name;
  pDataFC->get_ShapeFieldName(&bstr_name);

  ((IFeatureWorkspacePtr) ipOutWksp)->CreateFeatureClass(outName, 
                                                         (IFieldsPtr) ipCloned, 
                                                         0, 0,
                                                         esriFTSimple,
                                                         bstr_name, 
                                                         CComBSTR(L""),
                                                         ppOutFC);

  return S_OK;
}

HRESULT CreateNewGeom(IFeatureClass* pOutFC, IGeometry* pNewGeom)
{
  esriGeometryType esriGeom;
  pOutFC->get_ShapeType(&esriGeom);
  
  switch(esriGeom)
  {
  case esriGeometryPoint:
  {  
    IPointPtr ipPoint(CLSID_Point);
    pNewGeom = ipPoint;
  } 
  break;
  case esriGeometryMultipoint:
  {
    IMultipointPtr ipMultipoint(CLSID_Multipoint);
    pNewGeom = ipMultipoint;
  }
  break;
  case esriGeometryPolyline:
  {
    IPolylinePtr ipPolyline(CLSID_Polyline);
    pNewGeom = ipPolyline;
  }
  break;
  case esriGeometryPolygon:
  {
    IPolygonPtr ipPolygon(CLSID_Polygon);
    pNewGeom = ipPolygon;
  }
  break;
  default:
    cerr << "shape type not supported" << endl;
    return E_FAIL;
    break;
  }
  return S_OK;
}

HRESULT InsertFeature(IFeatureCursor* pInFeatCur, 
                      IFeatureBuffer* pInFeatBuf,
                      IFeature* pOrigFeat, IGeometry* pGeom)
{
  // Copy attributes of original feature to new feature
  IFieldsPtr ipFields;
  pOrigFeat->get_Fields(&ipFields);
  
  long fieldCount;
  ipFields->get_FieldCount(&fieldCount);
  
  IFieldPtr ipField;
  
  // Skip OID and geometry fields
  for (long i = 0; i <=  fieldCount - 1; i++)
  {
    ipFields->get_Field(i, &ipField);
      
    esriFieldType esriField;
    ipField->get_Type(&esriField);
      
    VARIANT_BOOL varboolEdit;
    ipField->get_Editable(&varboolEdit);
      
    // Skip OID and geometry fields
    if (!(esriField == esriFieldTypeGeometry) &&
        !(esriField == esriFieldTypeOID) && 
        (varboolEdit))
    {
      CComVariant origValue;  
      pOrigFeat->get_Value(i, &origValue);
      pInFeatBuf->put_Value(i, origValue);
    }
  }
  
  pInFeatBuf->putref_Shape(pGeom);
  
  CComVariant varID;
  pInFeatCur->InsertFeature(pInFeatBuf, &varID);
  
  return S_OK;
}