Add distance to a particular latitude/longitude position - ruby-on-rails-4

Rails 4 + Postgres. New to geospatial. Happy to accept solutions that involve RGeo, Geokit, Geocoder, or any other gem that helps solve this issue.
Model contains two fields latitude and longitude.
I have an offset attribute that contains a distance in meters and an orientation attribute that contains one of the 4 cardinal directions (N, E, W, S).
Example:
offset: 525.5 orientation: W
What's a standard way of adding the offset distance to the lat-long position, to give me a new lat-long pair as the result of the distance addition?

For small offsets such as a few hundred metres:
You can handle the N&S orientations knowing that:
R * (lat1-lat2)= NorthSouth distance
where R is the Earth radius (6335km).
You can handle the E&W orientations knowing that:
R * cos(lat)* (lon1-lon2) = EastWest distance.
I'm sorry, I don't speak Ruby, but it should be pretty easy to translate this pseudo-code:
R=6335000 // This is in metres
PI=3.14159265 // Your compiler may have a better constant/macro
if(orientation is North or orientation is South)
x = offset * 180 / (PI * R)
if(orientation is South)
x = -x
endif
newLatitude = latitude + x
else
x = offset * 180 / (PI * R * cos(lat))
if(orientation is West)
x = -x
endif
newLongitude = longitude + x
endif

It took a little bit of digging, and it turns out that there is a singular-ish library function (accounting for curvature, geometric, projection, location & mathematical assumptions) that helps add a distance to a specific surface position.
Function: ST_Project
Used below:
SELECT ST_AsGeoJSON(ST_Project('POINT(longitude latitude)'::geography, offset, radians(orientation)))
It's from PostGIS and therefore useable in Ruby/Rails, although not yet as native Ruby objects (gems haven't wrapped it yet), but as a PostGIS query instead.
offset is in meters. orientation is in degrees ('N'=0, 'E'=90, etc.)
Hopefully, this solution helps others looking for the same thing.

Related

Algorithm for determine the Arc Mid Point

I am currently looking to implement an algorithm that will be able to compute the arc midpoint. From here on out, I will be referring to the diagram below. What is known are the start and end nodes (A and B respectively), the center (point C) and point P which is the intersection point of the line AB and CM (I am able to find this point without knowing point M because line AB is perpendicular to line CM and thus, the slope is -1/m). I also know the arc angle and the radius of the arc. I am looking to find point M.
I have been looking at different sources. Some suggest converting coordinates to polar, computing the mid point from the polar coordinates then reverting back to Cartesian. This involves sin and cos (and arctan) which I am a little reluctant to do since trig functions take computing time.
I have been looking to directly computing point M by treating the arc as a circle and having Line CP as a line that intersects the circle at Point M. I would then get two values and the value closest to point P would be the correct intersection point. However, this method, the algebra becomes long and complex. Then I would need to create special cases for when P = C and for when the line AB is horizontal and vertical. This method is ok but I am wondering if there are any better methods out there that can compute this point that are simpler?
Also, as a side note, I will be creating this algorithm in C++.
A circumference in polar form is expressed by
x = Cx + R cos(alpha)
y = Cy + R sin(alpha)
Where alpha is the angle from center C to point x,y. The goal now is how to get alpha without trigonometry.
The arc-midpoint M, the point S in the middle of the segment AB, and your already-calculated point P, all of them have the same alpha, they are on the same line from C.
Let's get vector vx,vy as C to S. Also calculate its length:
vx = Sx - Cx = (Ax + Bx)/2 - Cx
vy = Sy - Cy = (Ay + By)/2 - Cy
leV = sqrt(vx * vx + vy * vy)
I prefer S to P because we can avoid some issues like infinite CP slope or sign to apply to slope (towards M or its inverse).
By defintions of sin and cos we know that:
sin(alpha) = vy / leV
cos(alpha) = vx / leV
and finally we get
Mx = Cx + R * vx / leV
My = Cy + R * vy / leV
Note: To calculate Ryou need another sqrt function, which is not quick, but it's faster than sin or cos.
For better accuracy use the average of Ra= dist(AC) and Rb= dist(BC)
I would then get two values
This is algebraically unavoidable.
and the value closest to point P would be the correct intersection point.
Only if the arc covers less than 180°.
Then I would need to create special cases for when P = C
This is indeed the most tricky case. If A, B, C lie on a line, you don't know which arc is the arc, and won't be able to answer the question. Unless you have some additional information to start with, e.g. know that the arc goes from A to B in a counter-clockwise direction. In this case, you know the orientation of the triangle ABM and can use that to decide which solition to pick, instead of using the distance.
and for when the line AB is horizontal and vertical
Express a line as ax + by + c = 0 and you can treat all slopes the same. THese are homogeneous coordinates of the line, you can compute them e.g. using the cross product (a, b, c) = (Ax, Ay, 1) × (Bx, By, 1). But more detailed questions on how best to compute these lines or intersect it with the circle should probably go to the Math Stack Exchange.
if there are any better methods out there that can compute this point that are simpler?
Projective geometry and homogeneous coordinates can avoid a lot of nasty corner cases, like circles of infinite radius (also known as lines) or the intersection of parallel lines. But the problem of deciding between two solutions remains, so it probably doesn't make things as simple as you'd like them to be.

