Assigning a Spatial Reference Using IGeoDatasetSchemaEdit
/// <summary>
/// Assigns or overwrites a spatial reference for a dataset that has an unknown coordinate system.
/// </summary>
/// <param name="dataset">The input geodataset.</param>
/// <param name="targetSpatialRef">The spatial reference to apply.</param>
/// <param name="success">Indicates whether the operation succeeded.</param>
public void ApplySpatialReference(IGeoDataset dataset, ISpatialReference targetSpatialRef, out bool success)
{
success = false;
IGeoDatasetSchemaEdit schemaEditor = dataset as IGeoDatasetSchemaEdit;
if (schemaEditor != null && schemaEditor.CanAlterSpatialReference)
{
schemaEditor.AlterSpatialReference(targetSpatialRef);
success = true;
}
}
This method mirrors the Define Projection tool found in ArcMap's toolbox. It solely attaches or replaces the coordinate system metadata—it does not tarnsform coordinates.


Reference from ArcObjects documentation:

Converting Feature Classes with IFeatureDataConverter
/// <summary>
/// Converts a feature class from its existing projection into a new one.
/// </summary>
/// <param name="sourceClass">The input feature class.</param>
/// <param name="filter">Optional query filter to select a subset of features.</param>
/// <param name="targetSpatialRef">The target spatial reference.</param>
/// <param name="outputDirectory">The output workspace path.</param>
/// <param name="outputName">The name of the resulting feature class.</param>
/// <param name="outputDatasetName">When writing to a geodatabase, the target feature dataset name (optional).</param>
public void ReprojectWithDataConverter(IFeatureClass sourceClass, IQueryFilter filter,
ISpatialReference targetSpatialRef, string outputDirectory, string outputName,
string outputDatasetName = null)
{
var sourceGeoDataset = (IGeoDataset)sourceClass;
var originalSpatialRef = sourceGeoDataset.SpatialReference;
IClone cloneChecker = originalSpatialRef as IClone;
if (cloneChecker.IsEqual((IClone)targetSpatialRef))
return;
Type workspaceFactoryType = null;
string extension = System.IO.Path.GetExtension(outputDirectory);
if (extension == ".gdb")
{
workspaceFactoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory");
}
else if (extension == "")
{
workspaceFactoryType = Type.GetTypeFromProgID("esriDataSourcesFile.ShapefileWorkspaceFactory");
outputDatasetName = null;
}
else
return;
var workspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(workspaceFactoryType);
var outputWorkspace = workspaceFactory.OpenFromFile(outputDirectory, 0);
// Delete any existing feature class with the same name
IWorkspace2 workspace2 = (IWorkspace2)outputWorkspace;
IFeatureWorkspace featureWorkspace = (IFeatureWorkspace)workspace2;
if (workspace2.get_NameExists(esriDatasetType.esriDTFeatureClass, outputName))
{
var existingDataset = (IDataset)featureWorkspace.OpenFeatureClass(outputName);
existingDataset.Delete();
}
IFeatureDataset targetDataset = null;
IFeatureDatasetName targetDatasetNameObj = null;
if (!string.IsNullOrWhiteSpace(outputDatasetName))
{
if (workspace2.get_NameExists(esriDatasetType.esriDTFeatureDataset, outputDatasetName))
targetDataset = featureWorkspace.OpenFeatureDataset(outputDatasetName);
else
targetDataset = featureWorkspace.CreateFeatureDataset(outputDatasetName, originalSpatialRef);
var tempGeoDataset = (IGeoDataset)targetDataset;
var tempClone = tempGeoDataset.SpatialReference as IClone;
if (!tempClone.IsEqual((IClone)targetSpatialRef))
return;
targetDatasetNameObj = (IFeatureDatasetName)targetDataset.FullName;
}
IDataset outputDataset = (IDataset)outputWorkspace;
IWorkspaceName outputWorkspaceName = (IWorkspaceName)outputDataset.FullName;
IFeatureClassName outputClassName = new FeatureClassNameClass();
IDatasetName outDatasetName = outputClassName as IDatasetName;
outDatasetName.WorkspaceName = outputWorkspaceName;
outDatasetName.Name = outputName;
IDataset inputDataset = sourceClass as IDataset;
IFeatureClassName inputClassName = inputDataset.FullName as IFeatureClassName;
IWorkspace inputWorkspace = inputDataset.Workspace;
IFieldChecker fieldValidator = new FieldCheckerClass();
fieldValidator.InputWorkspace = inputWorkspace;
fieldValidator.ValidateWorkspace = outputWorkspace;
IFields inputFields = sourceClass.Fields;
IEnumFieldError fieldErrors;
IFields validatedFields;
fieldValidator.Validate(inputFields, out fieldErrors, out validatedFields);
IGeometryDef geometryDef = new GeometryDefClass();
IGeometryDefEdit geomDefEditor = (IGeometryDefEdit)geometryDef;
geomDefEditor.GeometryType_2 = sourceClass.ShapeType;
geomDefEditor.SpatialReference_2 = targetSpatialRef;
IFeatureDataConverter converter = new FeatureDataConverterClass();
converter.ConvertFeatureClass(
inputClassName,
filter,
targetDatasetNameObj,
outputClassName,
geometryDef,
validatedFields,
"",
1000,
0);
}
This method mimics the Project tool in the ArcMap toolbox, but it does not allow a custom geographic (datum) transformation to be specified.


