Creating a shapefile feature class with Store method yields identical results to exporting feature classes, but performs significantly slower due to storage operations.
Method 2: IFeatureBuffer Approach
Using IFeatureBuffer provides faster performance but carries memory corruption risks with large datasets that can crash appliactions.
private static void ExportToShapefile(string outputPath, IFeatureLayer sourceLayer)
{
string directory = Path.GetDirectoryName(outputPath);
string fileName = Path.GetFileName(outputPath);
IWorkspaceFactory workspaceFactory = new ShapefileWorkspaceFactoryClass();
IFeatureWorkspace featureWorkspace = workspaceFactory.OpenFromFile(directory, 0) as IFeatureWorkspace;
IFeatureClassDescription classDesc = new FeatureClassDescriptionClass();
IObjectClassDescription objDesc = (IObjectClassDescription)classDesc;
IFields baseFields = objDesc.RequiredFields;
int geometryFieldIndex = baseFields.FindField(classDesc.ShapeFieldName);
IField geometryField = baseFields.get_Field(geometryFieldIndex);
IGeometryDef geometryDefinition = geometryField.GeometryDef;
IGeometryDefEdit geometryEditor = (IGeometryDefEdit)geometryDefinition;
switch(sourceLayer.FeatureClass.ShapeType)
{
case esriGeometryType.esriGeometryPolygon:
geometryEditor.GeometryType_2 = esriGeometryType.esriGeometryPolygon;
break;
case esriGeometryType.esriGeometryPolyline:
geometryEditor.GeometryType_2 = esriGeometryType.esriGeometryPolyline;
break;
case esriGeometryType.esriGeometryPoint:
geometryEditor.GeometryType_2 = esriGeometryType.esriGeometryPoint;
break;
}
IProjectedCoordinateSystem spatialRef = (sourceLayer as IGeoDataset).SpatialReference as IProjectedCoordinateSystem;
geometryEditor.SpatialReference_2 = spatialRef;
IFieldChecker fieldValidator = new FieldCheckerClass();
IEnumFieldError fieldErrors = null;
IFields validatedFields = null;
fieldValidator.ValidateWorkspace = (IWorkspace)featureWorkspace;
fieldValidator.Validate(baseFields, out fieldErrors, out validatedFields);
IFeatureClass outputFeatureClass = featureWorkspace.CreateFeatureClass(fileName, validatedFields,
objDesc.InstanceCLSID, objDesc.ClassExtensionCLSID, esriFeatureType.esriFTSimple,
classDesc.ShapeFieldName, "");
for(int i = 0; i < sourceLayer.FeatureClass.Fields.FieldCount; i++)
{
IField currentField = sourceLayer.FeatureClass.Fields.get_Field(i);
if(currentField.Type == esriFieldType.esriFieldTypeBlob) continue;
outputFeatureClass.AddField(currentField);
}
IFeatureLayer outputLayer = new FeatureLayerClass();
outputLayer.FeatureClass = outputFeatureClass;
ITable sourceTable = sourceLayer.FeatureClass as ITable;
IQueryFilter queryFilter = new QueryFilterClass();
queryFilter.WhereClause = "";
queryFilter.AddField(sourceLayer.FeatureClass.OIDFieldName);
int featureCount = sourceTable.RowCount(queryFilter);
TransferFeaturesWithBuffer(sourceLayer, outputLayer, featureCount);
}
private static void TransferFeaturesWithBuffer(IFeatureLayer sourceLayer, IFeatureLayer targetLayer, int totalFeatures)
{
try
{
int processedCount = 0;
IFeatureClass targetClass = targetLayer.FeatureClass;
IFeatureBuffer featureBuffer = targetClass.CreateFeatureBuffer();
IFeatureCursor insertCursor = targetClass.Insert(true);
IFeatureCursor sourceCursor = sourceLayer.FeatureClass.Search(null, false);
IFeature sourceFeature = sourceCursor.NextFeature();
while(sourceFeature != null)
{
processedCount++;
if(sourceFeature.ShapeCopy == null)
{
sourceFeature = sourceCursor.NextFeature();
continue;
}
IFeature targetFeature = featureBuffer as IFeature;
IGeometry processedGeometry = GeometryProcessor.SimplifyGeometry(sourceFeature.ShapeCopy);
targetFeature.Shape = processedGeometry;
IFields sourceFields = FieldManager.CopyFields(sourceFeature.Fields);
IFields targetFields = FieldManager.CopyFields(targetFeature.Fields);
Dictionary<int int=""> fieldMapping = FieldManager.CreateFieldMapping(sourceFields, targetFields);
FieldManager.CopyAttributes(sourceFeature, targetFeature, fieldMapping);
insertCursor.InsertFeature(featureBuffer);
if(processedCount % 500 == 0)
{
insertCursor.Flush();
ReleaseComObjects(targetFeature, sourceFeature, featureBuffer, insertCursor);
GC.Collect();
GC.WaitForPendingFinalizers();
featureBuffer = targetClass.CreateFeatureBuffer();
insertCursor = targetClass.Insert(true);
targetFeature = featureBuffer as IFeature;
}
sourceFeature = sourceCursor.NextFeature();
}
insertCursor.Flush();
ReleaseComObjects(insertCursor, featureBuffer, sourceCursor);
GC.Collect();
GC.WaitForPendingFinalizers();
}
catch(Exception ex)
{
throw ex;
}
}
</int>
Method 3: IFeatureDataConverter Approach
IFeatureDataConverter2 or IFeatureDataConverter offers the fastest performance but lacks progress tracking capabilities. Large field types require special handling.
public static void ConvertToShapefile(IFeatureLayer inputLayer, string outputName, string outputPath)
{
try
{
IFeatureClass sourceClass = inputLayer.FeatureClass;
if(sourceClass == null || string.IsNullOrEmpty(outputName)) return;
IWorkspaceFactory workspaceFactory = new ShapefileWorkspaceFactoryClass();
IWorkspace outputWorkspace = workspaceFactory.OpenFromFile(outputPath, 0);
IFeatureClassName outputClassName = new FeatureClassNameClass();
IDatasetName outputDatasetName = (IDatasetName)outputClassName;
outputDatasetName.Name = outputName;
IDataset workspaceDataset = outputWorkspace as IDataset;
outputDatasetName.WorkspaceName = workspaceDataset.FullName as IWorkspaceName;
IFieldChecker fieldChecker = new FieldCheckerClass();
fieldChecker.InputWorkspace = sourceClass.FeatureDataset.Workspace;
fieldChecker.ValidateWorkspace = outputWorkspace;
IFields inputFields = sourceClass.Fields;
IFields outputFields;
IEnumFieldError fieldErrors = null;
fieldChecker.Validate(inputFields, out fieldErrors, out outputFields);
List<int> invalidFields = new List<int>();
IFieldError fieldError = fieldErrors.Next();
while(fieldError != null)
{
invalidFields.Add(fieldError.FieldIndex);
fieldError = fieldErrors.Next();
}
IQueryFilter filter = new QueryFilterClass { WhereClause = "" };
StringBuilder fieldList = new StringBuilder();
IFields filteredFields = new FieldsClass();
IFieldsEdit fieldsEditor = (IFieldsEdit)filteredFields;
for(int i = 0; i < outputFields.FieldCount; i++)
{
IField currentField = outputFields.get_Field(i);
if(currentField.Type == esriFieldType.esriFieldTypeBlob ||
currentField.Type == esriFieldType.esriFieldTypeRaster ||
currentField.Type == esriFieldType.esriFieldTypeGUID) continue;
if(invalidFields.Contains(i)) continue;
fieldsEditor.AddField(currentField);
filter.AddField(currentField.Name);
fieldList.Append(currentField.Name + ",");
}
string subFields = fieldList.ToString().TrimEnd(',');
filter.SubFields = subFields;
IDataset sourceDataset = sourceClass as IDataset;
IFeatureClassName sourceClassName = (IFeatureClassName)sourceDataset.FullName;
IDatasetName sourceDatasetName = (IDatasetName)sourceClassName;
IFeatureDataConverter2 converter = new FeatureDataConverterClass();
converter.ConvertFeatureClass(sourceDatasetName, filter, null, null, outputClassName,
null, filteredFields, "", 1000, 0);
}
catch(Exception ex)
{
throw ex;
}
}
</int></int>
Method 4: Geoprocesisng Tool Approach
Using the FeatureClassToShapefile geoprocessing tool provides a straightforward solution with built-in error handling.
private void ExecuteShapefileConversion(string databaseName, string connectionFile, string outputFolder)
{
string sdeConnection = @"E:\connections\ConnectionTo" + databaseName + ".sde";
IWorkspace sdeWorkspace = DatabaseManager.OpenWorkspace(databaseName, sdeConnection);
IFeatureWorkspace featureWorkspace = sdeWorkspace as IFeatureWorkspace;
IFeatureClass featureClass = featureWorkspace.OpenFeatureClass("TEST.dbo.FEATURES");
Geoprocessor processor = new Geoprocessor();
processor.OverwriteOutput = true;
ESRI.ArcGIS.ConversionTools.FeatureClassToShapefile conversionTool =
new ESRI.ArcGIS.ConversionTools.FeatureClassToShapefile();
conversionTool.Input_Features = featureClass;
conversionTool.Output_Folder = outputFolder;
try
{
processor.Execute(conversionTool, null);
}
catch(Exception ex)
{
StringBuilder messages = new StringBuilder();
for(int i = 0; i < processor.MessageCount; i++)
{
messages.Append(processor.GetMessage(i) + "\n");
}
}
}