Create a SqlGeography polygon-circle from a center and radius - c#

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?

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!

Related

c# transform line equation to change reference origin and angle

Suppose I have the following situation.
I have a straight line equation in the form y=mx+q referred to the xOy plain.
I have the need to translate the line equation into the x'O'y' plain.
How would you procede in an efficient manner?
I should add that I know the coordinates and rotation of the two origins referred to a perfectly straight reference plain that can be seen as the paper on which the origins are drawn.
Refer to wikipedia : https://en.wikipedia.org/wiki/Rotation_of_axes#:~:text=If%20the%20curve%20
For just a rotation of coordinate systems: you can get the corpondances:
That's equivalent to :
in your case you can add the translation between the 2 coordinate systems, so you would have:
x' = x.cos(Th) + y.sin(Th) + tx
y' = -x.sin(Th) + y.cos(Th) + ty
with Th = Theta angle and T = (tx, ty) translation vector between both coordinate systems.
Once you have that you can simply replace x and y and simplify in the equation y=mx+q as you know:
y = mx + q
y' = m2 x' + q2
x' = x.cos(Th) + y.sin(Th) + tx
y' = -x.sin(Th) + y.cos(Th) + ty
(your goal is to find m2 and q2 knowing Theta and T)
Note: I think in 2D it is better to use the general equation of straight lines which is ax + by +c = 0 instead of the one you use y = mx +q. This is because vertical lines, whose equation are of the form "x = q" can't be written with the form y = mx +q.
Considering that the laser is my reference point for the line I did as follows:
private Model RotoTranslate(Model line, LaserContainer laser)
{
Model transformModel = new Model();
float cosA = (float)Math.Cos((laser.Rotation * Math.PI) / 180.0f);
float sinA = (float)Math.Sin((laser.Rotation * Math.PI) / 180.0f);
if (line.XModel)
{
transformModel.XModel = true;
transformModel.M = ((line.M * cosA) - sinA) / ((line.M * sinA) + cosA);
transformModel.Q = (line.Q + (laser.PositionGraph.Y * ((line.M * sinA) + cosA)) -
(laser.PositionGraph.X * ((line.M * cosA) - sinA))) / ((line.M * sinA) + cosA);
}
else
{
transformModel.XModel = false;
transformModel.M = ((line.M * cosA) + sinA) / (-(line.M * sinA) + cosA);
transformModel.Q = (line.Q + (laser.PositionGraph.X * (-(line.M * sinA) + cosA)) -
(laser.PositionGraph.Y * ((line.M * cosA) + sinA))) / (-(line.M * sinA) + cosA);
}
return transformModel;
}
I have to test now and see if there are exceptions I didn't account for...
The definition of XModel essentially refers to the way the line equation is expressed.
If the model has:
XModel = true;
then the equation is in the form y=mx+q, otherwise it's in the form x=my+q;

Calculate rotation angle with PanGestureRecognizer for analog clock hand

I am trying to rotate the clock hands using PanGestureRecognizer for BoxView.
Currently I can correctly rotate the hand, but only on one side of the clock, on the other side the hand does not move correctly. Also, the coordinates for some reason depend on how many times you rotate the arrow.
My OnPanUpdated function
switch (e.StatusType)
{
case GestureStatus.Running:
Point UpdatedPan = new Point(e.TotalX, e.TotalY);
UpdateHandRotation(hourHand, UpdatedPan);
break;
}
And the main function
private void UpdateHandRotation(BoxView hand, Point UpdatedPan)
{
// Get current hand rotation in radians
double r = hand.Rotation * Math.PI / 180;
Point center = new Point(absoluteLayout.Width / 2, absoluteLayout.Height / 2);
double radius = 0.45 * Math.Min(absoluteLayout.Width, absoluteLayout.Height);
double handEndX = Math.Cos(r) * radius + center.X;
double handEndY = Math.Sin(r) * radius + center.Y;
double movedEndX = handEndX + UpdatedPan.X;
double movedEndY = handEndY + UpdatedPan.Y;
double radians = Math.Atan2(movedEndY - center.Y, movedEndX - center.X);
// Convert to degrees
var angle = radians * (180.0 / Math.PI);
hand.Rotation = angle;
}
Here is an example of how this works.

Matrix (?) to Rectangle and vise versa

