I already know how to find a point on the circumference of a circle based on an angle. The code I'm using to do so is below.
x = Math.sin(Math.toRadians(angle)) * radius;
y = Math.cos(Math.toRadians(angle)) * radius;
I'm trying to undo this process.
So far, I have this code, which only works fully for angles less than or equal to 90 degrees.
DecimalFormat df = new DecimalFormat("###.####");
angleFromX = normalize(
Double.parseDouble(
df.format(
Math.toDegrees(
Math.asin(
(x / radius)
)
)
)
)
);
angleFromY = normalize(
Double.parseDouble(
df.format(
Math.toDegrees(
Math.acos(
(y / radius)
)
)
)
)
);
And here's normalize method used above.
public static double normalize(double angle) {
angle %= 360;
if (angle < 0) {
angle = angle + 360;
}
return angle;
}
You mixed up sin and cos.
double x = Math.cos(Math.toRadians(angle)) * radius;
double y = Math.sin(Math.toRadians(angle)) * radius;
To convert back, use this formula:
double newRadius = Math.hypot(x, y);
double theta = Math.atan2(y,x);
double newAngle = Math.toDegrees(theta);
Based on the implementation, you may need to adjust your value of theta (angle).
If it's in Quadrant 2 or 3, add 180 degrees.
If it's in Quadrant 4, add 360 degrees.
Also you may need to add:
newAngle = (newAngle+360)%360
To keep the angle positive and between 0 and 360.
Related
I'm trying to generate a point within a radius and I'm getting incorrect values. Someone mind taking a look and telling me what I'm doing wrong for the longitude? This was a formulaic approach posted on a different question...
public static Location generateLocationWithinRadius(Location myCurrentLocation) {
return getLocationInLatLngRad(1000, myCurrentLocation);
}
protected static Location getLocationInLatLngRad(double radiusInMeters, Location currentLocation) {
double x0 = currentLocation.getLatitude();
double y0 = currentLocation.getLongitude();
Random random = new Random();
// Convert radius from meters to degrees
double radiusInDegrees = radiusInMeters / 111000f;
double u = random.nextDouble();
double v = random.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
double x = w * Math.cos(t);
double y = w * Math.sin(t);
double new_x = x / Math.cos(y0);
double new_y = y / Math.cos(x0);
double foundLatitude;
double foundLongitude;
boolean shouldAddOrSubtractLat = random.nextBoolean();
boolean shouldAddOrSubtractLon = random.nextBoolean();
if (shouldAddOrSubtractLat) {
foundLatitude = new_x + x0;
} else {
foundLatitude = x0 - new_x;
}
if (shouldAddOrSubtractLon) {
foundLongitude = new_y + y0;
} else {
foundLongitude = y0 - new_y;
}
Location copy = new Location(currentLocation);
copy.setLatitude(foundLatitude);
copy.setLongitude(foundLongitude);
return copy;
}
I should also say that for some reason the valid points yield a uniform line of coordinates when looking at them.
I think the latitude is processing correctly whereas the longitude is not.
Your code seems to be more or less based on an idea
which is presented at gis.stackexchange.com
and discussed some more there in this discussion
and in this discussion.
If we take a closer look at it based on those discussions then maybe it makes more sense.
To easily limit the values to a circle it uses the approach of randomizing a direction and a distance. First we get two random double values between 0.0 ... 1.0:
double u = random.nextDouble();
double v = random.nextDouble();
As the radius is given in meters and the calculations require degrees, it's converted:
double radiusInDegrees = radiusInMeters / 111000f;
The degrees vs. meters ratio of the equator is used here. (Wikipedia suggests 111320 m.)
To have a more uniform distribution of the random points the distance is compensated with a square root:
w = r * sqrt(u)
Otherwise there would be a statistical bias in the amount of points near the center vs. far from the center. The square root of 1 is 1 and 0 of course 0, so
multiplying the root of the random double by the intended max. radius always gives a value between 0 and the radius.
Then the other random double is multiplied by 2 * pi because there are 2 * pi radians in a full circle:
t = 2 * Pi * v
We now have an angle somewhere between 0 ... 2 * pi i.e. 0 ... 360 degrees.
Then the random x and y coordinate deltas are calculated with basic trigonometry using the random distance and random angle:
x = w * cos(t)
y = w * sin(t)
The [x,y] then points some random distance w away from the original coordinates towards the direction t.
Then the varying distance between longitude lines is compensated with trigonometry (y0 being the center's y coordinate):
x' = x / cos(y0)
Above y0 needs to be converted to radians if the cos() expects the angle as radians. In Java it does.
It's then suggested that these delta values are added to the original coordinates. The cos and sin are negative for half of the full circle's angles so just adding is fine. Some of the random points will be to the west from Greenwich and and south from the equator. There's no need to randomize
should an addition or subtraction be done.
So the random point would be at (x'+x0, y+y0).
I don't know why your code has:
double new_y = y / Math.cos(x0);
And like said we can ignore shouldAddOrSubtractLat and shouldAddOrSubtractLon.
In my mind x refers to something going from left to right or from west to east. That's how the longitude values grow even though the longitude lines go from south to north. So let's use x as longitude and y as latitude.
So what's left then? Something like:
protected static Location getLocationInLatLngRad(double radiusInMeters, Location currentLocation) {
double x0 = currentLocation.getLongitude();
double y0 = currentLocation.getLatitude();
Random random = new Random();
// Convert radius from meters to degrees.
double radiusInDegrees = radiusInMeters / 111320f;
// Get a random distance and a random angle.
double u = random.nextDouble();
double v = random.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
// Get the x and y delta values.
double x = w * Math.cos(t);
double y = w * Math.sin(t);
// Compensate the x value.
double new_x = x / Math.cos(Math.toRadians(y0));
double foundLatitude;
double foundLongitude;
foundLatitude = y0 + y;
foundLongitude = x0 + new_x;
Location copy = new Location(currentLocation);
copy.setLatitude(foundLatitude);
copy.setLongitude(foundLongitude);
return copy;
}
It is hard for me to provide you with a pure Android solution as I never used those API. However I am sure you could easily adapt this solution to generate a random point within a given radius from an existing point.
The problem is solved in a two dimensions space however it is easy to extend to support altitude as well.
Please have a look at the code below. It provides you with a LocationGeneratoras well as my own Location implementation and an unit test proving that it works.
My solution is based on solving the circle equation (x-a)^2 + (y-b)^2 = r^2
package my.test.pkg;
import org.junit.Test;
import java.util.Random;
import static org.junit.Assert.assertTrue;
public class LocationGeneratorTest {
private class Location {
double longitude;
double latitude;
public Location(double longitude, double latitude) {
this.longitude = longitude;
this.latitude = latitude;
}
}
private class LocationGenerator {
private final Random random = new Random();
Location generateLocationWithinRadius(Location currentLocation, double radius) {
double a = currentLocation.longitude;
double b = currentLocation.latitude;
double r = radius;
// x must be in (a-r, a + r) range
double xMin = a - r;
double xMax = a + r;
double xRange = xMax - xMin;
// get a random x within the range
double x = xMin + random.nextDouble() * xRange;
// circle equation is (y-b)^2 + (x-a)^2 = r^2
// based on the above work out the range for y
double yDelta = Math.sqrt(Math.pow(r, 2) - Math.pow((x - a), 2));
double yMax = b + yDelta;
double yMin = b - yDelta;
double yRange = yMax - yMin;
// Get a random y within its range
double y = yMin + random.nextDouble() * yRange;
// And finally return the location
return new Location(x, y);
}
}
#Test
public void shoulRandomlyGeneratePointWithinRadius () throws Exception {
LocationGenerator locationGenerator = new LocationGenerator();
Location currentLocation = new Location(20., 10.);
double radius = 5.;
for (int i=0; i < 1000000; i++) {
Location randomLocation = locationGenerator.generateLocationWithinRadius(currentLocation, radius);
try {
assertTrue(Math.pow(randomLocation.latitude - currentLocation.latitude, 2) + Math.pow(randomLocation.longitude - currentLocation.longitude, 2) < Math.pow(radius, 2));
} catch (Throwable e) {
System.out.println("i= " + i + ", x=" + randomLocation.longitude + ", y=" + randomLocation.latitude);
throw new Exception(e);
}
}
}
}
NOTE:
This is just a generic solution to obtain a random point inside a circle with the center in (a, b) and a radius of r that can be used to solve your problem and not a straight solution that you can use as such. You most likely will need to adapt it to your use case.
I believe this is a natural solution.
Regards
Kotlin version of Markus Kauppinen answer
fun Location.getRandomLocation(radius: Double): Location {
val x0: Double = longitude
val y0: Double = latitude
// Convert radius from meters to degrees.
// Convert radius from meters to degrees.
val radiusInDegrees: Double = radius / 111320f
// Get a random distance and a random angle.
// Get a random distance and a random angle.
val u = Random.nextDouble()
val v = Random.nextDouble()
val w = radiusInDegrees * sqrt(u)
val t = 2 * Math.PI * v
// Get the x and y delta values.
// Get the x and y delta values.
val x = w * cos(t)
val y = w * sin(t)
// Compensate the x value.
// Compensate the x value.
val newX = x / cos(Math.toRadians(y0))
val foundLatitude: Double
val foundLongitude: Double
foundLatitude = y0 + y
foundLongitude = x0 + newX
val copy = Location(this)
copy.latitude = foundLatitude
copy.longitude = foundLongitude
return copy
}
Longitude and Latitude uses ellipsoidal coordinates so for big radius (hundred meters) the error using this method would become sinificant. A possible trick is to convert to Cartesian coordinates, do the radius randomization and then transform back again to ellipsoidal coordinates for the long-lat. I have tested this up to a couple of kilometers with great success using this java library from ibm. Longer than that might work, but eventually the radius would fall off as the earth shows its spherical nature.
I need to get an accurate value of my android device's angle of rotation around Z-axis while the device is inclined (not lying flat).
I reached this angle using accelerometer sensor and calculated the angle using this code:
float[] values = event.values;
double x = values[0];
double y = values[1];
double z = values[2];
double mag = Math.sqrt(x * x + y * y + z * z);
// Normalize the accelerometer vector
x = x / mag;
y = y / mag;
z = z / mag;
int inclination = (int) Math.round(Math.toDegrees(Math.acos(z)));
if (inclination < 25 || inclination > 155) {
return Orientation.Error;
} else {
double rotation = Math.toDegrees(Math.atan2(x, y));
but when the devices rotates in a high speed this value becomes unreliable.
How can I fix this problem?
Thanks in advance,
I am almost done of getting the coordinates of a diagonal circle.
Here is what I have so far.
// Center point
double centerX;
double centerY;
double centerZ;
for (double degree = 0D; degree < 360D; degree = degree + 8D) {
double angle = degree * Math.PI / 180D;
// Difference from the center
double x = 1.5D * Math.cos(angle);
double y;
if (degree >= 0D && degree < 90D) {
y = degree / 90D;
} else if (degree >= 90D && degree < 180D) {
y = 1D - ((degree - 90D) / 90D);
} else if (degree >= 180D && degree < 270D) {
y = -1D * ((degree - 180D) / 90D);
} else {
y = -1D * (1D - ((degree - 270D) / 90D));
}
double z = 1.5D * Math.sin(angle);
// New point
double pointX = centerX + x;
double pointY = centerY + y;
double pointZ = centerZ + z;
}
Here is the output in a game.
It is not perfect because it creates some edges and it looks inefficient to me.
How do I correct it?
Is there a better way to do this?
This should look similar to what you have already, but it's simpler and smoother:
double y = 1.0D * Math.sin(angle);
Now, with these dimensions, the result is not quite a circle, but a stretched ellipse. If you want a circle, make sure the coefficients on the cosine and sine obey the Pythagorean Theorem. For example:
double x = 1.5D * Math.cos(angle);
double y = 0.9D * Math.sin(angle);
double z = 1.2D * Math.sin(angle);
These coefficients will ensure that x^2 + y^2 + z^2 is a constant for every angle. You can verify that this is true, given the identity cos^2 + sin^2 = 1. (The coefficient representing the hypotenuse should be attached to the coordinate that uses a different trig function than the other two.)
For the most maintainable code, you might find it better to assign (x, y, z) = (cos, sin, 0) and then apply a rotation matrix, or a sequence of rotation matrices, to the vector (x, y, z). This will be easier to read and harder to mess up, if you want to fine-tune the amount of rotation later.
I wanted to find all points of a circle with angle of 30 degree and radius r, when i am given center of circle (x,y) till we scan entire 360 degree.
Please let me know if i can use any java library related to this.
Code below :
I have written code as below :
<code>
double xpos = xPosition;
double ypos = yPosition;
double step = 2 * Math.PI / 20;
double radius = 50;
List<DevicePositions> listdevicePos = new ArrayList<DevicePositions>();
DevicePositions devicePos = null;
for (double theta = 0; theta < 2 * Math.PI; theta += step) {
double x = xpos + radius * Math.cos(theta);
double y = ypos - radius * Math.sin(theta);
devicePos = new DevicePositions();
devicePos.setxCoordinate(x);
devicePos.setyCoordinate(y);
listdevicePos.add(devicePos);
}
</code>
Thanks
Brijesh
im trying to make the player look at at specific point. What i have looks at the point but only if its in its field of vision. and i would like for it to look up and down as well
public static void lookAt(double x, double y, double z) {
double l;
double w;
double c;
l = x - playerX;
w = z - playerZ;
c = Math.sqrt(l * l + w + w);
double alpha1 = -Math.asin(l / c) / Math.PI * 180;
double alpha2 = Math.acos(w / c) / Math.PI * 180;
if (alpha2 > 180) {
playerYaw = 180 - (float) alpha1;
} else {
playerYaw = (float) alpha1;
}
}
i looked for a pitch algorithm but none seem to work.
Late answer but oh well, you can calculate the pitch and yaw with simple trigonometry if you can get a unit vector that represents the direction from your position to the point you want to look at.
To start you need to subtract the your position by the point and normalize the result, that will give us the directional vector(unit vector) that we need.
Now we can calculate the angles, the arc sine of Y will give us the the pitch angle, and the arc tan of X and Z will give us the yaw angle.
To understand why: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
and now you should have the pitch and yaw, don't forget to convert them to degrees!
here's some code:
public static void LookAt(double px, double py, double pz , EntityPlayer me)
{
double dirx = me.getPosition(0f).X - px;
double diry = me.getPosition(0f).Y - py;
double dirz = me.getPosition(0f).Z - pz;
double len = Math.sqrt(dirx*dirx + diry*diry + dirz*dirz);
dirx /= len
diry /= len;
dirz /= len;
double pitch = Math.asin(dir.y);
double yaw = Math.atan2(dir.z, dir.x);
//to degree
pitch = pitch * 180.0 / Math.PI;
yaw = yaw * 180.0 / Math.PI;
yaw += 90f;
me.rotationPitch = (float)pitch;
me.rotationYaw = (float)yaw;
}