I am working on a C# script to do some mapping. I have one function I am working on where I am given a latitude and longitude, a distance, and an angle of rotation.
I then want to create a rectangle, that has the width of the distance passed in (length is dependant on something else), this square would have the latitude and longitude point at it's centre, and would be rotated any angle from 0 - 360.
The distance double that is passed in can either be in feet, or in meters, and the units are determined using the isMetric boolean.
The problem I believe has to do with my formula's that I am using because when it draws the square from the 4 points, the size of the square is too large. As well the angle of rotation seems to be set at 45 degrees when you pass it a angle of 0.0.
Here is what I have so far:
Parameters: Latitude, Longitude (in decimal format), (Double) distance, (double) angle
double diagonal = Math.Sqrt(Math.Pow((distance / 2), 2) * 2); //a^2 + b^2 = d^2
if (isMetric) //Convert meters to km.
{
diagonal = diagonal / 1000;
}
else //Convert feet to km.
{
diagonal = diagonal * 0.0003048;
}
MessageBox.Show("Diagonal: " + diagonal, "DEBUG"); //DEBUG
double pt1_lat = latDistance(diagonal * Math.Sin(angle), latitude);
double pt1_long = longDistance(diagonal * Math.Cos(angle), latitude, longitude);
double pt2_lat = latDistance(diagonal * Math.Cos(angle), latitude);
double pt2_long = longDistance(-diagonal * Math.Sin(angle), latitude, longitude);
double pt3_lat = latDistance(-diagonal * Math.Sin(angle), latitude);
double pt3_long = longDistance(-diagonal * Math.Cos(angle), latitude, longitude);
double pt4_lat = latDistance(-diagonal * Math.Cos(angle), latitude);
double pt4_long = longDistance(diagonal * Math.Sin(angle), latitude, longitude);
The remaining methods are below:
private double latDistance(double distance, double latitude)
{
return latitude + degToRad(distance / EARTH_RADIUS);
}
private double longDistance(double distance, double latitude, double longitude)
{
return longitude + degToRad(distance / EARTH_RADIUS / Math.Cos(latitude));
}
private double degToRad(double degrees)
{
return (degrees * Math.PI) / 180;
}
public double radToDeg(double radians)
{
return (180.0 * radians) / Math.PI;
}
Any help is greatly appreciated :)
First of all I assume you have a "small" square with respect to EARTH radius. In this case if distance is the length of the side and angle is the rotation angle, you should compute dx,dy which are the cartesian coordinates of one vertex with respect to the center of the square:
dx = 0.5 * Math.Sqrt(2.0) * distance * Math.Cos(angle+0.25*Math.PI);
dy = 0.5 * Math.Sqrt(2.0) * distance * Math.Sin(angle+0.25*math.PI);
(here I assume that angle is in radians, otherwise you should convert it)
The cartesian coordinates of the other 3 vertices are, respectively: (-dy,dx), (-dx,-dy), (dy,-dx)
To convert the cartesian coordinates to (longitude,latitude) you can use your formulas:
pt_lat = latDistance(latitude,dy)
pt_long = longDistance(dx,latitude,longitude)
Related
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.
I've been trying to get this code to work properly for the past hour and I almost got it complete. Everything works, but the float verticalDegrees.
In Detail Question: How do I get this code working so it returns XYZ from horizontal degrees, vertical degrees, radius and origin?
This link helped me, but it's missing Z coordinate
This is what I have so far:
private float[] DegreesToXYZ(float horizonalDegrees, float verticalDegrees, float radius, float[] origin)
{
float[] xyz = new float[3];
double radiansH = horizonalDegrees * Math.PI / 180.0;
double radiansV = verticalDegrees * Math.PI / 180.0;
xyz[1] = (float)Math.Cos(radiansH) * radius + origin[1];
xyz[0] = (float)Math.Sin(-radiansH) * radius + origin[0];
double deltaXY = Math.Sqrt(origin[0] * origin[0] + origin[1] * origin[1]);
xyz[2] = (float)Math.Atan2(origin[2], deltaXY);
return xyz;
}
This method converts spherical coordinates into cartesian coordinates:
private static double[] DegreesToXYZ(double radius, double theta, double phi, double[] originXYZ)
{
theta *= Math.PI / 180;//converting degress into radians
phi *= Math.PI / 180;//converting degress into radians
double[] xyz = new double[3];
xyz[0] = originXYZ[0] + radius * Math.Cos(theta) * Math.Sin(phi);//x
xyz[1] = originXYZ[1] + radius * Math.Sin(theta) * Math.Sin(phi);//y
xyz[2] = originXYZ[2] + radius * Math.Cos(phi);//z
return xyz;
}
Where theta is the 'horizontal' or 'azimuth' angle (angle from the x-axis in the x-y plane), and phi is the 'inclination' (angle from the positive
z axis) or 'vertical' angle.The radius is the distance to a given point (x,y,z) in cartesian coordinates.
Seems you have spherical coordinates and want to get Cartesian coordinates. In this case
x = x0 + r * Cos(fi) * Sin(theta)
y = y0 + r * Sin(fi) * Sin(theta)
z = z0 + r * Cos(theta)
Here fi is your "horizontal angle", theta is "vertical angle", x0..z0 are origin coordinates
I have a point expressed in lat/long
Position louvreMuseum = new Position( 48.861622, 2.337474 );
and I have a radius value expressed in meters. I need to check if another point, also expressed in lat/long, is inside the circle.
If I were on a flat surface I can simply use the formula
(x - center_x)^2 + (y - center_y)^2 <= radius^2
as deeply explained in these SO answer.
However as per the latitude/longitude usage I can not use that formula because of the spherical nature of the planet.
How can I calculate a distance from any given point to the center to be compared with the radius?
Function to calculate the distance between two coordinates (converted to C# from this answer):
double GetDistance(double lat1, double lon1, double lat2, double lon2)
{
var R = 6371; // Radius of the earth in km
var dLat = ToRadians(lat2-lat1);
var dLon = ToRadians(lon2-lon1);
var a =
Math.Sin(dLat/2) * Math.Sin(dLat/2) +
Math.Cos(ToRadians(lat1)) * Math.Cos(ToRadians(lat2)) *
Math.Sin(dLon/2) * Math.Sin(dLon/2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
double ToRadians(double deg)
{
return deg * (Math.PI/180);
}
If the distance between the two points is less than the radius, then it is within the circle.
I have a mission to calculate point on a Map. I have the start point, the angle and the distance from the point. How can I do it ? I search a lot I found something but it doesn't work good - I mean it it doesn't calculate the correct point. Thank's all.
My try :
public Point MesPoint(double x1, double x2, double y1, double y2, double distance, double x) // X is the angle
{
double xEndP, yEndP;
var angularDistance = distance / c_EarthRadiusInKilometers; // angular distance in radians
var lat = ToRadian(y2);
var lon = ToRadian(x2);
var angel = ToRadian(x);
double latRadians = Math.Asin((Math.Sin(lat) * Math.Cos(angularDistance)) + (Math.Cos(lat) * Math.Sin(angularDistance) * Math.Cos(angel)));
double lngRadians = Math.Atan2(
Math.Sin(angel) * Math.Sin(angularDistance) * Math.Cos(lat),
Math.Cos(angularDistance) - (Math.Sin(lat) * Math.Sin(latRadians)));
double lon1 = (lon + lngRadians + Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180º
yEndP = ToDegrees(latRadians);
xEndP = ToDegrees(lon1);
return (new Point(xEndP, yEndP));
}
I'm looking for a bit of math help. I have a game were a 2D heightmap is generated and then stretched over a sphere using a length/direction formula. Now I need to know how to calculate the height between 2 points on my heightmap.
What I know:
The array that holds the heightmap
The angle in radians to my object
how many points there are on the heightmap
My problem look somewhat like so:
image
more images
The red and blue lines are the 2 heightmap points, and the light blue is where I'd like to calculate the height at.
Here's my current code to do it, but it doesn't work to well.
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double offset = MathHelper.TwoPi / (heightmap.Length - 1);
//total brainfart attempt
double lowerAngle = offset * angle;
double upperAngle = offset * angle + offset;
//find heights
double height1 = heightmap[(int)lowerAngle];
double height2 = heightmap[(int)upperAngle];
//find offset angle
double u = angle - lowerAngle / (upperAngle - lowerAngle);
//return the height
return height1 + (height1 - height2) * u;
}
from my vegetation code, this seems to work okay, but is to rough to use for units and such, as they jump up/down as they move, due to it using only 1 heightmap point.
double[] hMap = planet.getHeightMap();
double i = hMap.Length / (Math.PI * 2);
this.height = hMap[(int)(angle * i)];
EDIT: example at end based on additional question info
Sounds to me like a linear interpolation - if you look at it from a 2d point of view, you've got two points:
(x1, y1) = point one on heightmap
(x2, y2) = point two on heightmap
and one point somewhere between (x1,x2) at an unknown height:
pu = (xu, yu)
A generic formula for LERP is:
pu = p0 + (p1 - p0) * u
where:
p0 = first value
p1 = second value
u = % where your unknown point lies between (p0,p1)
Here, we'll say p0 == y2 and p1 == y1. Now we need to determine "how far" the unknown point is between x1 and x2 - if you know the angles to the two heightmap points, this is easy:
u = ang(xu) - ang(x1) / (ang(x2) - ang(x1))
Alternatively, you could project your angle out to Max(y1,y2) and get the "unknown x pos" that way, then calculate the above.
So, let's try a contrived example:
p1 = point one in map = (1,2) therefore ang(p1) ~ 57 degrees
p2 = point two in map = (2,4) therefore ang(p2) ~ 114 degrees
note that here, the "x axis" is along the surface of the sphere, and the "y-axis" is the distance away from the center.
pu = object location = py #angle 100 degrees ~ 1.74 radians
px = (1.74 rad - 1 rad ) / (2 rad - 1 rad) = 0.74 / 1.0 = 0.74 => 74%
py = y0 + (y1 - y0) * u
= 2 + (4 - 2) * 0.74
= 2.96
Hopefully I didn't drop or misplace a sign there somewhere... :)
Ok, your example code - I've tweaked it a bit, here's what I've come up with:
First, let's define some helpers of my own:
public static class MathHelper
{
public const double TwoPi = Math.PI * 2.0;
public static double DegToRad(double deg)
{
return (TwoPi / 360.0) * deg;
}
public static double RadToDeg(double rad)
{
return (360.0 / TwoPi) * rad;
}
// given an upper/lower bounds, "clamp" the value into that
// range, wrapping over to lower if higher than upper, and
// vice versa
public static int WrapClamp(int value, int lower, int upper)
{
return value > upper ? value - upper - 1
: value < lower ? upper - value - 1
: value;
}
}
Our Test setup:
void Main()
{
var random = new Random();
// "sea level"
var baseDiameter = 10;
// very chaotic heightmap
heightmap = Enumerable
.Range(0, 360)
.Select(_ => random.NextDouble() * baseDiameter)
.ToArray();
// let's walk by half degrees, since that's roughly how many points we have
for(double i=0;i<360;i+=0.5)
{
var angleInDegrees = i;
var angleInRads = MathHelper.DegToRad(i);
Console.WriteLine("Height at angle {0}°({1} rad):{2} (using getheight:{3})",
angleInDegrees,
angleInRads,
heightmap[(int)angleInDegrees],
getheight(angleInRads));
}
}
double[] heightmap;
And our "getheight" method:
// assume: input angle is in radians
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double dTheta = MathHelper.TwoPi / (heightmap.Length);
// our "offset" will be how many dThetas we are
double offset = angle / dTheta;
// Figure out two reference points in heightmap
// THESE MAY BE THE SAME POINT, if angle ends up
// landing on a heightmap index!
int lowerAngle = (int)offset;
int upperAngle = (int)Math.Round(
offset,
0,
MidpointRounding.AwayFromZero);
// find closest heightmap points to angle, wrapping
// around if we go under 0 or over max
int closestPointIndex = MathHelper.WrapClamp(
lowerAngle,
0,
heightmap.Length-1);
int nextPointIndex = MathHelper.WrapClamp(
upperAngle,
0,
heightmap.Length-1);
//find heights
double height1 = heightmap[closestPointIndex];
double height2 = heightmap[nextPointIndex];
// percent is (distance from angle to closest angle) / (angle "step" per heightmap point)
double percent = (angle - (closestPointIndex * dTheta)) / dTheta;
// find lerp height = firstvalue + (diff between values) * percent
double lerp = Math.Abs(height1 + (height2 - height1) * percent);
// Show what we're doing
Console.WriteLine("Delta ang:{0:f3}, Offset={1:f3} => compare indices:[{2}, {3}]",
dTheta,
offset,
closestPointIndex,
nextPointIndex);
Console.WriteLine("Lerping {0:p} between heights {1:f4} and {2:f4} - lerped height:{3:f4}",
percent,
height1,
height2,
lerp);
return lerp;
}