Find a longitude given a pair of (lat,long) and an offset latitude

In a geodetic coordinate system (wgs84), i have a pair of (latitude,longitude) say (45,50) and (60,20). Also i am said that a new pair of latitude,longitude lies along the line joining these two and at an offset of say 0.1 deg lat from (45,50) i.e. (45.1, x). How do i find this new point? What i tried was to apply the straight line equation
y = mx+c
m = (lat1 - lat2)/ long1-long2)
c = lat1 - m * long1
but that seemed to give wrong results.
Your problem is the calculation of m. You have turned it around!
The normal formula is:
a = (y1 - y2) / (x1 - x2)
so in your case it is:
m = (long2 -long1) / (lat1 - lat2)
so you'll get m = -2
And you also turned the calculation of c around.
Normal is:
b = y1 - a * x1
so you should do:
c = long1 - m * lat1
So you'll get c = 140.
The formula is:
long = -2 * lat + 140
Another way to think about it is given below. The result is the same, of cause.
The surface-line between two coordinates is not a straight line. It is a line drawn on the surface of a round object, i.e. earth. It will be a circle around the earth.
However all coordinates on that line will still go through a straight line.
That is because the coordinate represents the angles of a vector from center of earth to the point you are looking at. The two angles are compared to Equator (latitude) and compared to Greenwich (longitude).
So you need to setup a formula describing all coordinates for that line.
In your case the latitude goes from 45 to 60, i.e. increases by 15.
Your longitude goes from 50 to 20, i.e. decreses by 30.
So your formula will be:
(lat(t), long(t)) = (45, 50) + (15*t, -30*t) for t in [0:1]
Now you can calculate the value of t that will hit (45.1, x) and afterwards you can calculate x.
The equations you use describe a straight line in an 2D cartesian coordinate system.
Longitude and latitude describe a point in a spherical coordinate system.
A spherical coordinate system is not cartesian.
A similar question was answered here.

Computing a character turn angle given old and new position - OpenGL

I am working on a game project using OpenGl. I am building a game from skeleton code I found online. I have a character that can move around in a 2D plane. (x and z, ie you are viewing the character from above.) I am currently stuck on making him rotate as he moves, and I can't seem to find a solution online that solves my problem.
At the moment when the character is being drawn he faces a certain way (along the arrow in my diagram below.). I can rotate him an arbitrary number of degrees from his default direction using glm::rotate.
I have updated the code to log the characters position from a frame ago when he moves, so I have this information:
character old position (known)-> O
character starting angle (unknown)-> |\
| \
| \
|(X)\
| \
V O <- character new position (known)
How do I compute the angle (X)? Is it possible with the information I have?
I have been doodling on a page trying to figure this out for the last hour but can't seem to figure it out. Thanks very much.
Yes. This answer gives you an example of how to do it: How to calculate the angle between a line and the horizontal axis?
Note however that that will give you the angle between the horizontal axsis and the point. You can however just add 90 degrees.
What you're doing sounds somewhat convoluted. From the description, it seems like you want a rotation matrix that matches the direction. There's really no need to calculate an angle. You can build the rotation matrix directly, which is easier and more efficient.
I'll illustrate the calculations with points/vectors in the xy-plane, since that's much more standard. It sounds like you're operating in the xz-plane. While that doesn't change things much, you might need slight changes because you have a left-handed coordinate system.
If you have the direction vector (difference between new position and old position), all you need to do is normalize it, and you already have what's needed for the rotation matrix. I'll write the calculation explicitly, but your matrix/vector library most likely has a method to normalize a vector.
float vx = nexPosX - oldPosX;
float vy = newPosY - oldPosY;
float s = 1.0f / sqrt(vx * vx + vy * vy);
vx *= s;
vy *= s;
vx is now the cosine of the rotation angle, and vy the sine of the rotation angle. Substituting this into the standard form of a rotation matrix, you get:
R = ( cos(phi) -sin(phi) ) = ( vx -vy )
( sin(phi) cos(phi) ) ( vy vx )
This is the absolute rotation for the new direction. If you need the relative rotation between old direction and new direction, it just takes a few more operations. Let's say you already calculated the normalized vectors for the old and new directions as (v1x, v1y) and (v2x, v2y). The cosine of the rotation angle is the scalar product of the two vectors:
cosPhi = v1x * v2x + v1y * v2y;
and the sine is the length of the cross product. Since both vectors are in the xy-plane, that's simply the z-component of the cross product:
sinPhi = v1x * v2y - v1y * v2x;
With these two values, you can directly build the rotation matrix again:
R = ( cosPhi -sinPhi )
( sinPhi cosPhi )

