Lat/Lng to Web Mercator Projection Issues - qt

I've got some simple code in a Qt application which converts from a latitude/longitude coordinate to a web mercator x/y position:
// Radius of the Earth in metres
const double EARTH_RADIUS = 20037508.34;
// Convert from a LL to a QPointF
QPointF GeoPoint::toMercator() {
double x = this->longitude() * EARTH_RADIUS / 180.0;
double y = log(tan((90.0 + this->latitude()) * PI / 360.0)) / (PI / 180.0);
y = y * EARTH_RADIUS / 180.0;
return QPointF(x, y);
}
// Convert from a QPointF to a LL
GeoPoint GeoPoint::fromMercator(const QPointF &pt) {
double lon = (pt.x() / EARTH_RADIUS) * 180.0;
double lat = (pt.y() / EARTH_RADIUS) * 180.0;
lat = 180.0 / PI * (2 * atan(exp(lat * PI / 180.0)) - PI / 2.0);
return GeoPoint(lat, lon);
}
I'm wanting to get the geographic position of a number of objects which are in metres distance away from a geographic origin, however either my lack of understanding or source code are not correct.
Consider the following:
GeoPoint pt1(54.253230, -3.006460);
QPointF m1 = pt1.toMercator();
qDebug() << m1;
// QPointF(-334678,7.21826e+06)
// Now I want to add a distance onto the mercator coordinates, i.e. 50 metres
m1.rx() += 50.0;
qDebug() << m1;
// QPointF(-334628,7.21826e+06)
// Take this back to a LL
GeoPoint pt1a = GeoPoint::fromMercator(m1);
qDebug() << pt1a.toString();
// "54.25323°, -3.00601°"
If I plot the two LL coordinates into Google Earth, they are not 50m apart as expected, they are about 29.3m apart.
I'm perplexed!

Related

How to convert a spherical velocity coordinates into cartesian

