views:

325

answers:

1

I am using Spatial.NHibernate to save some geometry shapes to a Geography column in Sql Server 2008. Here is my mapping:

public class AreaMapping : ClassMap<Area>
{
    public AreaMapping()
    {
        Id(c => c.Id).GeneratedBy.HiLo(100.ToString());
        Map(c => c.Name).Not.Nullable();
        Map(x => x.Boundary)
            .CustomTypeIs<MsSql2008GeographyType>()
            .Not.Nullable()
            .CustomSqlTypeIs("GEOGRAPHY");
    }
}

The mapping appears to be valid. Here is the class:

public class Area
{
    public virtual Guid Id { get; set; }
    public virtual Polygon Boundary { get; set; }
    public virtual string Name { get; set; }
}

However when I go to save an area like this:

Area area = new Area{ Boundary = new Polygon(new LinearRing(new ICoordinate[]{
                    new Coordinate(-1.911524, 55.136334),
                    new Coordinate(-1.912679, 55.136293),
                    new Coordinate(-1.912689, 55.136178),
                    new Coordinate(-1.911507, 55.136194),
                    new Coordinate(-1.911524, 55.136334)}))
Session.Save(area);

I get the following error:

The specified input does not represent a valid geography instance.

Type: System.ArgumentException Source: Microsoft.SqlServer.Types TargetSite: Microsoft.SqlServer.Types.SqlGeography ConstructGeographyFromUserInput(Microsoft.SqlServer.Types.GeoData, Int32) ...etc.

I understand that a valid Polygon for a geography type must be plotted anti-clockwise, and it must be closed, and it must not overlap itself. I'm pretty sure I'm fulfilling all these restrictions (although please correct me if I'm wrong) so I'm a bit stumped here. Either there is something wrong with my polygon, or NHibernate is not persisting it correctly - any help welcome!

Edit Okay I'm confused now.

To simplify things, I changed my polygon to this:

Area area = new Area{ Boundary = new Polygon(new LinearRing(new ICoordinate[]{
                    new Coordinate(10,15),
                    new Coordinate(10,5),
                    new Coordinate(20,5),
                    new Coordinate(20,15),
                    new Coordinate(10,15)}))

I get the same

The specified input does not represent a valid geography instance.

Note that the polygon is plotted anti-clockwise (as it should be according to various sources). But if I change my coordinates to clockwise:

Area area = new Area{ Boundary = new Polygon(new LinearRing(new ICoordinate[]{
                    new Coordinate(10,15),
                    new Coordinate(20,15),
                    new Coordinate(20,5),
                    new Coordinate(10,5),
                    new Coordinate(10,15)}))

It seems to be okay. So is clockwise valid or what?

A: 

Okay, I know what's going on. Microsoft, in their infinate wisdom, appear to have gone against the grain of the whole of the rest of the world and have decided that in their WKT representations of geography coordinates, they put Latitiude before Longitude (Y,X).

X,Y? WTF you might ask?! Well there's a whole debate about it, apparently they have their reasons, but the general consensus is that it sucks and so they're going to change it.

It sucks because it totally screws interoperablity. When NHibernate spatial persists the NetTopologySuite objects I am using to persist geography (IGeography types) it writes coordinates out as X,Y (the natural, expected way i would assume it should work). When this hits SQL server, it's trying to interpret the X as a Y and Y as an X, hence all my issues with invalid types.

So now I either have to fix NHibernate, or swap the coordinates in my code. Shame on you, Microsoft, for causing me such pain!

James Allen