I'm currently working with files that define rectangular shapes in a way that I'm unfamiliar with. Someone told me it might be a matrix, but knowing that doesn't particularly help me with my problem, converting it to points and back.
For example, I have these values:
0.95, -0.28, -0.28, -0.95, 250.0234, 172.1973, -589.0131, 604.8696
These 8 floats make up a rect with the following coordinates, the center being 0:
{X=-778,Y=838}
{X=-303,Y=698}
{X=-399,Y=372}
{X=-874,Y=512}
To get these points I have used the following function, that someone else wrote for use with these files:
static Point[] GetPoints(double d01, double d02, double d03, double d04, double l01, double l02, double p01, double p02)
{
var points = new Point[4];
double a00 = d01 * l01;
double a01 = d02 * l01;
double a02 = d03 * l02;
double a03 = d04 * l02;
double sx1 = p01 - a00 - a02; if (sx1 < p01) sx1 = Math.Ceiling(sx1);
double sy1 = p02 - a01 - a03; if (sy1 < p02) sy1 = Math.Ceiling(sy1);
double sx2 = p01 + a00 - a02; if (sx2 < p01) sx2 = Math.Ceiling(sx2);
double sy2 = p02 + a01 - a03; if (sy2 < p02) sy2 = Math.Ceiling(sy2);
double sx3 = p01 + a00 + a02; if (sx3 < p01) sx3 = Math.Ceiling(sx3);
double sy3 = p02 + a01 + a03; if (sy3 < p02) sy3 = Math.Ceiling(sy3);
double sx4 = p01 - a00 + a02; if (sx4 < p01) sx4 = Math.Ceiling(sx4);
double sy4 = p02 - a01 + a03; if (sy4 < p02) sy4 = Math.Ceiling(sy4);
if (a02 * a01 > a03 * a00)
{
points[0] = new Point((int)sx1, (int)sy1);
points[1] = new Point((int)sx2, (int)sy2);
points[2] = new Point((int)sx3, (int)sy3);
points[3] = new Point((int)sx4, (int)sy4);
}
else
{
points[0] = new Point((int)sx1, (int)sy1);
points[3] = new Point((int)sx2, (int)sy2);
points[2] = new Point((int)sx3, (int)sy3);
points[1] = new Point((int)sx4, (int)sy4);
}
return points;
}
What I'm looking for now is possibly an explanation what these numbers even are, is this something common that I can read up on or is it custom, and a way to convert the points back to the 8 float values.
Can someone help me with this? Without knowing what this even is, it's really hard to find anything :/
p01 and p02 are center coordinates (CenterX and CenterY)
d01-d04 represent cosine and sine of rotation angle (or dx,dy components of unit-length direction vector)
l01 and l02 are half-width and half-height of initial axis-aligned rectangle.
sxi and syi are X and Y-coordinates of ith vertice.
These coordinates are rounded toward the center coordinate.
Finally the vertices are numerated in certain order - either clockwise or counterclockwise (I did not checked)
To reconstruct initial parameters from vertice set:
You can determine center point as middle of two opposite vertices
p01 = (points[0].X + points[2].X) / 2
p02 = (points[0].Y + points[2].Y) / 2
and rotation angle
Angle = atan2(points[1].Y - points[0].Y, points[1].X - points[0].X)
Then correct Angle by +-Pi/2 or +-Pi to the smallest magnitude value (for example, 3/4*Pi=> Pi/4)
Note that initial parameter set angle may differ by +-Pi/2
Find
d01 = Cos(Angle) etc
Note that angle may differ by +-Pi/2 or +-Pi from initial parameter set.
l01 = Abs((points[1].X - points[0].X) * 0.5 / Cos(Angle))
l02 = Abs((points[2].Y - points[1].Y) * 0.5 / Cos(Angle))
Note that l01, l02 may be interchanged due to ambiguity of angle value.

C# WPF Draw equilateral polygons programatically

I'm working on a method to programatically draw equilateral polygon shapes in C#, WPF. But I have been stuck and I can't solve calculating the angles. Where is the problem? How should I correct this? I have given the public int, R(radius) a value of 100.
private Path EquilateralPolygon(int sides)
{
//Centering
Point center = new Point(canvasSize.Width / 2, canvasSize.Height / 2);
PathFigure myPathFigure = new PathFigure();
int alfa = 360 / sides;
int[] angles = new int[6];
for (int i = 0; i < sides; i++)
angles[i] = 360 - alfa * i;
MessageBox.Show(angles.Sum().ToString());
Point A = new Point(center.X, center.Y - R);
myPathFigure.StartPoint = A;
PolyLineSegment myLineSegment = new PolyLineSegment();
for (int i = 1; i < sides; i++)
{
myLineSegment.Points.Add(new Point(center.X + Math.Cos(angles[i]) * R, center.Y + Math.Sin(angles[i]) * R));
}
myLineSegment.Points.Add(A);
PathSegmentCollection myPathSegmentCollection = new PathSegmentCollection();
myPathSegmentCollection.Add(myLineSegment);
myPathFigure.Segments = myPathSegmentCollection;
PathFigureCollection myPathFigureCollection = new PathFigureCollection();
myPathFigureCollection.Add(myPathFigure);
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures = myPathFigureCollection;
Path myPath = new Path();
myPath.Stroke = Brushes.Red;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;
return myPath;
}
You've posted a lot of code and were not specific about how it's not working, so there may be more than one issue with your code. However, one big issue is that the Math.Cos (and related trig methods) take the angle in the form of radians, not degrees as you have them.
Parameters
d
Type: System.Double An angle, measured in radians.
You will need to convert them to radians. To convert, multiply by π (available via Math.PI) then divide by 180 degrees.
myLineSegment.Points.Add(
new Point(center.X + Math.Cos(angles[i] * Math.PI / 180.0) * R,
center.Y + Math.Sin(angles[i] * Math.PI / 180) * R));
EDIT: In addition to the radians/degrees issue, I can see you may be experiencing integer truncation, both in the use of your angles array and your calculation of alfa. I would suggest you try changing your use of integers to double so that your code works fine with fractions of a degree.

