Geo circle to rectangle coordinates - java

Given the input, center latitude, center longitude and radius in kilometers, I want to get the coordinates for the rectangle that contains this circle (northeast and southwest lat/lng).
Should I write the method myself? Even though I'm afraid not to account for some things as my math is rusty. Or can I find a ready implementation for java? I have google maps sdk in my project but I couldn't find anything useful there.

I suppose your square radius is much smaller than the earth's radius (6371 km)
so that you can safely ignore the earth's curvature.
Then the math is quite easy:
// center of square
double latitudeCenter = ...; // in degrees
double longitudeCenter = ...; // in degrees
double radius = ...; // in km
double RADIUS_EARTH = 6371; // in km
// north-east corner of square
double latitudeNE = latitudeCenter + Math.toDegrees(radius / RADIUS_EARTH);
double longitudeNE = longitudeCenter + Math.toDegrees(radius / RADIUS_EARTH / Math.cos(Math.toRadians(latitudeCenter)));
// south-west corner of square
double latitudeSW = latitudeCenter - Math.toDegrees(radius / RADIUS_EARTH);
double longitudeSW = longitudeCenter - Math.toDegrees(radius / RADIUS_EARTH / Math.cos(Math.toRadians(latitudeCenter)));
Example:
Center(lat,lon) at 48.00,11.00 and radius 10 km
will give NE-corner(lat,lon) at 48.09,11.13 and SW-corner(lat,lon) at 47.91,10.87.
And here is how to do it with LatLng
and Bounds of the google-maps-services-java API:
public static final double RADIUS_EARTH = 6371;
public static Bounds boundsOfCircle(LatLng center, double radius) {
Bounds bounds = new Bounds();
double deltaLat = Math.toDegrees(radius / RADIUS_EARTH);
double deltaLng = Math.toDegrees(radius / RADIUS_EARTH / Math.cos(Math.toRadians(center.lat)));
bounds.northeast = new LatLng(center.lat + deltaLat, center.lng + deltaLng);
bounds.southwest = new LatLng(center.lat - deltaLat, center.lng - deltaLng);
return bounds;
}

Related

How to get minimum/maximum latitude and longitude from center and given radius?

I need to calculate a square area that is equivalent to different zoom heights.
To achieve this I am using the scale in meters as radius and the latitude and longitude of where I click (LatLng center).
Doing it this way I get a square area, but it's too imprecise as you zoom out.
`
public static LatLngBounds getBboxFromCenterWithRadius(double radius, LatLng center){
double earthRadius = 6371000;
double longMin = center.longitude -
Math.toDegrees(radius/earthRadius/Math.cos(Math.toRadians(center.latitude)));
double longMax= center.longitude +
Math.toDegrees(radius/earthRadius/Math.cos(Math.toRadians(center.latitude)));
double latMax = center.latitude + Math.toDegrees(radius/earthRadius);
double latMin = center.latitude - Math.toDegrees(radius/earthRadius);
LatLng sw = new LatLng(latMin,longMin);
LatLng ne = new LatLng(latMax,longMax);
return new LatLngBounds(sw, ne);
}
`

Java Swing - Calculating what angle the mouse position is to the center of the screen

