12

I would like to save a circle in a sql-server 2008 geography field, using c#.

In c# I have a latitude, a longitude and a radius but I just can't find a way to calculate the polygon that would represent the circle and create a SqlGeography from it.

I have tried to following function to create the polygon:

    private List<Coordinate> getCirclePoints(Coordinate center, int radius, int speed)  //speed 1: draws 360 sides, 2 draws 180 etc...
    {
        var centerLat = (center.Latitude * Math.PI) / 180.0;  //rad
        var centerLng = (center.Longitude * Math.PI) / 180.0; //rad
        var dist = (float)radius / 6371.0;             //d = angular distance covered on earth's surface
        var circlePoints = new List<Coordinate>();
        for (int x = 0; x <= 360; x += speed)
        {
            var brng = x * Math.PI / 180.0;         //rad
            var latitude = Math.Asin(Math.Sin(centerLat) * Math.Cos(dist) + Math.Cos(centerLat) * Math.Sin(dist) * Math.Cos(brng));
            var longitude = ((centerLng + Math.Atan2(Math.Sin(brng) * Math.Sin(dist) * Math.Cos(centerLat), Math.Cos(dist) - Math.Sin(centerLat) * Math.Sin(latitude))) * 180.0) / Math.PI;
            circlePoints.Add(new Coordinate((latitude * 180.0) / Math.PI, longitude));
        }
        return circlePoints;
    }

And then try to convert this List<Coordinate> to a parsable string:

        var s = "POLYGON((" + string.Join(",", points.ConvertAll(p => p.Longitude + " " + p.Latitude).ToArray()) + "))";
        var poly = SqlGeography.STPolyFromText(new System.Data.SqlTypes.SqlChars((SqlString)s), 4326);

But it always complains the polygon has to be on a single hemisphere, where I'm sure it is the case.

Am I on the right track at all? Is there any other (simpler) way to do this?

Johann
  • 12,158
  • 11
  • 62
  • 89
  • Here is the official doc https://learn.microsoft.com/en-US/sql/t-sql/spatial-geography/bufferwithtolerance-geography-data-type?view=sql-server-ver15 – NoWar Oct 12 '21 at 06:18

1 Answers1

26

OK, found the answer on my own. The trick is to create a point

var point = SqlGeography.Point(latitude, longitude, 4326);

Then create a buffer around the point

var poly = point.BufferWithTolerance(radiusInMeter, 0.01, true); //0.01 is to simplify the polygon to keep only a few sides

Then you could simply create a SqlCommand and add the polygon as parameter:

var param = new SqlParameter(@"Polygon", poly);
param.UdtTypeName = "Geography";
command.Parameters.Add(param);

Hope that will help someone else in the future!

Orhan
  • 420
  • 1
  • 6
  • 12
Johann
  • 12,158
  • 11
  • 62
  • 89
  • 1
    This was very helpful, thanks for this. After a bit more reading, I thought it would be worthwhile to add that the .01 tolerance value will result in a 33 point polygon (standard sql geo circle). If some error is allowable, using a larger value could be used. I am expecting my radius to be anywhere from 5 to 50 meters, so I am using 3 for my tolerance value to improve efficiency. – Dave - NexRF Jun 23 '14 at 06:38
  • Thanks to your comment I just ran a few tests and changed my tolerance to 0.08 which seems to give a 16 sides polygon for small-ish radiuses. – Johann Jun 24 '14 at 08:23
  • From 0.09 (for radiuses of 100 meters or less) it gives a 8 sides polygon or less which is not good enough for me – Johann Jun 24 '14 at 08:30
  • The only issue i have with this approach is..How do you convert the "radius In Meters" to "lat-long" units. Latitude, longitudes use different scales, both being different from radius as well. – arviman Aug 26 '14 at 08:28