I have a velocity vector in altitude, longitude, altitude, I would like to convert it to Cartesian coordinates, vx,vy,vz. The format is from WGS84 standard.
here is the formula
//------------------------------------------------------------------------------
template <class T>
TVectorXYZ<T> WGS84::ToCartesian(T latitude, T longitude, T elevation)
//------------------------------------------------------------------------------
{
double sinlat, coslat;
double sinlon, coslon;
sincos_degree(latitude, sinlat, coslat);
sincos_degree(longitude, sinlon, coslon);
const double v = a / sqrt(1 - WGS84::ee * sinlat*sinlat);
TVectorXYZ<T> coord
(
static_cast<T>((v + elevation) * coslat * sinlon),
static_cast<T>(((1 - WGS84::ee) * v + elevation) * sinlat),
static_cast<T>((v + elevation) * coslat * coslon)
);
return coord;
}
OK based on your previous question and long comment flow lets assume your input is:
lon [rad], lat [rad], alt [m] // WGS84 position
vlon [m/s], vlat [m/s], alt [m/s] // speed in WGS84 lon,lat,alt directions but in [m/s]
And want output:
x,y,z // Cartesian position [m/s]
vx,vy,vz // Cartesian velocity [m/s]
And have valid transformation to Cartesian coordinates for positions at your disposal this is mine:
void WGS84toXYZ(double &x,double &y,double &z,double lon,double lat,double alt) // [rad,rad,m] -> [m,m,m]
{
const double _earth_a=6378137.00000; // [m] WGS84 equator radius
const double _earth_b=6356752.31414; // [m] WGS84 epolar radius
const double _earth_e=8.1819190842622e-2; // WGS84 eccentricity
const double _aa=_earth_a*_earth_a;
const double _ee=_earth_e*_earth_e;
double a,b,x,y,z,h,l,c,s;
a=lon;
b=lat;
h=alt;
c=cos(b);
s=sin(b);
// WGS84 from eccentricity
l=_earth_a/sqrt(1.0-(_ee*s*s));
x=(l+h)*c*cos(a);
y=(l+h)*c*sin(a);
z=(((1.0-_ee)*l)+h)*s;
}
And routine for normalize vector to unit size:
void normalize(double &x,double &y,double &z)
{
double l=sqrt(x*x+y*y+z*z);
if (l>1e-6) l=1.0/l;
x*=l; y*=l; z*=l;
}
Yes you can try to derive the formula lihe #MvG suggest but from your rookie mistakes I strongly doubt it would lead to successful result. Instead you can do this:
obtain lon,lat,alt direction vectors for your position (x,y,z)
that is easy just use some small step increment in WGS84 position convert to Cartesian substract and normalize to unit vectors. Let call these direction basis vectors U,V,W.
double Ux,Uy,Uz; // [m]
double Vx,Vy,Vz; // [m]
double Wx,Wy,Wz; // [m]
double da=1.567e-7; // [rad] angular step ~ 1.0 m in lon direction
double dl=1.0; // [m] altitide step 1.0 m
WGS84toXYZ( x, y, z,lon ,lat,alt ); // actual position
WGS84toXYZ(Ux,Uy,Uz,lon+da,lat,alt ); // lon direction Nort
WGS84toXYZ(Vx,Vy,Vz,lon,lat+da,alt ); // lat direction East
WGS84toXYZ(Wx,Wy,Wz,lon,lat ,alt+dl); // alt direction High/Up
Ux-=x; Uy-=y; Uz-=z;
Vx-=x; Vy-=y; Vz-=z;
Wx-=x; Wy-=y; Wz-=z;
normalize(Ux,Uy,Uz);
normalize(Vx,Vy,Vz);
normalize(Wx,Wy,Wz);
convert velocity from lon,lat,alt to vx,vy,vz
vx = vlon*Ux + vlat*Vx + valt*Wx;
vy = vlon*Uy + vlat*Vy + valt*Wy;
vz = vlon*Uz + vlat*Vz + valt*Wz;
Hope it is clear enough. As usual be careful about the units deg/rad and m/ft/km because units matters a lot.
Btw U,V,W basis vectors form NEH reference frame and in the same time are the direction derivates MvG is mentioning.
[Edit1] more precise conversions
//---------------------------------------------------------------------------
//--- WGS84 transformations ver: 1.00 ---------------------------------------
//---------------------------------------------------------------------------
#ifndef _WGS84_h
#define _WGS84_h
//---------------------------------------------------------------------------
// http://www.navipedia.net/index.php/Ellipsoidal_and_Cartesian_Coordinates_Conversion
//---------------------------------------------------------------------------
// WGS84(a,b,h) = (long,lat,alt) [rad,rad,m]
// XYZ(x,y,z) [m]
//---------------------------------------------------------------------------
const double _earth_a=6378137.00000; // [m] WGS84 equator radius
const double _earth_b=6356752.31414; // [m] WGS84 epolar radius
const double _earth_e=8.1819190842622e-2; // WGS84 eccentricity
//const double _earth_e=sqrt(1.0-((_earth_b/_earth_a)*(_earth_b/_earth_a)));
const double _earth_ee=_earth_e*_earth_e;
//---------------------------------------------------------------------------
const double kmh=1.0/3.6; // [km/h] -> [m/s]
//---------------------------------------------------------------------------
void XYZtoWGS84 (double *abh ,double *xyz ); // [m,m,m] -> [rad,rad,m]
void WGS84toXYZ (double *xyz ,double *abh ); // [rad,rad,m] -> [m,m,m]
void WGS84toXYZ_posvel(double *xyzpos,double *xyzvel,double *abhpos,double *abhvel); // [rad,rad,m],[m/s,m/s,m/s] -> [m,m,m],[m/s,m/s,m/s]
void WGS84toNEH (reper &neh ,double *abh ); // [rad,rad,m] -> NEH [m]
void WGS84_m2rad (double &da,double &db,double *abh); // [rad,rad,m] -> [rad],[rad] representing 1m angle step
void XYZ_interpolate (double *pt,double *p0,double *p1,double t); // [m,m,m] pt = p0 + (p1-p0)*t in ellipsoid space t = <0,1>
//---------------------------------------------------------------------------
void XYZtoWGS84(double *abh,double *xyz)
{
int i;
double a,b,h,l,n,db,s;
a=atanxy(xyz[0],xyz[1]);
l=sqrt((xyz[0]*xyz[0])+(xyz[1]*xyz[1]));
// estimate lat
b=atanxy((1.0-_earth_ee)*l,xyz[2]);
// iterate to improve accuracy
for (i=0;i<100;i++)
{
s=sin(b); db=b;
n=divide(_earth_a,sqrt(1.0-(_earth_ee*s*s)));
h=divide(l,cos(b))-n;
b=atanxy((1.0-divide(_earth_ee*n,n+h))*l,xyz[2]);
db=fabs(db-b);
if (db<1e-12) break;
}
if (b>0.5*pi) b-=pi2;
abh[0]=a;
abh[1]=b;
abh[2]=h;
}
//---------------------------------------------------------------------------
void WGS84toXYZ(double *xyz,double *abh)
{
double a,b,h,l,c,s;
a=abh[0];
b=abh[1];
h=abh[2];
c=cos(b);
s=sin(b);
// WGS84 from eccentricity
l=_earth_a/sqrt(1.0-(_earth_ee*s*s));
xyz[0]=(l+h)*c*cos(a);
xyz[1]=(l+h)*c*sin(a);
xyz[2]=(((1.0-_earth_ee)*l)+h)*s;
}
//---------------------------------------------------------------------------
void WGS84toNEH(reper &neh,double *abh)
{
double N[3],E[3],H[3]; // [m]
double p[3],xyzpos[3];
const double da=1.567e-7; // [rad] angular step ~ 1.0 m in lon direction
const double dl=1.0; // [m] altitide step 1.0 m
vector_copy(p,abh);
// actual position
WGS84toXYZ(xyzpos,abh);
// NEH
p[0]+=da; WGS84toXYZ(N,p); p[0]-=da;
p[1]+=da; WGS84toXYZ(E,p); p[1]-=da;
p[2]+=dl; WGS84toXYZ(H,p); p[2]-=dl;
vector_sub(N,N,xyzpos);
vector_sub(E,E,xyzpos);
vector_sub(H,H,xyzpos);
vector_one(N,N);
vector_one(E,E);
vector_one(H,H);
neh._rep=1;
neh._inv=0;
// axis X
neh.rep[ 0]=N[0];
neh.rep[ 1]=N[1];
neh.rep[ 2]=N[2];
// axis Y
neh.rep[ 4]=E[0];
neh.rep[ 5]=E[1];
neh.rep[ 6]=E[2];
// axis Z
neh.rep[ 8]=H[0];
neh.rep[ 9]=H[1];
neh.rep[10]=H[2];
// gpos
neh.rep[12]=xyzpos[0];
neh.rep[13]=xyzpos[1];
neh.rep[14]=xyzpos[2];
neh.orto(1);
}
//---------------------------------------------------------------------------
void WGS84toXYZ_posvel(double *xyzpos,double *xyzvel,double *abhpos,double *abhvel)
{
reper neh;
WGS84toNEH(neh,abhpos);
neh.gpos_get(xyzpos);
neh.l2g_dir(xyzvel,abhvel);
}
//---------------------------------------------------------------------------
void WGS84_m2rad(double &da,double &db,double *abh)
{
// WGS84 from eccentricity
double p[3],rr;
WGS84toXYZ(p,abh);
rr=(p[0]*p[0])+(p[1]*p[1]);
da=divide(1.0,sqrt(rr));
rr+=p[2]*p[2];
db=divide(1.0,sqrt(rr));
}
//---------------------------------------------------------------------------
void XYZ_interpolate(double *pt,double *p0,double *p1,double t)
{
const double mz=_earth_a/_earth_b;
const double _mz=_earth_b/_earth_a;
double p[3],r,r0,r1;
// compute spherical radiuses of input points
r0=sqrt((p0[0]*p0[0])+(p0[1]*p0[1])+(p0[2]*p0[2]*mz*mz));
r1=sqrt((p1[0]*p1[0])+(p1[1]*p1[1])+(p1[2]*p1[2]*mz*mz));
// linear interpolation
r = r0 +(r1 -r0 )*t;
p[0]= p0[0]+(p1[0]-p0[0])*t;
p[1]= p0[1]+(p1[1]-p0[1])*t;
p[2]=(p0[2]+(p1[2]-p0[2])*t)*mz;
// correct radius and rescale back
r/=sqrt((p[0]*p[0])+(p[1]*p[1])+(p[2]*p[2]));
pt[0]=p[0]*r;
pt[1]=p[1]*r;
pt[2]=p[2]*r*_mz;
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
However they require basic 3D vector math see here for equations:
Understanding 4x4 homogenous transform matrices
Take the formula you use to convert positions from geographic to Cartesian coordinates. That's some vector p(λ,φ,h) ∈ ℝ³, i.e. you turn latitude, longitude and altitude into a three-element vector of x,y,z coordinates. Now compute the partial derivatives of this formula with respect to the three parameters. You will get three vectors, which should be orthogonal to one another. The derivative with respect to longitude λ should be pointing locally east, the one with respect to latitude φ pointing north, the one with respect to altitude h pointing up. Multiply these vectors with the velocities you have to obtain a Cartesian velocity vector.
Observe how the units match: the position is in meters, the first two derivatives are meters per degree, and the velocity would be degrees per second. Or something else, perhaps miles and radians.
All of this is fairly easy for the sphere. For the WGS84 ellipsoid the position formula is a bit more involved, and that complexity will carry through the computation.

openGL (Qt) bind properly + Two rotation at the same time

I'm trying to get some experience in openGL, but now I'm facing "1.5" problems ;).
The first problem / question is how can I get a rotation in two directions "simultaneously"?
I want to draw a coordinate system which is movable on the x- and y-axis. But I'm only able to move on the x-axis or y-axis. I can't figure it out how to do both at the same time.
My other half problem is not really a problem but as you can see I'm binding my shaders all the time new when I move my mouse. Is there a better way how it could been done?
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
differencePostition.setX(event->x() - lastPosition.x());
differencePostition.setY(event->y() - lastPosition.y());
shaderProgram.removeAllShaders();
shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, "../Vector/yRotation.vert");
shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, "../Vector/CoordinateSystemLines.frag");
shaderProgram.link();
shaderProgram.bind();
shaderProgram.setAttributeValue("angle", differencePostition.x());
//shaderProgram.release();
//shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, "../Vector/xRotation.vert");
//shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, "../Vector/CoordinateSystemLines.frag");
//shaderProgram.link();
//shaderProgram.bind();
//shaderProgram.setAttributeValue("angle", differencePostition.y());
updateGL();
}
void GLWidget::mousePressEvent(QMouseEvent *event)
{
lastPosition = event->posF();
}
xRotation.vert
#version 330
in float angle;
const float PI = 3.14159265358979323846264;
void main(void)
{
float rad_angle = angle * PI / 180.0;
vec4 oldPosition = gl_Vertex;
vec4 newPosition = oldPosition;
newPosition.y = oldPosition.y * cos(rad_angle) - oldPosition.z * sin(rad_angle);
newPosition.z = oldPosition.y * sin(rad_angle) + oldPosition.z * cos(rad_angle);
gl_Position = gl_ModelViewProjectionMatrix * newPosition;
}
yRotation.vert
#version 330
in float angle;
const float PI = 3.14159265358979323846264;
void main(void)
{
float rad_angle = angle * PI / 180.0;
vec4 oldPosition = gl_Vertex;
vec4 newPosition = oldPosition;
newPosition.x = oldPosition.x * cos(rad_angle) + oldPosition.z * sin(rad_angle);
newPosition.z = oldPosition.z * cos(rad_angle) - oldPosition.x * sin(rad_angle);
gl_Position = gl_ModelViewProjectionMatrix * newPosition;
}
Rotation in more than one direction at the same time requires a combination of matrices ( commonly called a general rotation matrix )
There are several sites that show how this matrix is generated if you are more interested.
As to your second problem, the shaders are usually initialized in the init section.
Example: http://doc-snapshot.qt-project.org/5.0/qtopengl/cube-mainwidget-cpp.html
You only need to call shaderProgram.bind(); every time before you want to draw an object with your shader. Loading and linking is usually only done once in the initialization of your programm. Only call shaderProgram.setAttributeValue your mouseMoveEvent method.
EDIT
A quick way to solve your rotation problem is to write a shader that does both rotations one after the other. Add a second in variable and set both using the setAttributeValue method.
#version 330
in float angleX;
in float angleY;
const float PI = 3.14159265358979323846264;
void main(void)
{
float rad_angle_x = angleX * PI / 180.0;
vec4 oldPosition = gl_Vertex;
vec4 newPositionX = oldPosition;
newPositionX.y = oldPosition.y * cos(rad_angle_x) - oldPosition.z * sin(rad_angle_x);
newPositionX.z = oldPosition.y * sin(rad_angle_x) + oldPosition.z * cos(rad_angle_x);
float rad_angle_y = angleY * PI / 180.0;
vec4 newPositionXY = newPositionX;
newPositionXY.x = newPositionX.x * cos(rad_angle_y) + newPositionX.z * sin(rad_angle_y);
newPositionXY.z = newPositionX.z * cos(rad_angle_y) - newPositionX.x * sin(rad_angle_y);
gl_Position = gl_ModelViewProjectionMatrix * newPositionXY;
}
This way you don't need to know matrix multiplications.