Adjusting glRotate, using dot product

Introducing:
I'm developing a little Tower defense game in opengl, currently I'm just despairing of a little problem....
I want the projectiles from the tower to aim with the head facing the unit. So my problem is more a mathmatical one but it belongs to opengl :)
I had the following idea; I could use a dot product to get an angle rotating around the x axis to get the head depending on the distance just straight down or flat to the ground and after that an additional angle to rotate around the y axis that the head of the arrow is everytime adjusted to the unit it's aiming on.
My code for the angle of rotation around the X axis (i called it m_fYNeigung because the height(Y) of the head changes by rotating around the x axis) looks like this:
plocalTowerArray[(sizeMapIndexY * 12) + sizeMapIndexX].Projektils[byteProjectilIndex].m_fYNeigung =
RADIANS_TO_DEGREES (acos ((float)
(
(faTowerPosition[0]) * (plocalTowerArray[(sizeMapIndexY * 12) + sizeMapIndexX].Projektils[byteProjectilIndex].m_faProDirectionVector[0]) +
(faTowerPosition[1] - 1) * (plocalTowerArray[(sizeMapIndexY * 12) + sizeMapIndexX].Projektils[byteProjectilIndex].m_faProDirectionVector[1]) +
(faTowerPosition[2]) * (plocalTowerArray[(sizeMapIndexY * 12) + sizeMapIndexX].Projektils[byteProjectilIndex].m_faProDirectionVector[2])
)
/
(
fabs (faTowerPosition[0]) * fabs (plocalTowerArray[(sizeMapIndexY * 12) + sizeMapIndexX].Projektils[byteProjectilIndex].m_faProDirectionVector[0]) +
fabs (faTowerPosition[1] - 1) * fabs (plocalTowerArray[(sizeMapIndexY * 12) + sizeMapIndexX].Projektils[byteProjectilIndex].m_faProDirectionVector[1]) +
fabs (faTowerPosition[2]) * fabs (plocalTowerArray[(sizeMapIndexY * 12) + sizeMapIndexX].Projektils[byteProjectilIndex].m_faProDirectionVector[2])
)
));
where faTowerPosition is the first vector, which is pointing down from the top of the tower (the arrow also starts at faTowerPosition[X/Y/Z]) the second vector for the dot product is m_faProDirectionVector which is a normalized direction vector describing the route of the arrow from the tower to the unit.
The Opengl Drawing part looks just as simple as this:
for (sizeJ = 0; sizeJ < localTowerArray[sizeI].m_byteProjectilAmount; sizeJ++)
{
if (localTowerArray[sizeI].Projektils[sizeJ].m_bOnFlight == true)
{
glPushMatrix();
glTranslatef (localTowerArray[sizeI].Projektils[sizeJ].m_faProPosition[0], localTowerArray[sizeI].Projektils[sizeJ].m_faProPosition[1], localTowerArray[sizeI].Projektils[sizeJ].m_faProPosition[2]);
//glRotatef (360.0f - localTowerArray[sizeI].Projektils[sizeJ].m_fXNeigung, 0, 1, 0);
glRotatef (localTowerArray[sizeI].Projektils[sizeJ].m_fYNeigung, 1, 0, 0);
DrawWaveFrontObject (m_pArrowProjektilObject);
glPopMatrix();
}
}
Just ignore the calculations I'm doing to the angle, I just did it to experiment with the acting of the arrows, i just noticed that it appears as would the arrow act different depending on the (i gotta say: the buildable map is scaled by x: -3.4 to 3.4 and z from 4 to -4) cords the tower was builded on -x/z,-z/x,z/x,-z/-x all these cases i guess are different and at least depending on the unit is running left or right side of the tower, the acting is also different.... so what i forgot to remind by using the dot product in this way?
First at all, your code is very difficult to understand, so I'm guessing a lot to try to answer you. If I assume something wrong, my apologize for it.
I am assuming that you want to use the euler angle rotation to align correctly your projectiles. So, first you will do a X rotation and after that, a Y rotation.
To do a X rotation, your vectors, for the dot product, must be on an YZ plane and assuming that your projectile start at Z direction, your first vector is (0, 0, 1). The second vector, as you said, is a vector pointing to unit and could be expressed by (px, py, pz). You must project this vector to the plane YZ to get the second vector for your dot product, so this vector will be (0, py, pz)
Now, to calculate the dot product you apply the following formule
x1.x2+y1.y2+z1.z2 = |p1|.|p2|.cos a, where |p1| and |p2| is the module of vector (its length)
In this example, the first vector is unitary, but the second not. So |p2| = sqrt(py^2 +pz^2). Thereafter:
acos(a) = pz/sqrt(py^2 + pz^2)
This will give you the angle around X axis. Do the same calculation to achieve Y angle rotation
PS. After I wrote this answer, I noted that you use the function "fabs". I guess you want to find the module of you second vector, but fabs give you the absolute value of a escalar. To calculate a module of a vector (its length) you need to use the above formulae as cited.

