OpenGL draw lines based on Lat/Lon Coordinates - c#

I need some help with drawing lines from lat/lon coordinates to an OpenGL canvas.
Gl.glViewport(0, 0, simpleOpenGlControl.ClientRectangle.Width, simpleOpenGlControl.ClientRectangle.Height);
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Gl.glOrtho(0, simpleOpenGlControl.ClientRectangle.Width, 0, simpleOpenGlControl.ClientRectangle.Height, -1, 1);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
Gl.glBegin(Gl.GL_LINES);
Gl.glVertex2d(getX(36.0740197, -115.1051997), getY(36.0740197, -115.1051997));
Gl.glVertex2d(getX(36.0740197, -115.0845997), getY(36.0740197, -115.0845997));
Gl.glEnd();
Gl.glFlush();
And here's the methods to convert the lat/lon to XY:
public static double ToRadians(double valueInDegrees)
{
return (Math.PI / 180) * valueInDegrees;
}
public double getY(double lat, double lon)
{
double mercN = Math.Log(Math.Tan((Math.PI / 4) + (ToRadians(lat) / 2)));
return (simpleOpenGlControl.ClientRectangle.Height / 2) - (simpleOpenGlControl.ClientRectangle.Width * mercN / (2 * Math.PI));
}
public double getX(double lat, double lon)
{
return (lon + 180) * (simpleOpenGlControl.ClientRectangle.Width / 360);
}
However, when I run the application, nothing is drawn on the canvas. Any ideas? Additionally, the Lat/Lon coordinates are in pairs:
StartLon="-118.1624997" StartLat="33.9512797" EndLon="-118.1664997" EndLat="33.9508597"
With the conversion code, I these XY coordinates for 36.0740197, -115.1051997
129.7896006
181.797964976478
If someone can point me in the right direction, I'd much appreciate it!

I was able to replicate your code and successfully draw lines between given points.
(Your lat/lon coordinate values of two points are too close to be noticeable but I was able to see a spec of line between them.)
Based on this, I wonder where you have put your first code listing (the part you setup viewport and draw lines) in your main program.
Try to render something simpler and see whether your OpenGL context is working correctly.

Related

Issue with trigonometry using Xamarin.Forms.Map.Position

I am working on a mobile app in C# using the Xamarin framework. I am trying to move a point by a fixed angle on a map like in the first part of the gif below. I believe I am using the right mathematical functions to compute the coordinates of the shifted points since in first part of the GIF, in GeoGebra, everything seems to be fine.
But when it comes to the actual in-app implementation, the results are quite weird : the angle is not consistent and the distance between the center and the points varies by moving the target.
The GIF showing the issue
I don't have a clue about what is wrong with the code. In the code below I use polylineOptions to draw the lines but I've tried with a Polygon and it displays the same results. Maybe it's because customMap.UserPin.Position returns the coordinates in Decimal Degree format (i.g. 34.00462, -4.512221) and the gap between two position is too small for a double.
Here are the two functions used to draw the lines.
// Add a cone's side to the variable coneLines
private void addConePolyline(double angle, CustomMap customMap, LatLng userPos)
{
// The coordinates of the end of the side to be drawn
LatLng conePoint = movePoint(angle, customMap.UserPin.Position, customMap.TargetPin.Position);
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeWidth(10f);
polylineOptions.InvokeColor(Android.Graphics.Color.Argb(240, 255, 20, 147)); // Pink
polylineOptions.Add(userPos);
polylineOptions.Add(conePoint);
// Add the line to coneLines
coneLines.Add(map.AddPolyline(polylineOptions));
}
// Moves a point by the given angle on a circle of center rotationCenter with respect to p
private LatLng movePoint(double angle, Position rotationCenter, Position initialPoint)
{
// Compute the components of the translation vector between rotationCenter and initialPoint
double dx = initialPoint.Latitude - rotationCenter.Latitude;
double dy = initialPoint.Longitude - rotationCenter.Longitude;
// Compute the moved point's position
double x = rotationCenter.Latitude + Math.Cos(angle) * dx - Math.Sin(angle) * dy;
double y = rotationCenter.Longitude + Math.Sin(angle) * dx + Math.Cos(angle) * dy;
LatLng res = new LatLng(x, y);
return res;
}
I hope someone can help me with this!
Thank you.

Find the required angle of rotation to move from point A to point B