draw flighroute on mercator map how to calculate polyline points with a given longitude

i would like to draw a polyline on a Mercator map between two cities. e.g Startpoints:
Location berlin = new Location(52.517, 13.40);
Location tokio = new Location(35.70,139.767);
it should look like a flight route.
so my plan was to go through all longitude values between the two cities and calculate corresponding latitude values:
LocationCollection locationCollection = new LocationCollection();
Location next = new Location(berlin.Latitude,berlin.Longitude); //startpunkt
for (double x = berlin.Longitude+1; x < tokio.Longitude; x++) {
locationCollection.Add(next);
next = new Location(???, x);
}
the question is how can i calculate the latitude for each longitude value for the polyline?
Thanks!
From this link:
Here's an implementation in C.
#define PI 3.14159265358979323846
double degrees_radians(double d) { return d * PI / 180.0; }
double radians_degrees(double r) { return r * 180.0 / PI; }
double latitude(double lat1, double lon1, double lat2, double lon2, double lon)
{
return atan((sin(lat1)*cos(lat2)*sin(lon-lon2)-sin(lat2)*cos(lat1)*sin(lon-lon1))/(cos(lat1)*cos(lat2)*sin(lon1-lon2)));
}
int main()
{
// start and end in degrees
double lat1_d = 52.517;
double lon1_d = 13.40;
double lat2_d = 35.70;
double lon2_d = 139.767;
// start and end in radians
double lat1_r = degrees_radians(lat1_d);
double lon1_r = degrees_radians(lon1_d);
double lat2_r = degrees_radians(lat2_d);
double lon2_r = degrees_radians(lon2_d);
// interpolate latitide at every degree of longitude between 1 and 2
for (double lon = ceil(lon1_d); lon < lon2_d; lon += 1.0)
{
double lat_r = latitude(lat1_r, lon1_r, lat2_r, lon2_r, degrees_radians(lon));
double lat_d = radians_degrees(lat_r);
printf("%.3f , %.3f\n", lat_d, lon);
}
return 0;
}
The maximum latitude reached on the great circle between Berlin and Tokyo is then shown to be 66.183° at longitude 68°.