Manual Projection with Feature-by-Feature Copy and IGeometry5
/// <summary>
/// Re-projects a feature class by creating a new one and transforming each geometry individually.
/// </summary>
/// <param name="sourceClass">The original feature class.</param>
/// <param name="targetSpatialRef">The target spatial reference.</param>
/// <param name="outputDirectory">Path to the destination workspace.</param>
/// <param name="outputName">Name of the output feature class.</param>
/// <param name="outputDatasetName">Optional feature dataset name for geodatabase output.</param>
/// <param name="geoTransformationType">An esriSRGeoTransformationType, esriSRGeoTransformation2Type, or esriSRGeoTransformation3Type value.</param>
/// <returns>The newly created feature class.</returns>
public IFeatureClass ReprojectManually(
IFeatureClass sourceClass,
ISpatialReference targetSpatialRef,
string outputDirectory,
string outputName,
string outputDatasetName = null,
object geoTransformationType = null)
{
var sourceGeoDataset = (IGeoDataset)sourceClass;
var originalSpatialRef = sourceGeoDataset.SpatialReference;
IClone cloneChecker = originalSpatialRef as IClone;
if (cloneChecker.IsEqual((IClone)targetSpatialRef))
return null;
int geoTransCode = int.MinValue;
if (geoTransformationType is esriSRGeoTransformationType)
geoTransCode = (int)geoTransformationType;
else if (geoTransformationType is esriSRGeoTransformation2Type)
geoTransCode = (int)geoTransformationType;
else if (geoTransformationType is esriSRGeoTransformation3Type)
geoTransCode = (int)geoTransformationType;
IGeoTransformation geoTransformation = null;
if (geoTransCode != int.MinValue)
{
ISpatialReferenceFactory2 factory =
new SpatialReferenceEnvironmentClass();
geoTransformation =
(IGeoTransformation)factory.CreateGeoTransformation(geoTransCode);
}
Type workspaceFactoryType = null;
string extension = System.IO.Path.GetExtension(outputDirectory);
if (extension == ".gdb")
{
workspaceFactoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory");
}
else if (extension == "")
{
workspaceFactoryType = Type.GetTypeFromProgID("esriDataSourcesFile.ShapefileWorkspaceFactory");
outputDatasetName = null;
}
else
return null;
var workspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(workspaceFactoryType);
var targetWorkspace = workspaceFactory.OpenFromFile(outputDirectory, 0);
var featureWorkspace = (IFeatureWorkspace)targetWorkspace;
IWorkspace2 workspace2 = (IWorkspace2)targetWorkspace;
if (workspace2.get_NameExists(esriDatasetType.esriDTFeatureClass, outputName))
{
var existingClass = featureWorkspace.OpenFeatureClass(outputName);
((IDataset)existingClass).Delete();
}
int shapeIndex = sourceClass.FindField(sourceClass.ShapeFieldName);
IFields outputFields = new FieldsClass();
IFieldsEdit outputFieldsEditor = (IFieldsEdit)outputFields;
for (int i = 0; i < sourceClass.Fields.FieldCount; i++)
{
if (i == shapeIndex)
{
IGeometryDef geomDef = new GeometryDefClass();
IGeometryDefEdit geomDefEditor = (IGeometryDefEdit)geomDef;
geomDefEditor.GeometryType_2 = sourceClass.ShapeType;
geomDefEditor.SpatialReference_2 = targetSpatialRef;
IField shapeField = new FieldClass();
IFieldEdit shapeFieldEditor = (IFieldEdit)shapeField;
shapeFieldEditor.Name_2 = "SHAPE";
shapeFieldEditor.AliasName_2 = "SHAPE";
shapeFieldEditor.Type_2 = esriFieldType.esriFieldTypeGeometry;
shapeFieldEditor.GeometryDef_2 = geomDef;
outputFieldsEditor.AddField(shapeField);
}
else
{
outputFieldsEditor.AddField(sourceClass.Fields.Field[i]);
}
}
IFeatureClass newFeatureClass = null;
if (string.IsNullOrWhiteSpace(outputDatasetName))
{
newFeatureClass = featureWorkspace.CreateFeatureClass(
outputName, outputFields, sourceClass.CLSID, sourceClass.EXTCLSID,
esriFeatureType.esriFTSimple, "Shape", "");
}
else
{
IFeatureDataset featDataset = null;
if (workspace2.get_NameExists(esriDatasetType.esriDTFeatureDataset, outputDatasetName))
featDataset = featureWorkspace.OpenFeatureDataset(outputDatasetName);
else
featDataset = featureWorkspace.CreateFeatureDataset(outputDatasetName, originalSpatialRef);
newFeatureClass = featDataset.CreateFeatureClass(
outputName, outputFields, sourceClass.CLSID, sourceClass.EXTCLSID,
esriFeatureType.esriFTSimple, "Shape", "");
System.Runtime.InteropServices.Marshal.ReleaseComObject(featDataset);
}
var fieldMapping = new Dictionary<int, int>();
for (int i = 0; i < sourceClass.Fields.FieldCount; i++)
{
if (!sourceClass.Fields.Field[i].Editable)
continue;
string fieldName = sourceClass.Fields.Field[i].Name.ToUpper();
for (int j = 0; j < newFeatureClass.Fields.FieldCount; j++)
{
string destFieldName = newFeatureClass.Fields.Field[j].Name.ToUpper();
if (fieldName == destFieldName)
fieldMapping.Add(i, j);
}
}
var searchCursor = sourceClass.Search(null, false);
var insertCursor = newFeatureClass.Insert(true);
IFeatureBuffer insertBuffer = newFeatureClass.CreateFeatureBuffer();
IFeature currentFeature = searchCursor.NextFeature();
int counter = 0;
while (currentFeature != null)
{
foreach (var mapping in fieldMapping)
{
if (mapping.Key == shapeIndex)
{
IGeometry5 geom = currentFeature.ShapeCopy as IGeometry5;
if (geoTransformation == null)
geom.Project(targetSpatialRef);
else
geom.ProjectEx(targetSpatialRef,
esriTransformDirection.esriTransformForward,
geoTransformation, false, 0, 0);
insertBuffer.Shape = geom;
}
else
{
insertBuffer.set_Value(mapping.Value,
currentFeature.get_Value(mapping.Key));
}
}
insertCursor.InsertFeature(insertBuffer);
currentFeature = searchCursor.NextFeature();
if (counter++ % 1000 == 0)
insertCursor.Flush();
}
insertCursor.Flush();
System.Runtime.InteropServices.Marshal.ReleaseComObject(originalSpatialRef);
System.Runtime.InteropServices.Marshal.ReleaseComObject(targetWorkspace);
System.Runtime.InteropServices.Marshal.ReleaseComObject(searchCursor);
System.Runtime.InteropServices.Marshal.ReleaseComObject(insertCursor);
System.Runtime.InteropServices.Marshal.ReleaseComObject(insertBuffer);
return newFeatureClass;
}
This final approach replicates the full Project tool behaviour from the ArcMap toolbox, including an optional geographic (datum) transformation paarmeter.

