SimplifyShapefile.cpp
// Copyright 2010 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; }