Get direction (compass) with two longitude/latitude points

I'm working on a "compass" for a mobile-device. I have the following points:
point 1 (current location): Latitude = 47.2246, Longitude = 8.8257
point 2 (target location): Latitude = 50.9246, Longitude = 10.2257
Also I have the following information (from my android-phone):
The compass-direction in degree, which bears to the north.
For example, when I direct my phone to north, I get 0°
How can I create a "compass-like" arrow which shows me the direction to the point?
Is there a mathematic-problem for this?
EDIT: Okay I found a solution, it looks like this:
/**
* Params: lat1, long1 => Latitude and Longitude of current point
* lat2, long2 => Latitude and Longitude of target point
*
* headX => x-Value of built-in phone-compass
*
* Returns the degree of a direction from current point to target point
*
*/
function getDegrees(lat1, long1, lat2, long2, headX) {
var dLat = toRad(lat2-lat1);
var dLon = toRad(lon2-lon1);
lat1 = toRad(lat1);
lat2 = toRad(lat2);
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = toDeg(Math.atan2(y, x));
// fix negative degrees
if(brng<0) {
brng=360-Math.abs(brng);
}
return brng - headX;
}
O forgot to say I found the answer eventually. The application is to determine compass direction of a transit vehicle and its destination. Essentially, fancy math for acquiring curvature of Earth, finding an angle/compass reading, and then matching that angle with a generic compass value. You could of course just keep the compassReading and apply that as an amount of rotation for your image. Please note this is an averaged determination of the vehicle direction to the end point (bus station) meaning it can't know what the road is doing (so this probably best applies to airplanes or roller derby).
//example obj data containing lat and lng points
//stop location - the radii end point
endpoint.lat = 44.9631;
endpoint.lng = -93.2492;
//bus location from the southeast - the circle center
startpoint.lat = 44.95517;
startpoint.lng = -93.2427;
function vehicleBearing(endpoint, startpoint) {
endpoint.lat = x1;
endpoint.lng = y1;
startpoint.lat = x2;
startpoint.lng = y2;
var radians = getAtan2((y1 - y2), (x1 - x2));
function getAtan2(y, x) {
return Math.atan2(y, x);
};
var compassReading = radians * (180 / Math.PI);
var coordNames = ["N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"];
var coordIndex = Math.round(compassReading / 45);
if (coordIndex < 0) {
coordIndex = coordIndex + 8
};
return coordNames[coordIndex]; // returns the coordinate value
}
ie:
vehicleBearing(mybus, busstation)
might return "NW" means its travelling northwesterly
I found some useful gps coordinates formula in math here.
For this case, here my solution
private double getDirection(double lat1, double lng1, double lat2, double lng2) {
double PI = Math.PI;
double dTeta = Math.log(Math.tan((lat2/2)+(PI/4))/Math.tan((lat1/2)+(PI/4)));
double dLon = Math.abs(lng1-lng2);
double teta = Math.atan2(dLon,dTeta);
double direction = Math.round(Math.toDegrees(teta));
return direction; //direction in degree
}
I couldn't understand your solution well, calculating the slope worked for me.
To modify on efwjames's and your answer. This should do -
import math
def getDegrees(lat1, lon1, lat2, lon2,head):
dLat = math.radians(lat2-lat1)
dLon = math.radians(lon2-lon1)
bearing = math.degrees(math.atan2(dLon, dLat))
return head-bearing
You'd need to calculate an Euclidean vector between your start point and end point, then calculate its angle (let's say relative to positive X) which would be the angle you want to rotate your arrow by.