I'm making a 2D topdown view shooter game with Java Swing. I want to calculate what angle the mouse pointer is compared to the center of the screen so some of my Sprites can look toward the pointer and so that I can create projectiles described by an angle and a speed. Additionally If the pointer is straight above the middle of the screen, I want my angle to be 0°, if straight to its right, 90°, if straight below 180°, and straight left 270°.
I have made a function to calculate this:
public static float calculateMouseToPlayerAngle(float x, float y){
float mouseX = (float) MouseInfo.getPointerInfo().getLocation().getX();
float mouseY = (float)MouseInfo.getPointerInfo().getLocation().getY();
float hypotenuse = (float) Point2D.distance(mouseX, mouseY, x, y);
return (float)(Math.acos(Math.abs(mouseY-y)/hypotenuse)*(180/Math.PI));
}
The idea behind it is that I calculate the length of the hypotenuse then the length of the side opposite of the angle in question. The fraction of the 2 should be a cos of my angle, so taking that result's arc cos then multiplying that by 180/Pi should give me the angle in degrees. This does work for above and to the right, but straight below returns 0 and straight left returns 90. That means that I currently have 2 problems where the domain of my output is only [0,90] instead of [0,360) and that it's mirrored through the y (height) axis. Where did I screw up?
You can do it like this.
For a window size of 500x500, top left being at point 0,0 and bottom right being at 500,500.
The tangent is the change in Y over the change in X of two points. Also known as the slope it is the ratio of the sin to cos of a specific angle. To find that angle, the arctan (Math.atan or Math.atan2) can be used. The second method takes two arguments and is used below.
BiFunction<Point2D, Point2D, Double> angle = (c,
m) -> (Math.toDegrees(Math.atan2(c.getY() - m.getY(),
c.getX() - m.getX())) + 270)%360;
BiFunction<Point2D, Point2D, Double> distance = (c,
m) -> Math.hypot(c.getY() - m.getY(),
c.getX() - m.getX());
int screenWidth = 500;
int screenHeight = 500;
int ctrY = screenHeight/2;
int ctrX = screenWidth/2;
Point2D center = new Point2D.Double(ctrX,ctrY );
Point2D mouse = new Point2D.Double(ctrX, ctrY-100);
double straightAbove = angle.apply(center, mouse);
System.out.println("StraightAbove: " + straightAbove);
mouse = new Point2D.Double(ctrX+100, ctrY);
double straightRight = angle.apply(center, mouse);
System.out.println("StraightRight: " + straightRight);
mouse = new Point2D.Double(ctrX, ctrY+100);
double straightBelow = angle.apply(center, mouse);
System.out.println("StraightBelow: " + straightBelow);
mouse = new Point2D.Double(ctrX-100, ctrY);
double straightLeft = angle.apply(center, mouse);
System.out.println("Straightleft: " + straightLeft);
prints
StraightAbove: 0.0
StraightRight: 90.0
StraightBelow: 180.0
Straightleft: 270.0
I converted the radian output from Math.atan2 to degrees. For your application it may be more convenient to leave them in radians.
Here is a similar Function to find the distance using Math.hypot
BiFunction<Point2D, Point2D, Double> distance = (c,m) ->
Math.hypot(c.getY() - m.getY(),
c.getX() - m.getX());

Triangle Coordinate Geometry

I have a triangle ABC inscribed in a circle. Point B is located in the centre of the circle, A and C are two points on the circle.
Given
Given AB (length)
Given coords of A and B
Given angle B (angle ABC)
Needed
Find coords of C
What I know
AB = BC, both are radius's
What I am using this for
I am making a basic 3D java game, for android. This will be used for looking left and right, so if you click on the right part of the screen the objects will move around you by adding one degree to angle B.
The code I tried for finding coords of C
rect.get(index)[5] = (int) ((di * Math.cos(Math.toRadians(angle-90)))+.5);
rect.get(index)[6] = (int) ((di * Math.sin(Math.toRadians(angle-90)))+.5);
rect.get(index)[5] = shapes x coord
rect.get(index)[6] = shapes y coord
di = radius length
angle = angle B
and I added the .5 so that when the coord is truncated it is rounded.
My complete code
double di = distance(playerx, playery, rect.get(index)[5], rect.get(index)[6]);
double side1 = di;
System.out.println("Side1: "+ side1);
double side2 = side1;
System.out.println("Side2: "+ side2);
double side3 = distance(rect.get(index)[5], rect.get(index)[6], playerx, playery+di);
System.out.println("Side3: "+ side3);
double angle = ((side1*side1)+(side2*side2)-(side3*side3));
angle = angle/(2*side1*side2);
angle = Math.acos(angle)*(180/Math.PI);
System.out.println("Angle: "+angle);
if(playerx > rect.get(index)[5]){
if(lookdirection.equals("left")){
angle += 5;
}
if(lookdirection.equals("right")){
angle -= 5;
}
}
else{
if(lookdirection.equals("left")){
angle -= 5;
}
if(lookdirection.equals("right")){
angle += 5;
}
}
System.out.println("Angle: "+angle);
rect.get(index)[5] = -(di * Math.cos(Math.toRadians(angle-90)));
rect.get(index)[6] = -(di * Math.sin(Math.toRadians(angle-90)));
di = distance(playerx, playery, rect.get(index)[5], rect.get(index)[6]);
side1 = di;
System.out.println("Side1: "+ side1);
side2 = side1;
System.out.println("Side2: "+ side2);
side3 = distance(rect.get(index)[5], rect.get(index)[6], playerx, playery+di);
System.out.println("Side3: "+ side3);
angle = ((side1*side1)+(side2*side2)-(side3*side3));
angle = angle/(2*side1*side2);
angle = Math.acos(angle)*(180/Math.PI);
System.out.println("Angle: "+angle);
repaint();
}
The angles are now working but the X and Y coords
but
rect.get(index)[5] = -(di * Math.cos(Math.toRadians(angle-90)));
rect.get(index)[6] = -(di * Math.sin(Math.toRadians(angle-90)));
are getting very large/ small values. They should stay 'di' distance away from point B.
UPDATED
It seems that this is a geometry problem!
I haven't tested your code but I guess what you want is not
rect.get(index)[6] = (int) ((di * Math.sin(Math.toRadians(angle-90)))+.5);
but
rect.get(index)[6] = (int) (-(di * Math.sin(Math.toRadians(angle-90)))+.5);
Try to visualise what you are doing and check the identities under symmetry, shifts, and periodicity here. The image is turning and reflecting along the x axis with what you are doing now. You have to take care of rounding issues as well. If coordinates are close to 0 the image might not move.
My suggestion is to keep your coordinates as doubles and only round them when you are going to render them into a pixel without overwriting their value

