I need to calculate the angle in degrees between two points for my own Point class, Point a shall be the center point.
Method:
public float getAngle(Point target) {
return (float) Math.toDegrees(Math.atan2(target.x - x, target.y - y));
}
Test 1: // returns 45
Point a = new Point(0, 0);
System.out.println(a.getAngle(new Point(1, 1)));
Test 2: // returns -90, expected: 270
Point a = new Point(0, 0);
System.out.println(a.getAngle(new Point(-1, 0)));
How can i convert the returned result into a number between 0 and 359?
you could add the following:
public float getAngle(Point target) {
float angle = (float) Math.toDegrees(Math.atan2(target.y - y, target.x - x));
if(angle < 0){
angle += 360;
}
return angle;
}
by the way, why do you want to not use a double here?
I started with johncarls solution, but needed to adjust it to get exactly what I needed.
Mainly, I needed it to rotate clockwise when the angle increased. I also needed 0 degrees to point NORTH. His solution got me close, but I decided to post my solution as well in case it helps anyone else.
I've added some additional comments to help explain my understanding of the function in case you need to make simple modifications.
/**
* Calculates the angle from centerPt to targetPt in degrees.
* The return should range from [0,360), rotating CLOCKWISE,
* 0 and 360 degrees represents NORTH,
* 90 degrees represents EAST, etc...
*
* Assumes all points are in the same coordinate space. If they are not,
* you will need to call SwingUtilities.convertPointToScreen or equivalent
* on all arguments before passing them to this function.
*
* #param centerPt Point we are rotating around.
* #param targetPt Point we want to calcuate the angle to.
* #return angle in degrees. This is the angle from centerPt to targetPt.
*/
public static double calcRotationAngleInDegrees(Point centerPt, Point targetPt)
{
// calculate the angle theta from the deltaY and deltaX values
// (atan2 returns radians values from [-PI,PI])
// 0 currently points EAST.
// NOTE: By preserving Y and X param order to atan2, we are expecting
// a CLOCKWISE angle direction.
double theta = Math.atan2(targetPt.y - centerPt.y, targetPt.x - centerPt.x);
// rotate the theta angle clockwise by 90 degrees
// (this makes 0 point NORTH)
// NOTE: adding to an angle rotates it clockwise.
// subtracting would rotate it counter-clockwise
theta += Math.PI/2.0;
// convert from radians to degrees
// this will give you an angle from [0->270],[-180,0]
double angle = Math.toDegrees(theta);
// convert to positive range [0-360)
// since we want to prevent negative angles, adjust them now.
// we can assume that atan2 will not return a negative value
// greater than one partial rotation
if (angle < 0) {
angle += 360;
}
return angle;
}
Based on Saad Ahmed's answer, here is a method that can be used for any two points.
public static double calculateAngle(double x1, double y1, double x2, double y2)
{
double angle = Math.toDegrees(Math.atan2(x2 - x1, y2 - y1));
// Keep angle between 0 and 360
angle = angle + Math.ceil( -angle / 360 ) * 360;
return angle;
}
The javadoc for Math.atan(double) is pretty clear that the returning value can range from -pi/2 to pi/2. So you need to compensate for that return value.
Why is everyone complicating this?
The only problem is Math.atan2( x , y)
The corret answer is Math.atan2( y, x)
All they did was mix the variable order for Atan2 causing it to reverse the degree of rotation.
All you had to do was look up the syntax
https://www.google.com/amp/s/www.geeksforgeeks.org/java-lang-math-atan2-java/amp/
angle = Math.toDegrees(Math.atan2(target.x - x, target.y - y));
now for orientation of circular values to keep angle between 0 and 359 can be:
angle = angle + Math.ceil( -angle / 360 ) * 360
If you want the "bearing" degrees from north, so:
Direction
Degees
North
0
North East
45
East
90
South East
135
South
180
South West
-135
West
-95
North West
-45
you can do this:
public static final double RAD_360_DEG = Math.PI * 360d / 180d;
public static final double RAD_180_DEG = Math.PI * 180d / 180d;
public static final double RAD_90_DEG = Math.PI * 90d / 180d;
/**
* #return The angle from north from p1 to p2. Returns (in radians) -180 to 180, with 0 as north.
*/
public static double getAngleBearing(double p1x, double p1y, double p2x, double p2y) {
double result = Math.atan2(p2y - p1y, p2x - p1x) + RAD_90_DEG;
if (result > RAD_180_DEG) {
result = result - RAD_360_DEG;
}
return result;
}
double bearingAngle = Math.toDegrees(getAngleBearing(...));
my realization:
private double searchAngle(Point posOne, Point posTwo) {
int sumPos = (posOne.x * posTwo.x) + (posOne.y * posTwo.y);
double moduleOne = Math.sqrt( (posOne.x * posOne.x) + (posOne.y * posOne.y) );
double moduleTwo = Math.sqrt( (posTwo.x * posTwo.x) + (posTwo.y * posTwo.y) );
return Math.toDegrees( Math.acos( sumPos / (Math.abs( moduleOne ) * Math.abs( moduleTwo )) ) );
}
Input:
posOne: (x = 50, y = 43)
posTwo: (x = 12, y = 42)
Output is
33.35907305958513
in degrees.
What about something like :
angle = angle % 360;
Related
I have this function were I want to add a distance in meters (x and y) at a given angle to existing coordinates (lat/long):
* Calculates a new GPS coordinate from given coordinates and a given movement distances in meters
*
* #param lat0 the latitude to move from
* #param long0 the longitude to move from
* #param dx the distance to move on the x axis in meters. Use positive values to move to the east(right) and negative values to move to the west(left)
* #param dy the distance to move on the y axis in meters. Use positive values to move to the north (up) and negative values to move to the south(down)
* #return a new double array containing the target coordinates: [latitude, longitude]
*/
public double[] calculateCoordinatesAfterMove(double lat0, double long0, double dx, double dy) {
double lat = lat0 + (180 / Math.PI) * (dy / 6378137);
double lon = long0 + (180 / Math.PI) * (dx / 6378137) / Math.cos(Math.PI / 180.0 * lat0);
return new double[]{lat, lon};
}
public double[] calculateCoodinatesAfterMove(Waypoint w, double dx, double dy, double angle) {
return calculateCoordinatesAfterMove(w, dx * Math.cos(angle * Math.PI / 180), dy * Math.sin(angle * Math.PI / 180));
}
public double[] calculateCoordinatesAfterMove(double lat0, double long0, double dx, double dy, double angle) {
return calculateCoordinatesAfterMove(lat0, long0, dx * Math.cos(angle * Math.PI / 180), dy * Math.sin(angle * Math.PI / 180));
}
I also have a function that calculates the bearing between two coordinates and I checked that this function is correct.
The problem is that the function above does not create waypoints with the specified angle.
Here is an example call where I specify an angle of 64:
double[] resultMove = coordinateCalculator.calculateCoordinatesAfterMove(48.993268432102354, 8.395133104531464, 10, 5, 64);
System.out.println(resultMove[0] + ", " +resultMove[1]);
System.out.println("Calculated angle: " + coordinateCalculator.calculateAngleBetweenWaypoints(
48.993268432102354, 8.395133104531464, resultMove[0], resultMove[1]
));
which gives me an calculated waypoint of:
48.993308802123806, 8.395193120821242
and a calculated angle between this and the starting point of 44.2XXX degrees which is correct (checked it here: https://www.movable-type.co.uk/scripts/latlong.html)
Latitudes and longitudes are always passed as shown in the example which should be radian. Correct me if this kind of representation is degree:)
Could someone help me why my function does not return a waypoint in the desired angle?
I have a Java class that creates a virtual screen (let's call it a map) that can be translated, resized, and rotated.
When I rotate it, however, it only rotates it around (0, 0).
To transform the point to the screen you first rotate it, then resize it, then translate it.
private double dx; //offset in x and y
private double dy;
private double t; //rotation (radians)
private double sx; //scale of x and y
private double sy;
public double[] toScreen(double x, double y) //takes (x, y) on the map and gives (x1, y1) for the screen
{
double[] xy = {x, y};
if(t != 0)
{
double distance = Math.hypot(xy[0], xy[1]);
double theta = Math.atan2(xy[1], xy[0]) + t;
xy[0] = Math.cos(theta)*distance;
xy[1] = Math.sin(theta)*distance;
}
xy[0] *= sx;
xy[1] *= sy;
xy[0] += dx;
xy[1] += dy;
return xy;
}
to set the rotation or change it, you manipulate the variable t, but it rotates on (0, 0).
If I make a method that takes in (x, y) to rotate around like public void changeRotation(double t, double x, double y).
I want (x, y) to be map coordinates. What would the method look like and can you explain what it does?
If I understood correctly this is what you need :
/**
* #param point point (x,y) of the coordinates to be rotated
* #param center point (x,y) of the center (pivot) coordinates
* #param angle in radians
* #return point (x,y) of the new (translated) coordinates
*/
static Point2D.Double rotateAPoint(Point2D.Double point, Point2D.Double center, double angle){
double newX = center.x + Math.cos(angle) * (point.x - center.x) -
Math.sin(angle) * (point.y-center.y) ;
double newY = center.y + Math.sin(angle) * (point.x - center.x) +
Math.cos(angle) * (point.y - center.y) ;
return new Point2D.Double(newX, newY);
}
Try with
Point2D.Double point = new Point2D.Double(200,100);
Point2D.Double center = new Point2D.Double(100,100);
double angle = Math.PI/2 ; //90 degress
System.out.println(rotateAPoint(point, center, angle) );
System.out.println(rotateAPoint(point, center, -angle));
If you prefer to use double[] :
/**
* #param point (x,y) of the coordinates to be rotated
* #param center (x,y) of the center (pivot) coordinates
* #param angle in radians
* #return (x,y) of the new (translated) coordinates
*/
static double[] rotateAPoint(double[] point, double[] center, double angle){
double newX = center[0] + Math.cos(angle) * (point[0] - center[0]) -
Math.sin(angle) * (point[0]-center[0]) ;
double newY = center[1] + Math.sin(angle) * (point[1] - center[1]) +
Math.cos(angle) * (point[1] - center[1]) ;
return new double[]{newX, newY};
}
Explanation about the math here
Imagine I have drawn a circle with center coordinates (cx,cy) on the screen and a random point (A) is selected on the circle.
By having the the coordinates of point A, I need to find the angle of (a).
Update:
I have tried using the following formula:
Math.toDegrees(Math.asin(((x - cx) / radius).toDouble()))
which is actually the reverse of (the circle is created by feeding angles to this one):
x = radius * Math.sin(Math.toRadians(angle.toDouble())) + cx
y = radius * Math.cos(Math.toRadians(angle.toDouble())) + cy
But since the y coordinate is not present in the formula the answer could be wrong.
If you know the cartesian coordinates of the point A(x,y), then you can find the angle theta by converting it into polar coordinates as below:
double theta = Math.toDegrees(Math.atan2(y - cy, x - cx));
This formula works if your X axis is in 0 degrees, otherwise you need to consider the offset.
I think the method you are looking for i Math.atan2 which computes the angle to an x and y cordinate. I have now modified the code to adjust for putting 0 degrees downwards. I have also flipped the y-axis to put 0, 0 cordinate in the upper left corner (screen coordinates), and adjusted degrees above 180 to be reported as negative degrees:
public double theta(int cx, int cy, int x, int y)
{
double angle = Math.toDegrees(Math.atan2(cy - y, x - cx)) + 90;
return angle <= 180? angle: angle - 360;
}
A small test to verify some angles...
#Test
public void test()
{
assertThat(theta(50, 50, 60, 50), is(90.0));
assertThat(theta(50, 50, 50, 60), is(0.0));
assertThat(theta(50, 50, 40, 50), is(-90.0));
assertThat(theta(50, 50, 50, 40), is(180.0));
}
You can find the tangent angles and add to 90 or substruct from 270 that angle and find the result, I believe. I design the code like your drawing. You can make it more generic, I guess.
You have 4 area:
0 degree to 90 degree
90 degree to 180 degree
90 degree to -90(270) degree
-90(270) degree to 0(360) degree
Code:
public static double findAngle(double x, double y,
double cx, double cy, double radius){
double beta, alfa;
double distanceX = Math.abs(Math.abs(x) - Math.abs(cx));
double distanceY = Math.abs(Math.abs(y) - Math.abs(cy));
// check the point is on the circle or not
// with euchlid
if (radius != Math.sqrt(x * x + y * y)) {
return -1;
}
if (x >= cx && y <= cy) {
// find tangent
beta = Math.atan(distanceY / distanceX);
alfa = 90 - beta;
return alfa;
}
// 90-180 -> second area
else if (x >= cx && y >= cy) {
beta = Math.atan(distanceY / distanceX);
alfa = 90 + beta;
return alfa;
}
// 180 - -90 -> third area
else if (x <= cx && y >= cy) {
beta = Math.atan(distanceY / distanceX);
alfa = 270 - beta;
return alfa;
}
// -90 - 0 -> forth area
else if (x <= cx && y <= cy) {
beta = Math.atan(distanceY / distanceX);
alfa = 270 + beta;
if (alfa == 360) {
alfa = 0;
}
return alfa;
}
else {
return -1;
}
}
The main catch with Atan2 is the center point. If your center is offset (Cx, Cy) and not at the origin and you want to find the arc angle between (X1, Y1) and (X2, Y2) then I would suggest this approach:
double startAngle = Math.Atan2(Y1-Cy, X1-Cx);
double endAngle = Math.Atan2(Y2-Cy, X2-Cx);
double ArcAngle = endangle - startAngle;
I am having a user draw a line on the screen, there is a start point and a end point. If the user extends over a certain angle I change the position of the endpoint so the user cannot extend beyond the specified angle. However it seems that when I cacluate the angle the user is drawing and make suer it is not over MAX ANGLE, and set the angle to the MAX ANGLE, there is a difference. This can be seen by when I draw the line, once i get to a certain angle, the line jumps and locks at the MAX ANGLE, but there shouldnt be any jump, it should be smooth, like the line ran into a invisible barrier. This could just be me though, my PosX and PosY are floats.
private void CheckAngle() {
double adj = Math.abs(PosX - PosX2);
double c1 = adj;
double c2 = Math.abs(PosY - PosY2);
double hyp = Math.hypot(c1, c2);
double angle = Math.cos((adj/hyp));
angle = angle * 100;
if (angle > MAX_ANGLE) {
double opp = (Math.tan(MAX_ANGLE) * Math.abs(PosX - PosX2));
if (PosY > PosY2) {
PosY2 =(float) (PosY - opp);
} else {
PosY2 =(float) (PosY + opp);
}
}
}
My answer was a combination of using radians, as well as unsing
Math.acos() & Math.atan()
so the final code looks like this
private void CheckAngle() {
double adj = Math.abs(PosX - PosX2);
double opp = Math.abs(PosY - PosY2);
double hyp = Math.sqrt((adj*adj)+(opp*opp));
double angle = Math.acos((adj/hyp));
angle = angle * 100;
angle = Math.toRadians(angle);
if (angle > MAX_ANGLE) {
opp = (Math.atan(MAX_ANGLE) * adj);
if (PosY > PosY2) {
PosY2 =(float) (PosY - opp);
} else {
PosY2 =(float) (PosY + opp);
}
}
}
Here's the conversion:
final double MAX_ANGLE = Math.toRadians(80);
Note that this is identical to saying:
final double MAX_ANGLE = 80 * Math.PI / 180;
I need to calculate the angle in degrees between two points for my own Point class, Point a shall be the center point.
Method:
public float getAngle(Point target) {
return (float) Math.toDegrees(Math.atan2(target.x - x, target.y - y));
}
Test 1: // returns 45
Point a = new Point(0, 0);
System.out.println(a.getAngle(new Point(1, 1)));
Test 2: // returns -90, expected: 270
Point a = new Point(0, 0);
System.out.println(a.getAngle(new Point(-1, 0)));
How can i convert the returned result into a number between 0 and 359?
you could add the following:
public float getAngle(Point target) {
float angle = (float) Math.toDegrees(Math.atan2(target.y - y, target.x - x));
if(angle < 0){
angle += 360;
}
return angle;
}
by the way, why do you want to not use a double here?
I started with johncarls solution, but needed to adjust it to get exactly what I needed.
Mainly, I needed it to rotate clockwise when the angle increased. I also needed 0 degrees to point NORTH. His solution got me close, but I decided to post my solution as well in case it helps anyone else.
I've added some additional comments to help explain my understanding of the function in case you need to make simple modifications.
/**
* Calculates the angle from centerPt to targetPt in degrees.
* The return should range from [0,360), rotating CLOCKWISE,
* 0 and 360 degrees represents NORTH,
* 90 degrees represents EAST, etc...
*
* Assumes all points are in the same coordinate space. If they are not,
* you will need to call SwingUtilities.convertPointToScreen or equivalent
* on all arguments before passing them to this function.
*
* #param centerPt Point we are rotating around.
* #param targetPt Point we want to calcuate the angle to.
* #return angle in degrees. This is the angle from centerPt to targetPt.
*/
public static double calcRotationAngleInDegrees(Point centerPt, Point targetPt)
{
// calculate the angle theta from the deltaY and deltaX values
// (atan2 returns radians values from [-PI,PI])
// 0 currently points EAST.
// NOTE: By preserving Y and X param order to atan2, we are expecting
// a CLOCKWISE angle direction.
double theta = Math.atan2(targetPt.y - centerPt.y, targetPt.x - centerPt.x);
// rotate the theta angle clockwise by 90 degrees
// (this makes 0 point NORTH)
// NOTE: adding to an angle rotates it clockwise.
// subtracting would rotate it counter-clockwise
theta += Math.PI/2.0;
// convert from radians to degrees
// this will give you an angle from [0->270],[-180,0]
double angle = Math.toDegrees(theta);
// convert to positive range [0-360)
// since we want to prevent negative angles, adjust them now.
// we can assume that atan2 will not return a negative value
// greater than one partial rotation
if (angle < 0) {
angle += 360;
}
return angle;
}
Based on Saad Ahmed's answer, here is a method that can be used for any two points.
public static double calculateAngle(double x1, double y1, double x2, double y2)
{
double angle = Math.toDegrees(Math.atan2(x2 - x1, y2 - y1));
// Keep angle between 0 and 360
angle = angle + Math.ceil( -angle / 360 ) * 360;
return angle;
}
The javadoc for Math.atan(double) is pretty clear that the returning value can range from -pi/2 to pi/2. So you need to compensate for that return value.
Why is everyone complicating this?
The only problem is Math.atan2( x , y)
The corret answer is Math.atan2( y, x)
All they did was mix the variable order for Atan2 causing it to reverse the degree of rotation.
All you had to do was look up the syntax
https://www.google.com/amp/s/www.geeksforgeeks.org/java-lang-math-atan2-java/amp/
angle = Math.toDegrees(Math.atan2(target.x - x, target.y - y));
now for orientation of circular values to keep angle between 0 and 359 can be:
angle = angle + Math.ceil( -angle / 360 ) * 360
If you want the "bearing" degrees from north, so:
Direction
Degees
North
0
North East
45
East
90
South East
135
South
180
South West
-135
West
-95
North West
-45
you can do this:
public static final double RAD_360_DEG = Math.PI * 360d / 180d;
public static final double RAD_180_DEG = Math.PI * 180d / 180d;
public static final double RAD_90_DEG = Math.PI * 90d / 180d;
/**
* #return The angle from north from p1 to p2. Returns (in radians) -180 to 180, with 0 as north.
*/
public static double getAngleBearing(double p1x, double p1y, double p2x, double p2y) {
double result = Math.atan2(p2y - p1y, p2x - p1x) + RAD_90_DEG;
if (result > RAD_180_DEG) {
result = result - RAD_360_DEG;
}
return result;
}
double bearingAngle = Math.toDegrees(getAngleBearing(...));
my realization:
private double searchAngle(Point posOne, Point posTwo) {
int sumPos = (posOne.x * posTwo.x) + (posOne.y * posTwo.y);
double moduleOne = Math.sqrt( (posOne.x * posOne.x) + (posOne.y * posOne.y) );
double moduleTwo = Math.sqrt( (posTwo.x * posTwo.x) + (posTwo.y * posTwo.y) );
return Math.toDegrees( Math.acos( sumPos / (Math.abs( moduleOne ) * Math.abs( moduleTwo )) ) );
}
Input:
posOne: (x = 50, y = 43)
posTwo: (x = 12, y = 42)
Output is
33.35907305958513
in degrees.
What about something like :
angle = angle % 360;