Calculating bounding box a certain distance away from a lat/long coordinate in Java

Given a coordinate (lat, long), I am trying to calculate a square bounding box that is a given distance (e.g. 50km) away from the coordinate. So as input I have lat, long and distance and as output I would like two coordinates; one being the south-west (bottom-left) corner and one being the north-east (top-right) corner. I have seen a couple of answers on here that try to address this question in Python, but I am looking for a Java implementation in particular.
Just to be clear, I intend on using the algorithm on Earth only and so I don't need to accommodate a variable radius.
It doesn't have to be hugely accurate (+/-20% is fine) and it'll only be used to calculate bounding boxes over small distances (no more than 150km). So I'm happy to sacrifice some accuracy for an efficient algorithm. Any help is much appreciated.
Edit: I should have been clearer, I really am after a square, not a circle. I understand that the distance between the center of a square and various points along the square's perimeter is not a constant value like it is with a circle. I guess what I mean is a square where if you draw a line from the center to any one of the four points on the perimeter that results in a line perpendicular to a side of the perimeter, then those 4 lines have the same length.
I wrote an article about finding the bounding coordinates:
http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates
The article explains the formulae and also provides a Java implementation. (It also shows why IronMan's formula for the min/max longitude is inaccurate.)
double R = 6371; // earth radius in km
double radius = 50; // km
double x1 = lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));
double x2 = lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));
double y1 = lat + Math.toDegrees(radius/R);
double y2 = lat - Math.toDegrees(radius/R);
Although I would also recommend JTS.
import com.vividsolutions.jts.geom.Envelope;
...
Envelope env = new Envelope(centerPoint.getCoordinate());
env.expandBy(distance_in_degrees);
...
Now env contains your envelope. It's not actually a "square" (whatever that means on the surface of a sphere), but it should do.
You should note that the distance in degrees will depend on the latitude of the center point. At the equator, 1 degree of latitude is about 111km, but in New York, it's only about 75km.
The really cool thing is that you can toss all your points into a com.vividsolutions.jts.index.strtree.STRtree and then use it to quickly calculate points inside that Envelope.
All of the previous answers are only partially correct. Specially in region like Australia, they always include pole and calculate a very large rectangle even for 10kms.
Specially the algorithm by Jan Philip Matuschek at http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex included a very large rectangle from (-37, -90, -180, 180) for almost every point in Australia. This hits a large users in database and distance have to be calculated for all of the users in almost half the country.
I found that the Drupal API Earth Algorithm by Rochester Institute of Technology works better around pole as well as elsewhere and is much easier to implement.
https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54
Use earth_latitude_range and earth_longitude_range from the above algorithm for calculating bounding rectangle
Here is the implementation is Java
/**
* Get bouding rectangle using Drupal Earth Algorithm
* #see https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54
* #param lat
* #param lng
* #param distance
* #return
*/
default BoundingRectangle getBoundingRectangleDrupalEarthAlgo(double lat, double lng, int distance) {
lng = Math.toRadians(lng);
lat = Math.toRadians(lat);
double radius = earth_radius(lat);
List<Double> retLats = earth_latitude_range(lat, radius, distance);
List<Double> retLngs = earth_longitude_range(lat, lng, radius, distance);
return new BoundingRectangle(retLats.get(0), retLats.get(1), retLngs.get(0), retLngs.get(1));
}
/**
* Calculate latitude range based on earths radius at a given point
* #param latitude
* #param longitude
* #param distance
* #return
*/
default List<Double> earth_latitude_range(double lat, double radius, double distance) {
// Estimate the min and max latitudes within distance of a given location.
double angle = distance / radius;
double minlat = lat - angle;
double maxlat = lat + angle;
double rightangle = Math.PI / 2;
// Wrapped around the south pole.
if (minlat < -rightangle) {
double overshoot = -minlat - rightangle;
minlat = -rightangle + overshoot;
if (minlat > maxlat) {
maxlat = minlat;
}
minlat = -rightangle;
}
// Wrapped around the north pole.
if (maxlat > rightangle) {
double overshoot = maxlat - rightangle;
maxlat = rightangle - overshoot;
if (maxlat < minlat) {
minlat = maxlat;
}
maxlat = rightangle;
}
List<Double> ret = new ArrayList<>();
ret.add((minlat));
ret.add((maxlat));
return ret;
}
/**
* Calculate longitude range based on earths radius at a given point
* #param lat
* #param lng
* #param earth_radius
* #param distance
* #return
*/
default List<Double> earth_longitude_range(double lat, double lng, double earth_radius, int distance) {
// Estimate the min and max longitudes within distance of a given location.
double radius = earth_radius * Math.cos(lat);
double angle;
if (radius > 0) {
angle = Math.abs(distance / radius);
angle = Math.min(angle, Math.PI);
}
else {
angle = Math.PI;
}
double minlong = lng - angle;
double maxlong = lng + angle;
if (minlong < -Math.PI) {
minlong = minlong + Math.PI * 2;
}
if (maxlong > Math.PI) {
maxlong = maxlong - Math.PI * 2;
}
List<Double> ret = new ArrayList<>();
ret.add((minlong));
ret.add((maxlong));
return ret;
}
/**
* Calculate earth radius at given latitude
* #param latitude
* #return
*/
default Double earth_radius(double latitude) {
// Estimate the Earth's radius at a given latitude.
// Default to an approximate average radius for the United States.
double lat = Math.toRadians(latitude);
double x = Math.cos(lat) / 6378137.0;
double y = Math.sin(lat) / (6378137.0 * (1 - (1 / 298.257223563)));
//Make sure earth's radius is in km , not meters
return (1 / (Math.sqrt(x * x + y * y)))/1000;
}
And use the distance calculation formula documented by google maps to calculate distance
https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#outputting-data-as-xml-using-php
To search by kilometers instead of miles, replace 3959 with 6371.
For (Lat, Lng) = (37, -122) and a Markers table with columns lat and lng, the formula is:
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;
Based on IronMan response:
/**
* Calculate the lat and len of a square around a point.
* #return latMin, latMax, lngMin, lngMax
*/
public static double[] calculateSquareRadius(double lat, double lng, double radius) {
double R = 6371; // earth radius in km
double latMin = lat - Math.toDegrees(radius/R);
double latMax = lat + Math.toDegrees(radius/R);
double lngMin = lng - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));
double lngMax = lng + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));
return new double[] {latMin, latMax, lngMin, lngMax};
}
Here is a simple solution that I used to generate bounding box coordinates that I use with GeoNames citieJSON API to get nearby big cities from a gps decimal coordinate.
This is a Java method from my GitHub repository: FusionTableModifyJava
I had a decimal GPS location and I needed to find the biggest city/state "near" that location. I needed a relatively accurate bounding box to pass to the citiesJSON GeoNames webservice to get back the biggest city in that bounding box. I pass the location and the "radius" I am interested in (in km) and it gives back the north, south, east, west decimal coordinates needed to pass to citiesJSON.
(I found these resources useful in doing my research:
Calculate distance, bearing and more between Latitude/Longitude points.
Longitude - Wikipedia)
It is not super accurate but accurate enough for what I was using it for:
// Compute bounding Box coordinates for use with Geonames API.
class BoundingBox
{
public double north, south, east, west;
public BoundingBox(String location, float km)
{
//System.out.println(location + " : "+ km);
String[] parts = location.replaceAll("\\s","").split(","); //remove spaces and split on ,
double lat = Double.parseDouble(parts[0]);
double lng = Double.parseDouble(parts[1]);
double adjust = .008983112; // 1km in degrees at equator.
//adjust = 0.008983152770714983; // 1km in degrees at equator.
//System.out.println("deg: "+(1.0/40075.017)*360.0);
north = lat + ( km * adjust);
south = lat - ( km * adjust);
double lngRatio = 1/Math.cos(Math.toRadians(lat)); //ratio for lng size
//System.out.println("lngRatio: "+lngRatio);
east = lng + (km * adjust) * lngRatio;
west = lng - (km * adjust) * lngRatio;
}
}

Resources