How do I draw a circle on my Bing Map application around a centerpoint with a radius given in miles

I have been able to draw an ellipse on my map using latitude and longitude values around a given center point. Although I see a shape on the map, I get an ellipse instead of a circle and I don't think it matches the distance specified. I intend to use this to display objects within that circle (this will be done later on once I can get the circle displaying properly, which is the reason why i need a circle not an ellipse as it should be perfectly round).
I am using a Bing Maps API. I wish to draw the circle in a given miles (distance) from the center which has been passed in through the parameter, the other variable in the parameter called miles is just holding a double value of 1D. I think the problem is to do with the way my maths is being calculated. Has anyone got a clue on how I can refine this code to calculate my miles better.
private void drawPoly(SearchLocation center, Double miles)
{
//amount of vertex
double vertexCount = 100D;
//used by the api to carried out searches
List<SearchLocation> vertices = new List<SearchLocation>();
double v = 0;
double radians = Math.PI / 180D;
double radiansPerDegree = Math.PI / 180D;
double degreePerVertex = 360D / vertexCount;
double radiansPerVertex = degreePerVertex * radiansPerDegree;
var centerOfMap = center;
const double degLatMiles = 68.68637156368D;
double degLonMiles = Math.Cos(center.Latitude.Value) * (68.68637156368D);
double milesLat = (miles * degLatMiles) / 3600;
double milesLon = (miles * degLonMiles) / 3600;
for (v = 0; v < vertexCount; v++)
{
radians = v * radiansPerVertex;
//adds the miles from the center point and draws a circle
double centrLat = center.Latitude.Value + (milesLat * Math.Sin(radians));
double centrLon = center.Longitude.Value + (milesLon * Math.Cos(radians));
vertices.Add(new SearchLocation() { Latitude = centrLat, Longitude = centrLon });
}
Ok, I've misundestood your question. This should work :
/// <summary>
/// Calculates the end-point from a given source at a given range (meters) and bearing (degrees).
/// This methods uses simple geometry equations to calculate the end-point.
/// </summary>
/// <param name="source">Point of origin</param>
/// <param name="range">Range in meters</param>
/// <param name="bearing">Bearing in degrees</param>
/// <returns>End-point from the source given the desired range and bearing.</returns>
public static PointLatLng CalculateDerivedPosition(PointLatLng source, double range, double bearing)
{
double latA = source.Lat * DEGREES_TO_RADIANS;
double lonA = source.Lng * DEGREES_TO_RADIANS;
double angularDistance = range / EARTH_RADIUS_M;
double trueCourse = bearing * DEGREES_TO_RADIANS;
double lat = Math.Asin(
Math.Sin(latA) * Math.Cos(angularDistance) +
Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse));
double dlon = Math.Atan2(
Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA),
Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat));
double lon = ((lonA + dlon + Math.PI) % (Math.PI * 2)) - Math.PI;
return new PointLatLng(
lat / DEGREES_TO_RADIANS,
lon / DEGREES_TO_RADIANS);
}
Juste take your center as source :
for (int i = 0; i < 360; i++)
{
vertices.Add(CalculateDerivedPosition(center, circleRadius, i));
}
To prevent elipses on sertain latitudes I use the following code:
// Function to draw circle on map:
private void DrawCircle(BasicGeoposition CenterPosition, int Radius)
{
Color FillColor = Colors.Purple;
Color StrokeColor = Colors.Red;
FillColor.A = 80;
StrokeColor.A = 80;
Circle = new MapPolygon
{
StrokeThickness = 2,
FillColor = FillColor,
StrokeColor = StrokeColor,
Path = new Geopath(Functions.CalculateCircle(CenterPosition, Radius))
};
mpBingMaps.MapElements.Add(Circle);
}
// Constants and helper functions:
const double earthRadius = 6371000D;
const double Circumference = 2D * Math.PI * earthRadius;
public static List<BasicGeoposition> CalculateCircle(BasicGeoposition Position, double Radius)
{
List<BasicGeoposition> GeoPositions = new List<BasicGeoposition>();
for (int i = 0; i <= 360; i++)
{
double Bearing = ToRad(i);
double CircumferenceLatitudeCorrected = 2D * Math.PI * Math.Cos(ToRad(Position.Latitude)) * earthRadius;
double lat1 = Circumference / 360D * Position.Latitude;
double lon1 = CircumferenceLatitudeCorrected / 360D * Position.Longitude;
double lat2 = lat1 + Math.Sin(Bearing) * Radius;
double lon2 = lon1 + Math.Cos(Bearing) * Radius;
BasicGeoposition NewBasicPosition = new BasicGeoposition();
NewBasicPosition.Latitude = lat2 / (Circumference / 360D);
NewBasicPosition.Longitude = lon2 / (CircumferenceLatitudeCorrected / 360D);
GeoPositions.Add(NewBasicPosition);
}
return GeoPositions;
}
private static double ToRad(double degrees)
{
return degrees * (Math.PI / 180D);
}
This code is usefull for small radius of less than a few miles.

Categories