Getting lon/lat from pixel coords in Google Static Map

I have a JAVA project to do using Google Static Maps and after hours and hours working, I can't get a thing working, I will explain everything and I hope someone will be able to help me.
I am using a static map (480pixels x 480pixels), the map's center is lat=47, lon=1.5 and the zoom level is 5.
Now what I need is being able to get lat and lon when I click a pixel on this static map. After some searches, I found that I should use Mercator Projection (right ?), I also found that each zoom level doubles the precision in both horizontal and vertical dimensions but I can't find the right formula to link pixel, zoom level and lat/lon...
My problem is only about getting lat/lon from pixel, knowing the center's coords and pixel and the zoom level...
Thank you in advance !
Use the Mercator projection.
If you project into a space of [0, 256) by [0,256]:
LatLng(47,=1.5) is Point(129.06666666666666, 90.04191318303863)
At zoom level 5, these equate to pixel coordinates:
x = 129.06666666666666 * 2^5 = 4130
y = 90.04191318303863 * 2^5 = 2881
Therefore, the top left of your map is at:
x = 4130 - 480/2 = 4070
y = 2881 - 480/2 = 2641
4070 / 2^5 = 127.1875
2641 / 2^5 = 82.53125
Finally:
Point(127.1875, 82.53125) is LatLng(53.72271667491848, -1.142578125)
Google-maps uses tiles for the map to efficient divide the world into a grid of 256^21 pixel tiles. Basically the world is made of 4 tiles in the lowest zoom. When you start to zoom you get 16 tiles and then 64 tiles and then 256 tiles. It basically a quadtree. Because such a 1d structure can only flatten a 2d you also need a mercantor projection or a conversion to WGS 84. Here is a good resource Convert long/lat to pixel x/y on a given picture. There is function in Google Maps that convert from lat-long pair to pixel. Here is a link but it says the tiles are 128x128 only: http://michal.guerquin.com/googlemaps.html.
Google Maps V3 - How to calculate the zoom level for a given bounds
http://www.physicsforums.com/showthread.php?t=455491
Based on the math in Chris Broadfoot's answer above and some other code on Stack Overflow for the Mercator Projection, I got this
public class MercatorProjection implements Projection {
private static final double DEFAULT_PROJECTION_WIDTH = 256;
private static final double DEFAULT_PROJECTION_HEIGHT = 256;
private double centerLatitude;
private double centerLongitude;
private int areaWidthPx;
private int areaHeightPx;
// the scale that we would need for the a projection to fit the given area into a world view (1 = global, expect it to be > 1)
private double areaScale;
private double projectionWidth;
private double projectionHeight;
private double pixelsPerLonDegree;
private double pixelsPerLonRadian;
private double projectionCenterPx;
private double projectionCenterPy;
public MercatorProjection(
double centerLatitude,
double centerLongitude,
int areaWidthPx,
int areaHeightPx,
double areaScale
) {
this.centerLatitude = centerLatitude;
this.centerLongitude = centerLongitude;
this.areaWidthPx = areaWidthPx;
this.areaHeightPx = areaHeightPx;
this.areaScale = areaScale;
// TODO stretch the projection to match to deformity at the center lat/lon?
this.projectionWidth = DEFAULT_PROJECTION_WIDTH;
this.projectionHeight = DEFAULT_PROJECTION_HEIGHT;
this.pixelsPerLonDegree = this.projectionWidth / 360;
this.pixelsPerLonRadian = this.projectionWidth / (2 * Math.PI);
Point centerPoint = projectLocation(this.centerLatitude, this.centerLongitude);
this.projectionCenterPx = centerPoint.x * this.areaScale;
this.projectionCenterPy = centerPoint.y * this.areaScale;
}
#Override
public Location getLocation(int px, int py) {
double x = this.projectionCenterPx + (px - this.areaWidthPx / 2);
double y = this.projectionCenterPy + (py - this.areaHeightPx / 2);
return projectPx(x / this.areaScale, y / this.areaScale);
}
#Override
public Point getPoint(double latitude, double longitude) {
Point point = projectLocation(latitude, longitude);
double x = (point.x * this.areaScale - this.projectionCenterPx) + this.areaWidthPx / 2;
double y = (point.y * this.areaScale - this.projectionCenterPy) + this.areaHeightPx / 2;
return new Point(x, y);
}
// from https://stackoverflow.com/questions/12507274/how-to-get-bounds-of-a-google-static-map
Location projectPx(double px, double py) {
final double longitude = (px - this.projectionWidth/2) / this.pixelsPerLonDegree;
final double latitudeRadians = (py - this.projectionHeight/2) / -this.pixelsPerLonRadian;
final double latitude = rad2deg(2 * Math.atan(Math.exp(latitudeRadians)) - Math.PI / 2);
return new Location() {
#Override
public double getLatitude() {
return latitude;
}
#Override
public double getLongitude() {
return longitude;
}
};
}
Point projectLocation(double latitude, double longitude) {
double px = this.projectionWidth / 2 + longitude * this.pixelsPerLonDegree;
double siny = Math.sin(deg2rad(latitude));
double py = this.projectionHeight / 2 + 0.5 * Math.log((1 + siny) / (1 - siny) ) * -this.pixelsPerLonRadian;
Point result = new org.opencv.core.Point(px, py);
return result;
}
private double rad2deg(double rad) {
return (rad * 180) / Math.PI;
}
private double deg2rad(double deg) {
return (deg * Math.PI) / 180;
}
}
Here's a unit test for the original answer
public class MercatorProjectionTest {
#Test
public void testExample() {
// tests against values in https://stackoverflow.com/questions/10442066/getting-lon-lat-from-pixel-coords-in-google-static-map
double centerLatitude = 47;
double centerLongitude = 1.5;
int areaWidth = 480;
int areaHeight = 480;
// google (static) maps zoom level
int zoom = 5;
MercatorProjection projection = new MercatorProjection(
centerLatitude,
centerLongitude,
areaWidth,
areaHeight,
Math.pow(2, zoom)
);
Point centerPoint = projection.projectLocation(centerLatitude, centerLongitude);
Assert.assertEquals(129.06666666666666, centerPoint.x, 0.001);
Assert.assertEquals(90.04191318303863, centerPoint.y, 0.001);
Location topLeftByProjection = projection.projectPx(127.1875, 82.53125);
Assert.assertEquals(53.72271667491848, topLeftByProjection.getLatitude(), 0.001);
Assert.assertEquals(-1.142578125, topLeftByProjection.getLongitude(), 0.001);
// NOTE sample has some pretty serious rounding errors
Location topLeftByPixel = projection.getLocation(0, 0);
Assert.assertEquals(53.72271667491848, topLeftByPixel.getLatitude(), 0.05);
// the math for this is wrong in the sample (see comments)
Assert.assertEquals(-9, topLeftByPixel.getLongitude(), 0.05);
Point reverseTopLeftBase = projection.projectLocation(topLeftByPixel.getLatitude(), topLeftByPixel.getLongitude());
Assert.assertEquals(121.5625, reverseTopLeftBase.x, 0.1);
Assert.assertEquals(82.53125, reverseTopLeftBase.y, 0.1);
Point reverseTopLeft = projection.getPoint(topLeftByPixel.getLatitude(), topLeftByPixel.getLongitude());
Assert.assertEquals(0, reverseTopLeft.x, 0.001);
Assert.assertEquals(0, reverseTopLeft.y, 0.001);
Location bottomRightLocation = projection.getLocation(areaWidth, areaHeight);
Point bottomRight = projection.getPoint(bottomRightLocation.getLatitude(), bottomRightLocation.getLongitude());
Assert.assertEquals(areaWidth, bottomRight.x, 0.001);
Assert.assertEquals(areaHeight, bottomRight.y, 0.001);
}
}
If you're (say) working with aerial photography, I feel like the algorithm doesn't take into account the stretching effect of the mercator projection, so it might lose accuracy if your region of interest isn't relatively close to the equator. I guess you could approximate it by multiplying your x coordinates by cos(latitude) of the center?
It seems worth mentioning that you can actually have the google maps API give you the latitudinal & longitudinal coordinates from pixel coordinates.
While it's a little convoluted in V3 here's an example of how to do it. (NOTE: This is assuming you already have a map and the pixel vertices to be converted to a lat&lng coordinate):
let overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.onAdd = function() {};
overlay.onRemove = function() {};
overlay.setMap(map);
let latlngObj = overlay.fromContainerPixelToLatLng(new google.maps.Point(pixelVertex.x, pixelVertex.y);
overlay.setMap(null); //removes the overlay
Hope that helps someone.
UPDATE: I realized that I did this two ways, both still utilizing the same way of creating the overlay (so I won't duplicate that code).
let point = new google.maps.Point(628.4160703464878, 244.02779437950872);
console.log(point);
let overlayProj = overlay.getProjection();
console.log(overlayProj);
let latLngVar = overlayProj.fromContainerPixelToLatLng(point);
console.log('the latitude is: '+latLngVar.lat()+' the longitude is: '+latLngVar.lng());

java 3D rotation with quaternions

I have this method for rotating points in 3D using quaternions, but it seems not to work properly:
public static ArrayList<Float> rotation3D(ArrayList<Float> points, double angle, int xi, int yi, int zi)
{
ArrayList<Float> newPoints = new ArrayList<>();
for (int i=0;i<points.size();i+=3)
{
float x_old = points.get(i);
float y_old = points.get(i+1);
float z_old = points.get(i+2);
double w = Math.cos(angle/2.0);
double x = xi*Math.sin(angle/2.0);
double y = yi*Math.sin(angle/2.0);
double z = zi*Math.sin(angle/2.0);
float x_new = (float) ((1 - 2*y*y -2*z*z)*x_old + (2*x*y + 2*w*z)*y_old + (2*x*z-2*w*y)*z_old);
float y_new = (float) ((2*x*y - 2*w*z)*x_old + (1 - 2*x*x - 2*z*z)*y_old + (2*y*z + 2*w*x)*z_old);
float z_new = (float) ((2*x*z + 2*w*y)*x_old + (2*y*z - 2*w*x)*y_old + (1 - 2*x*x - 2*y*y)*z_old);
newPoints.add(x_new);
newPoints.add(y_new);
newPoints.add(z_new);
}
return newPoints;
}
If i make this call rotation3D(list, Math.toRadians(90), 0, 1, 0); where points is (0,0,10), the output is (-10.0, 0.0, 2.220446E-15), but it should be (-10,0,0), right? Could someone take a look at my code and tell me if is there somethig wrong?
Here are 4 screens that represent the initial position of my object, and 3 rotations with -90 degrees (the object is not properly painted, that's a GL issue, that i will work on later):
I haven't studied the code but what you get from it is correct: Assuming a left-handed coordinate system, when you rotate the point (0,0,10) 90 degrees around the y-axis (i.e. (0,1,0)) you end up with (-10,0,0).
If your coordinate system is right-handed I think you have to reverse the sign of the angle.

Categories

Resources