I want to make a bot that will walk along points from two coordinates (X and Y), I have the coordinates of the character, his rotation angle (1-180 / (- 1) - (-180)), and the required point where he is should get there. How do I know if the angle of rotation is necessary for the person to look directly at the point?
I have been sitting with this for a long time, and my head refuses to think at all, I tried to solve this by creating an angle between the radius vector, but nothing came of it.
public static double GetRotation(Point Destination)
{
double cos = Destination.Y / Math.Sqrt(Destination.X * Destination.X + Destination.Y * Destination.Y);
double angle = Math.Acos(cos);
return angle;
}
With regard to this problem, I would suggest generally using tan rather than cos due to the fact to get the angle between two point (X and y), the difference in the points provide the opposite and adjacent sides of the triangle for the calculation, rather than adjacent and hypotenuse.
If the player is at position X,Y, and the new position to walk to is X2,Y2, the equation to calculate the relative angle between them should be tan-1((Y2-Y)/(X2-X)) with regards to the X plane.
I think the below implementation should work for your example (though you may need to subtract the current player angle the player is facing to get the difference):
public static double GetRotation(Point source, Point Destination)
{
double tan = (Destination.Y - source.Y) / (Destination.X -source.X);
double angle = Math.Atan(tan) * (180/Math.PI) // Converts to degrees from radians
return angle;
}
Apologies for the formatting, am currently on mobile.
dx = Destination.X - Person.X;
dy = Destination.Y - Person.Y;
directionangle = Math.Atan2(dy, dx); // in radians!
degrees = directionangle * 180 / Math.PI;

Draw 3D arc given start, middle, and endpoint

I am trying to make a user-defined arc with the Helix 3D toolkit. The user selects 3 points on the arc (start, middle, end) and the program finds the center of the circle and draws the arc from start to end. My problem is I'm not good at math and I am having problems making this work. My main problem is getting the start and end angles and having it draw arcs of all sizes accurately. Any help is appreciated. Here is my code:
private void Draw_Arc(object sender, MouseButtonEventArgs e)
{
linept = new List<Point3D>();
linept.Add(startPoint);
linept.Add(endPoint);
linept.Add((Point3D)GetPoints(e));
LinesVisual3D line = new LinesVisual3D();
line.Thickness = 2;
line.Color = Colors.Blue;
line.Points = linept;
port.Children.Add(line);
double startAngle, sweepAngle;
Point3D center = GetCenterOfArc(linept.ElementAt(0), linept.ElementAt(1), linept.ElementAt(2));
GetAngles(linept.ElementAt(0), linept.ElementAt(1), linept.ElementAt(2), out startAngle, out sweepAngle);
circle = new PieSliceVisual3D();
double RadiusX = Math.Abs(startPoint.X - center.X);
double RadiusY = Math.Abs(startPoint.Y - center.Y);
circle.Center = center;
if (RadiusX >= RadiusY)
circle.OuterRadius = RadiusX;
else
circle.OuterRadius = RadiusY;
circle.InnerRadius = circle.OuterRadius + 3;
circle.StartAngle = (180 / Math.PI * Math.Atan2(startPoint.Y - circle.Center.Y, startPoint.X - circle.Center.X));
circle.EndAngle = (180 / Math.PI * Math.Atan2(linept.ElementAt(2).Y - circle.Center.Y, linept.ElementAt(2).X - circle.Center.X));
port.Children.Add(circle);
}
I think that you have to know the center of the circle in order to know the starting and ending angle of the arc.
Say that you just have three points, and you want to find a circle that goes through all three, you basically have three equations with three variables:
(x-x0)^2 + (y-y0)^2 = R^2
(x-x1)^2 + (y-y1)^2 = R^2
(x-x2)^2 + (y-y2)^2 = R^2
Solving that can get a little tricky if you try to program that on your own and have average knowledge in math, but you can do it fairly easily using matrices. Read here for a bit information.
After you've solved the three equations, you should have X, Y, R.
X and Y will be the center point of the circle, and R - it's radius.
Now, as far as I remember, they count the arc's degrees starting from the positive X axis, going upwards. So you would need to calculate the angle between two lines - the line that stretches between the center to your floating point, and the line that stretches from your center point to the "limitless" right. You may just Google "calculate angle between two lines". Repeating that process for both your starting point and your ending point, will give each their respective entering/exiting angle.
The middle point isn't really used anymore, but the radius is. You just set it to be the radius and you're good to go.
I haven't really implemented anything - just giving you a fair direction. (and I bet that there's a much cleaner and nicer-to-work-with solution)

How to do the inverse Mercator projection

