eliprandi

Universal Projection API/Algorithm?

Discussion created by eliprandi on Oct 19, 2012
Latest reply on Oct 22, 2012 by eliprandi
Hi,
we are writing a Server Object Extension for ArcGIS Server 10.1. To follow the same API conventions as esri's REST endpoints, we are providing the option for our end-users to provide an output Spatial Reference. Our endpoint extracts some geometries from some features and returns a trunacated version of some of the geometries.
We have run into lots of issues using simple code to convert our geometries from the GDB's spatial reference to the client (javascript - web Mercator) spatial reference. We first tried the basic IGeometry.Project(our_client_spatial_reference), but that didn't work. We then tried to use IGeometry2.ProjectEx(...). This seemed to yield better results, but still off by a noticeable amount on the map.
If we use esri's out-of-the-box endpoints (<myserver>/MapServer/<layerId>/query) and specify the same spatial reference for output, it works fine (but we can't get the real GDB geometries for our need, so this only works for the full geometries). We are assuming there is a fairly simple way to project a geometry from any spatial reference to any other spatial reference.
Is anybody aware of an esri API that provides this functionality? can anybody point us to a code sample that would show what all is involved in properly projecting? we are an ISV and our code must work on any input and output spatial reference. We cannot assume a particular spatial reference for input and ouput.
We have already looked and implemented suggestions from this post, but, while it gets us a more accurate result, it's still not what esri seems to be doing for their projection.
Here is the core of our code:
private void ReprojectGeometry(IGeometry2 geometry, ISpatialReference targetReference)
{
    var sourceReference = geometry.SpatialReference;
    var factory = new SpatialReferenceEnvironmentClass();
    var transformations = factory.GetPredefinedGeographicTransformations();
    var sourceFactoryCode = sourceReference.FactoryCode;
    var targetFactoryCode = targetReference.FactoryCode;
    var transformation = GetGeoTransformation(transformations, sourceFactoryCode, targetFactoryCode);
    if (transformation == null)
    {
        sourceFactoryCode = GetUnderlyingCoordinateSystem(sourceReference);
        targetFactoryCode = GetUnderlyingCoordinateSystem(targetReference);

        transformation = GetGeoTransformation(transformations, sourceFactoryCode, targetFactoryCode);
        if (transformation == null)
        {
            throw new InvalidOperationException("No transformation found to/from spatial references");
        }
    }

    esriTransformDirection direction = GetTransformationDirection(transformation, sourceFactoryCode, targetFactoryCode);
    geometry.ProjectEx(targetReference, direction, transformation, true, 0d, 0d);
}

private int GetUnderlyingCoordinateSystem(ISpatialReference sourceReference)
{
    var projected = sourceReference as IProjectedCoordinateSystem3;
    if (projected != null)
    {
        return projected.GeographicCoordinateSystem.FactoryCode;
    }
    var geographic = sourceReference as IGeographicCoordinateSystem2;
    if (geographic != null)
    {
        return geographic.FactoryCode;
    }
    return 0;
}

private static IGeoTransformation GetGeoTransformation(ISet transformations,
                                                        int sourceFactoryCode, int targetFactoryCode)
{
    var transformation = transformations.AsEnumerable<IGeoTransformation>().FirstOrDefault(t =>
        {
            ISpatialReference fromSR;
            ISpatialReference toSR;
            t.GetSpatialReferences(out fromSR, out toSR);
            return (fromSR.FactoryCode == targetFactoryCode && toSR.FactoryCode == sourceFactoryCode) ||
                    (fromSR.FactoryCode == sourceFactoryCode && toSR.FactoryCode == targetFactoryCode);
        });
    return transformation;
}

private esriTransformDirection GetTransformationDirection(IGeoTransformation tx, int fromCode, int toCode)
{
    ISpatialReference fromSR;
    ISpatialReference toSR;
    tx.GetSpatialReferences(out fromSR, out toSR);
    return fromSR.FactoryCode == fromCode ? esriTransformDirection.esriTransformForward : esriTransformDirection.esriTransformReverse;
}
...
public static class SetExtensions
{
    public static IEnumerable<T> AsEnumerable<T>(this ISet set) where T : class
    {
        set.Reset();
        var item = (T) set.Next();
        while (item != null)
        {
            yield return item;
            item = (T) set.Next();
        }
    }
}



Thanks in advance for your help,

Eric.

Outcomes