Skip to content Skip to sidebar Skip to footer

Dbgeography Intersects Method Not Working

The System.Data.Spatial.DbGeography.Intersects method seems to always return true for me. I'm not sure why this happens. I've created a simple command line snippet below that resul

Solution 1:

The point clearly is nowhere near the bounds, and therefore should not intersect.

There is a rule: as soon as you say "clearly", prepare to be incorrect. :)

Kidding aside, you have a ring orientation problem. That is, the order in which you specify the points matters. As you've specified the corners, you've defined an area that is the entire globe with a very tiny hole in it. Try using this instead:

POLYGON ((146 -20,146 -22,148 -22,148 -20,146 -20))

So, how do you know intrinsically that you have an orientation problem? One heuristic that I like to use is that if the envelope angle for the object is large (90 degrees = one hemisphere), then you've specified the ordering incorrectly. There's an EnvelopeAngle method on the Geography datatype in the DB engine (but it looks like not in the DbGeography class in C#) to determine this. There's also a convenience method (again for sure in the DB but not in C#) for reorienting the ring called, not surprisingly ReorientObject.

Solution 2:

As already mentioned this is a ring orientation error. For those that use SQL Server - It uses left-handed orientation, which means that if you are walking along the perimeter of a polygon, your left hand should be on the inside of the polygon and your right hand on the outside (counter-clockwise or anti-clockwise). I have gotten the “ring orientation” error even from government data because the polygon was drawn in the opposite direction (clockwise, or right-handed) which meant that SQL Server was treating the whole surface of the earth EXCEPT FOR my polygon as the area of the polygon.

This is a method that fixes this problem automatically:

publicboolIsInside(DbGeography polygon, double longitude, double latitude)
{
    DbGeography point = DbGeography.FromText(string.Format("POINT({1} {0})", latitude.ToString().Replace(',', '.'), longitude.ToString().Replace(',', '.')), DbGeography.DefaultCoordinateSystemId);

    var wellKnownText = polygon.AsText();

    var sqlGeography =
        SqlGeography.STGeomFromText(new SqlChars(wellKnownText), DbGeography.DefaultCoordinateSystemId)
            .MakeValid();

    //Now get the inversion of the above areavar invertedSqlGeography = sqlGeography.ReorientObject();

    //Whichever of these is smaller is the enclosed polygon, so we use that one.if (sqlGeography.STArea() > invertedSqlGeography.STArea())
    {
        sqlGeography = invertedSqlGeography;
    }

    polygon = DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography);

    return point.Intersects(polygon);
}

For those that use Entity Framework 5<:

I use this extension method to check every Polygon and Multipolygon before saving them to the database.

publicstatic DbGeography MakePolygonValid(this DbGeography geom)
{
    var wellKnownText = geom.AsText();

    //First, get the area defined by the well-known text using left-hand rulevar sqlGeography =
        SqlGeography.STGeomFromText(new SqlChars(wellKnownText), DbGeography.DefaultCoordinateSystemId)
            .MakeValid();

    //Now get the inversion of the above areavar invertedSqlGeography = sqlGeography.ReorientObject();

    //Whichever of these is smaller is the enclosed polygon, so we use that one.if (sqlGeography.STArea() > invertedSqlGeography.STArea())
    {
        sqlGeography = invertedSqlGeography;
    }
    return DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography);
}

I can then use a method like this to check Intersects at database level.

publicstaticclassGeoHelper
{
    publicconstint SridGoogleMaps = 4326;
    publicconstint SridCustomMap = 3857;

    publicstatic DbGeography PointFromLatLng(double lat, double lng)
    {
        return DbGeography.PointFromText(
            "POINT("
            + lng.ToString(CultureInfo.InvariantCulture) + " "
            + lat.ToString(CultureInfo.InvariantCulture) + ")",
            SridGoogleMaps);
    }
}

public County GetCurrentCounty(double latitude, double longitude)
{
    var point = GeoHelper.PointFromLatLng(latitude, longitude);

    var county = db.Counties.FirstOrDefault(x =>
        x.Area.Intersects(point));

    return county;
}

T-SQL generated by Entity Framework:

SELECT TOP (1) 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
[Extent1].[Code] AS [Code], 
[Extent1].[Area] AS [Area]FROM[Election].[County] AS [Extent1]
WHERE ([Extent1].[Area].STIntersects(@p__linq__0)) = 1


-- p__linq__0: 'POINT (10.0000000 32.0000000)' (Type = Object)

Can be tested like this manually:

declare @p__linq__0 varchar(max)
set @p__linq__0 = 'POINT (10.0000000 32.0000000)' SELECT TOP (1) 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Code] AS [Code], 
    [Extent1].[Area] AS [Area]
    FROM [Election].[County] AS [Extent1]
    WHERE ([Extent1].[Area].STIntersects(@p__linq__0)) = 1

More information can be found here:

https://docs.microsoft.com/en-us/sql/t-sql/spatial-geometry/stintersects-geometry-data-type

Sources from this thread:

Check if dbgeometry dbgeometry/dbgeography point is within a polygon

Post a Comment for "Dbgeography Intersects Method Not Working"