3d coordinate from point and angles

I'm working on a simple OpenGL world- and so far I've got a bunch of cubes randomly placed about and it's pretty fun to go zooming about. However I'm ready to move on. I would like to drop blocks in front of my camera, but I'm having trouble with the 3d angles. I'm used to 2d stuff where to find an end point we simply do something along the lines of:
endy = y + (sin(theta)*power);
endx = x + (cos(theta)*power);
However when I add the third dimension I'm not sure what to do! It seems to me that the power of the second dimensional plane would be determined by the z axis's cos(theta)*power, but I'm not positive. If that is correct, it seems to me I'd do something like this:
endz = z + (sin(xtheta)*power);
power2 = cos(xtheta) * power;
endx = x + (cos(ytheta) * power2);
endy = y + (sin(ytheta) * power2);
(where x theta is the up/down theta and y = left/right theta)
Am I even close to the right track here? How do I find an end point given a current point and an two angles?
Working with euler angles doesn't work so well in 3D environments, there are several issues and corner cases in which they simply don't work. And you actually don't even have to use them.
What you should do, is exploit the fact, that transformation matrixes are nothing else, then coordinate system bases written down in a comprehensible form. So you have your modelview matrix MV. This consists of a model space transformation, followed by a view transformation (column major matrices multiply right to left):
MV = V * M
So what we want to know is, in which way the "camera" lies within the world. That is given to you by the inverse view matrix V^-1. You can of course invert the view matrix using Gauss Jordan method, but most of the time your view matrix will consist of a 3×3 rotation matrix with a translation vector column P added.
R P
0 1
Recall that
(M * N)^-1 = N^-1 * M^-1
and also
(M * N)^T = M^T * N^T
so it seems there is some kind of relationship between transposition and inversion. Not all transposed matrices are their inverse, but there are some, where the transpose of a matrix is its inverse. Namely it are the so called orthonormal matrices. Rotations are orthonormal. So
R^-1 = R^T
neat! This allows us to find the inverse of the view matrix by the following (I suggest you try to proof it as an exersice):
V = / R P \
\ 0 1 /
V^-1 = / R^T -P \
\ 0 1 /
So how does this help us to place a new object in the scene at a distance from the camera? Well, V is the transformation from world space into camera space, so V^-1 transforms from camera to world space. So given a point in camera space you can transform it back to world space. Say you wanted to place something at the center of the view in distance d. In camera space that would be the point (0, 0, -d, 1). Multiply that with V^-1:
V^-1 * (0, 0, -d, 1) = (R^T)_z * d - P
Which is exactly what you want. In your OpenGL program you somewhere have your view matrix V, probably not properly named yet, but anyway it is there. Say you use old OpenGL-1 and GLU's gluLookAt:
void display(void)
{
/* setup viewport, clear, set projection, etc. */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(...);
/* the modelview matrix now holds the View transform */
At this point we can extract the modelview matrix
GLfloat view[16];
glGetFloatv(GL_MODELVIEW_MATRIX, view);
Now view is in column major order. If we were to use it directly we could directly address the columns. But remember that transpose is inverse of a rotation, so we actually want the 3rd row vector. So let's assume you keep view around, so that in your event handler (outside display) you can do the following:
GLfloat z_row[3];
z_row[0] = view[2];
z_row[1] = view[6];
z_row[2] = view[10];
And we want the position
GLfloat * const p_column = &view[12];
Now we can calculate the new objects position at distance d:
GLfloat new_object_pos[3] = {
z_row[0]*d - p_column[0],
z_row[1]*d - p_column[1],
z_row[2]*d - p_column[2],
};
There you are. As you can see, nowhere you had to work with angles or trigonometry, it's just straight linear algebra.
Well I was close, after some testing, I found the correct formula for my implementation, it looks like this:
endy = cam.get_pos().y - (sin(toRad(180-cam.get_rot().x))*power1);
power2 = cos(toRad(180-cam.get_rot().x))*power1;
endx = cam.get_pos().x - (sin(toRad(180-cam.get_rot().y))*power2);
endz = cam.get_pos().z - (cos(toRad(180-cam.get_rot().y))*power2);
This takes my camera's position and rotational angles and get's the corresponding points. Works like a charm =]