Parsing UTM coordinates to DBGeography in C#
Asked Answered
O

2

7

I'm writing a WinForms app in C#. I need to ensure no two Datarows in my Datatable are more than 100 km apart. Each row has a UTM Zone, Easting, and Northing in separate DataColumns. All coordinates use the same datum, but some have different zones (otherwise, I would just use pythag math since UTMs are in metres). So far, I'm using the following code to do this, but it doesn't seem like my DbGeography.PointFromText method is working quite right (see * in the code), as when the code reaches the line of code with the '**' at the start of it, I get an error saying "24201: Latitude values must be between -90 and 90 degrees". I've also tried:

dtTrap.Rows[i]["TrapGeog"] = DbGeography.PointFromText(pointWellKnownText: "POINT M(" + dtTrap.Rows[i][intEastingIndex].ToString() + " " + dtTrap.Rows[i][intNorthingIndex].ToString() + " " + dtTrap.Rows[i][Zone].ToString() + ")", coordinateSystemId: SRID);

Only to have it complain that "There's no column # 17" (17 is my UTM Zone).

I've found very little documentation for using this stuff... as far as I can tell, my SRIDs are correct (I pulled them from this site). I've done this kind of thing using Lat+Longs before, and it worked wonderfully. I just can't find proper syntax for the UTMs.

using System.Data.Entity.Spatial;
...
DataColumn dcGeog = new DataColumn("TrapGeog", typeof(DbGeography));
dtTrap.Columns.Add(dcGeog);
byte Zone;
Int16 SRID;
for (int i = 0; i < dtTrap.Rows.Count; ++i)
{
    if (dtTrap.Rows[i][intZoneIndex] != null
    && dtTrap.Rows[i][intNorthingIndex] != null
    && dtTrap.Rows[i][intEastingIndex] != null
    && byte.TryParse(dtTrap.Rows[i][intZoneIndex].ToString(), out Zone) == true)
    {
        if (Zone == 15) { SRID = 26915; }
        else if (Zone == 16) { SRID = 26916; }
        else if (Zone == 17) { SRID = 26917; }
        else { SRID = 26918; }
        // shove it in:
        try
        {
            *dtTrap.Rows[i]["TrapGeog"] = DbGeography.PointFromText(pointWellKnownText: "POINT(" + dtTrap.Rows[i][intEastingIndex].ToString() + " " + dtTrap.Rows[i][intNorthingIndex].ToString() + ")", coordinateSystemId: SRID);
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null)
            {
                **MessageBox.Show(ex.InnerException.Message);
            }
            else
            {
                MessageBox.Show(ex.Message);
            }
        }

    }
}
for (int i = 0; i < dtTrap.Rows.Count - 1; ++i)
{
    for (int k = i + 1; k < dtTrap.Rows.Count; ++i)
    {
        DbGeography iTrap = (DbGeography)dtTrap.Rows[i]["TrapGeog"];
        DbGeography kTrap = (DbGeography)dtTrap.Rows[k]["TrapGeog"];
        if (iTrap.Distance(kTrap) > 100000)
        {
            sbErrorsAndWarningsLog.Append(@"Warning:  Line number " + (i + 2).ToString() + " on the Trap spreadsheet has coordinates that are at least 100 km away from row " + (k + 2).ToString() + "'s point.  Please check that these coordinates are correct.").AppendLine();
            boolWarningsFound = true;
            break;
        }
    }
}
Origami answered 16/2, 2018 at 21:40 Comment(0)
O
5

In my quest to figure this out, I stumbled upon this post on a sister site here. I figure that DbGeography is based on SQL Server's geography data type, and DbGeometry likewise, geometry data type.

Some pertinent tidbits:

The geography datatype, EPSG:4326, is in degrees, which is why your POINT(257306 142708) is failing, as it falls out of the (-180,180), (-90,90) range.

...

The solution ... to use a geometry datatype instead, ie, projected, not geographic.

And so I've changed my code to:

DataColumn dcGeom = new DataColumn("TrapGeom", typeof(DbGeometry));
...
dtTrap.Rows[i]["TrapGeom"] = DbGeometry.PointFromText(pointWellKnownText: stPoint, coordinateSystemId: SRID);

And it appears to parse just fine. Though, the last part of the post has me worried:

SQL Server insists on there being a SRID, even though you can't actually do anything useful with it, such as convert for one coordinate system to another. It can get really confusing if you accidentally mix geometries with different SRIDs as you will simply get no results (witout warning, but this is an aside).

And so, when I finally do if (Trap1.Distance(Trap2) > 100000) ... then I'm half-expecting it to malfunction when I have two different points in different SRIDs. I'll test and write a comment on what I find.

Origami answered 21/2, 2018 at 16:40 Comment(1)
Update: My worries were warranted. When calculating distance of Trap1 to Trap2 (double? d = Trap1.Distance(Trap2)) when they have different SRIDs (due to a difference in UTM zones), Visual Studio throws the error "Data is Null. This method or property cannot be called on Null values." - despite both points being non-null.Origami
J
2

For better debugging of this error I recommend first checking your input data.

string point = "";
try
{
    string point = "POINT(" + dtTrap.Rows[i][intEastingIndex].ToString() + " " + dtTrap.Rows[i][intNorthingIndex].ToString() + ")";

    dtTrap.Rows[i]["TrapGeog"] = DbGeography.PointFromText(pointWellKnownText: point, coordinateSystemId: SRID);
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine("correct point? " + point);
    if (ex.InnerException != null)
    {
        MessageBox.Show(ex.InnerException.Message + " at " + point);
    }
    else
    {
        MessageBox.Show(ex.Message);
    }
}

My guess is, there is some comma instead of a point or some spaces inside your string.

Janessa answered 21/2, 2018 at 10:32 Comment(6)
Thanks for the thought. The string in this scenario comes out to POINT(312338 5346686). I'm all ears for alternate syntax for parsing UTM eastings and northings into this datatype...Origami
I've also tried it with the Easting and Northing flipped (e.g., POINT(5346686 312338) with no change in error message.Origami
I am no expert in spatial data, but shouldn't it range between 0 and 359.999 ? So your data is missing the decimal dot POINT(534.46686 31.2338) ?Janessa
My understanding is that with this SRID, the unit of measure is in metres rather than radians and so my easting and northing values are entirely appropriate.Origami
I'm not helpful so far. should I delete my answer, so you would draw in more experts?Janessa
No, I believe your answer should stay. As for the original question "Parsing UTM coordinates to DBGeography in C#", I believe I've found my answer. I may post a related question about comparing distances of DbGeometry points with different UTM zones in the future.Origami

© 2022 - 2024 — McMap. All rights reserved.