Here is some code I started writing, the Mercator projection is based on this answer.
using System;
using System.Drawing;
namespace CoordinatesTool
{
public class GeoPoint
{
public double Longitude { get; set; }
public double Latitude { get; set; }
public string ToString()
{
return Latitude + "," + Longitude;
}
public PointF ToMercator(int width, int height)
{
var x = (float)((Longitude + 180) * width / 360);
var latRadians = Latitude * Math.PI / 180;
var yTransformed = Math.Log(Math.Tan((latRadians / 2) + (Math.PI / 4)));
var yScaled = (float)((height / 2.0) - (width * yTransformed / (2 * Math.PI)));
return new PointF(x, yScaled);
}
public static GeoPoint FromMercator(PointF point, int width, int height)
{
return FromMercator(point.X, point.Y, width, height);
}
public static GeoPoint FromMercator(double x, double y, int width, int height)
{
// No clue what to do here
}
}
}
My goal is to use this utility class in a WinForms application. I'm using it with this map:
http://en.wikipedia.org/wiki/File:Mercator-projection.jpg (width: 2048, height: 1588).
The Mercator inversion is working quite well (however, I suspect it's not very accurate in the arctic/antarctic reagions).
But the inverse Mercator projection really leaves me puzzled. I played around with the solution proposed in another question, but couldn't get anywhere. Especially I don't understand the Gudermannian (and inverse) function, the DEGREES_PER_RADIAN and RADIANS_PER_DEGREE constants, and how I should convert the y value into a latitude to call the GudermannianInv() function with.
EDIT: Here is how I tried how to do the inverse projection:
Starting with yScaled (parameter y in FromMercator function):
var yTransformed = 2 * Math.PI * (height / 2.0 - yScaled) / width;
var latRadians = Math.Arctan(Math.Pow(Math.E, yTransformed) - Math.PI / 4) / 2;
// ...
Here are some bits of what you seek:
radians * degrees/radians == degrees : degrees_per_radian is just a way of expressing degrees/radians in 'English' rather than in 'maths'. radians_per_degree is left as an exercise for the reader. So these two constants represent the numbers you use when converting between angles in degrees and angles in radians.
Looking at the code you've posted, you have the lines to convert latRadians to y. It looks straightforward to implement code to invert those operations. You need to 'un'-scale and 'un'-transform 'yScaled'. For example, treating the line (part):
yScaled = ((height / 2.0) - (width * yTransformed / (2 * Math.PI)))
as a mathematical equation, you need to solve for yTransformed in terms of yScaled.
As for the Gudermannian, the question you refer to implements that in one line of code and the inverse Gudermannian in 3 lines. The Gudermannian is just a way of transforming a circular measurement (such as a measurement in degrees or radians) into a linear measurement (such as one in centimetres on a chart to be published on paper). In particular, and pertinent here, the Gudermannian will transform a latitude into a linear distance from 0.
EDIT
OK, so looking a bit closer, in your original code you transform the latitude from an angular measurement into a linear one with the line:
yTransformed = Math.Log(Math.Tan((latRadians / 2) + (Math.PI / 4)))
I think that you should probably replace this with a call to the inverse Gudermannian, as indicated in the answer to the question you link to. I suspect that your home-brewed transformation is stumbling over extreme points in the tan/arctan functions. You would then use the direct Gudermannian in the un-transformation of course.

Rotation of vector graphics

I have an application for drawing and editing vector graphics in WinForms
I have images, rectangles, ellipses, regions etc. and I know how to resize them by mouse move. But I don't know how to rotate them by mouse move.
I draw objects into Graphics.
I've tried this, but it didn't work.
g.TranslateTransform((float)(this.Rectangle.X + this.Rectangle.Width / 2), (float)(this.Rectangle.Y + this.Rectangle.Height / 2));
g.RotateTransform(this.Rotation);
g.TranslateTransform(-(float)(this.Rectangle.X + this.Rectangle.Width / 2), -(float)(this.Rectangle.Y + this.Rectangle.Height / 2));
//g.TranslateTransform(-(float)(rect.X + rect.Width / 2), -(float)(rect.Y + rect.Height / 2));
g.DrawImage(img, rect);
g.ResetTransform();
This didn't work, because I don't know how to find corners of objects in new (rotated) position, so I'm not able to resize that...
You need to apply high school trigonometry. There are lots of articles if you google "graphics.drawimage rotation"
But to start with, you should NOT be transforming the Graphics object itself. You are just looking to get the new bounding box of your image. To do this:
Take the bounding box of the image centered on the origin. Remember this is defined as three points for the benefit of DrawImage(Image, Point[])
Point[] boundingBox = { new Point(-width /2, -height/2),
new Point(width/2, -height/2),
new Point(-width/2, height/2) };
Use trig to rotate it. Feed each point through the following function:
Point rotatePointAroundOrigin(Point point, float angleInDegrees) {
float angle = angleInDegrees * Math.PI/180; // get angle in radians
return new Point( point.X * Math.Cos(angle) - point.Y * Math.Sin(angle),
point.X * Math.Sin(angle) + point.Y * Math.Cos(angle));
}
Translate the boundind box to where it has to go. Add the width/2 and height/2 to each of its points, plus whatever extra amount you want.
Call DrawImage(image, boundingBox)

Categories