Using ArcObjects to choose GeoTransformation?
See c# code below. (Updated: refactored)
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ESRI.ArcGIS.Geometry;
using System.Windows.Forms;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Framework;
namespace HansenAddin
{
public class PickGeoTransButton : ESRI.ArcGIS.Desktop.AddIns.Button
{
public PickGeoTransButton()
{
}
protected override void OnClick()
{
try
{
// let user choose a transformation for projecting from featureclass's spatial ref
// into the dataframe's spatial ref.
var featClass = ((IFeatureLayer)ArcMap.Document.FocusMap.get_Layer(0)).FeatureClass;
var fromSR = ((IGeoDataset)featClass).SpatialReference;
var toSR = ArcMap.Document.FocusMap.SpatialReference;
IGeoTransformation geoTrans;
esriTransformDirection direction;
ChooseGeotrans(fromSR, toSR, ArcMap.Application.hWnd, out geoTrans, out direction);
if (geoTrans != null)
{
MessageBox.Show(String.Format("{0} \n{1} \n{2} \n{3}", geoTrans.Name, fromSR.Name, toSR.Name, direction));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public static void ChooseGeotrans(ISpatialReference fromSR, ISpatialReference toSR, int hWnd,
out IGeoTransformation geoTrans, out esriTransformDirection direction)
{
geoTrans = null;
direction = esriTransformDirection.esriTransformForward;
var list = GetTransformations(fromSR, toSR);
if (list.Count == 0)
{
MessageBox.Show(String.Format("No geotransforms to go from {0} to {1}", fromSR.Name, toSR.Name));
return;
}
IListDialog dlg = new ListDialogClass();
foreach (IGeoTransformation gt in list)
dlg.AddString(gt.Name);
if (dlg.DoModal("Choose a Geotransformation", 0, hWnd))
{
geoTrans = list[dlg.Choice];
direction = GetDir(geoTrans, fromSR, toSR);
}
}
public static List<IGeoTransformation> GetTransformations(ISpatialReference fromSR, ISpatialReference toSR)
{
int fromFactcode = GetGCSFactoryCode(fromSR);
int toFactcode = GetGCSFactoryCode(toSR);
var outList = new List<IGeoTransformation>();
// Use activator to instantiate arcobjects singletons ...
var type = Type.GetTypeFromProgID("esriGeometry.SpatialReferenceEnvironment");
var srf = Activator.CreateInstance(type) as ISpatialReferenceFactory2;
var gtSet = srf.CreatePredefinedGeographicTransformations();
gtSet.Reset();
for (int i = 0; i < gtSet.Count; i++)
{
ISpatialReference fromGcsSR;
ISpatialReference toGcsSR;
var geoTrans = (IGeoTransformation)gtSet.Next();
geoTrans.GetSpatialReferences(out fromGcsSR, out toGcsSR);
if ((fromGcsSR.FactoryCode == fromFactcode && toGcsSR.FactoryCode == toFactcode) ||
(fromGcsSR.FactoryCode == toFactcode && toGcsSR.FactoryCode == fromFactcode))
{
outList.Add(geoTrans);
}
}
return outList;
}
private static esriTransformDirection GetDir(IGeoTransformation geoTrans, ISpatialReference sr1, ISpatialReference sr2)
{
int code1 = GetGCSFactoryCode(sr1);
int code2 = GetGCSFactoryCode(sr2);
ISpatialReference fromSR;
ISpatialReference toSR;
geoTrans.GetSpatialReferences(out fromSR, out toSR);
if (fromSR.FactoryCode == code1 && toSR.FactoryCode == code2)
return esriTransformDirection.esriTransformForward;
else if (fromSR.FactoryCode == code2 && toSR.FactoryCode == code1)
return esriTransformDirection.esriTransformReverse;
else
throw new Exception(String.Format("{0} does not support going between {1} and {2}",
geoTrans.Name, sr1.Name, sr2.Name));
}
private static int GetGCSFactoryCode(ISpatialReference sr)
{
if (sr is IProjectedCoordinateSystem)
return ((IProjectedCoordinateSystem)sr).GeographicCoordinateSystem.FactoryCode;
else if (sr is IGeographicCoordinateSystem)
return ((IGeographicCoordinateSystem)sr).FactoryCode;
else
throw new Exception("unsupported spatialref type");
}
protected override void OnUpdate()
{
}
}
}
For the GetDir method, the second and third parameters should be the factory codes of the base geographic coordinate systems of the source and target coordinate systems. For projected coordinate systems, the ISpatialReference interface will provide the factory code of the coordinate system but not the base geographic coordinate system. For example, NAD27 Zone 12 equates to a factory code 26712 and the base geographic coordinate system is 4267 (NAD27 geographic). The 'out' parameters of geoTrans.GetSpatialReferences(out fromSR, out toSR); will only be factory codes for geographic coordinate systems.
You can get the base geographic coordinate system of a projected coordinate system by creating a projected coordinate system object and then getting its base geogr. coord sys factory code.
THe code below should help. The integers for the original and target coordinate systems would have to be provided to properly create the object. And the code is expecting the type(projected vs. geographic) of source/target coordinate systems has been provided.
//Create Spatial Reference Factory
ISpatialReferenceFactory3 srFactory = new SpatialReferenceEnvironmentClass();
IProjectedCoordinateSystem3 pcsSource;
IGeographicCoordinateSystem2 gcsSource;
IProjectedCoordinateSystem3 pcsTarget;
IGeographicCoordinateSystem2 gcsTarget;
ISpatialReference3 srSourceCoordSys;
ISpatialReference3 srTargetCoordSys;
int OriginalCoordSys;
int TargetCoordSys;
int SourceGeographicBaseID = 0;
int TargetGeographicBaseID = 0;
//Define the source coordinate system
if (OrigCoordSysType.ToUpper() == "PROJECTED")
{
pcsSource = (IProjectedCoordinateSystem3)srFactory.CreateProjectedCoordinateSystem(OriginalCoordSys);
SourceGeographicBaseID = pcsSource.GeographicCoordinateSystem.FactoryCode;
srSourceCoordSys = (ISpatialReference3)pcsSource;
}
else
{
gcsSource = (IGeographicCoordinateSystem2)srFactory.CreateGeographicCoordinateSystem(OriginalCoordSys);
SourceGeographicBaseID = gcsSource.FactoryCode;
srSourceCoordSys = (ISpatialReference3)gcsSource;
}
//Define the target coordinate system
if (TargetCoordSysType.ToUpper() == "PROJECTED")
{
pcsTarget = (IProjectedCoordinateSystem3)srFactory.CreateProjectedCoordinateSystem(TargetCoordSys);
TargetGeographicBaseID = pcsTarget.GeographicCoordinateSystem.FactoryCode;
srTargetCoordSys = (ISpatialReference3)pcsTarget;
}
else
{
gcsTarget = (IGeographicCoordinateSystem2)srFactory.CreateGeographicCoordinateSystem(TargetCoordSys);
TargetGeographicBaseID = gcsTarget.FactoryCode;
srTargetCoordSys = (ISpatialReference3)gcsTarget;
}
The simplest method is write a python script (arcpy) and expose it how toolbox in arcobjects. Then you use arcobjects and call this tool.
In arcpy you have ListTransformations (http://resources.arcgis.com/en/help/main/10.1/index.html#/ListTransformations/018v0000001p000000/)
toolbox tool exposed with a toolbox:
import arcpy
try:
layer = arcpy.GetParameter(0)
layerDescribe = arcpy.Describe(layer)
from_sr = layerDescribe.featureClass.spatialReference
to_sr = arcpy.GetParameter(1)
extent = layerDescribe.extent
transformations = arcpy.ListTransformations(from_sr, to_sr, extent)
arcpy.SetParameter(2, transformations)
except StandardError, ErrDesc:
arcpy.AddMessage("Error: " + str(ErrDesc))
except:
arcpy.AddMessage("Error: get list transformations")
In arcobjects:
IVariantArray parameters = new VarArrayClass();
parameters.Add(this.pathLayer); //path and file lyr example c:\temp\test.lyr
parameters.Add(this.spatialReferenceOut);
IGPValue output = null;
Geoprocessor geoprocessor = new Geoprocessor();
geoprocessor.AddToolbox(@"c:\Temp\myToolbox.tbx");
ExecuteTask(this.geoprocessor, parameters, "ListTransformations", 0 ,out output); //ListTransformations is the name of your toolbox tool
IGPMultiValue GPMultiValue = output as IGPMultiValue;
for (int i = 0; i < GPMultiValue.Count;i++ )
{
cboDatumTransformation.Items.Add((GPMultiValue.get_Value(i) as IGPString).Value);
}
So you have only datum transformations available for extent of input
News: now in ArcGIS Server 10.3 API Rest you have available in GeometryService -> Project FindTransformations that return a list of applicable geographic transformations one should use when projecting geometries from the input spatial reference to the output spatial reference