Shortest path on a sphere direction descision - math

I'm trying to write an algorithm that does the following.
Given a current position (in Azimuth and Inclination) and a target position (again in A, I) in what direction should I travel to travel over the shortest path. The return value could be something like a vector A = -1, I = +0.5, that I can then scale for step size/time.
The shortest path can be found by using a great circle, this is easy to visualize, but it's hard to implement like above because my coordinate system isn't continuous.
My coordinate system is as followed (imagine standing in front of the sphere)
The azimuth is 0 ~ pi when traveling along the equator along the front side, it's 0 ~ -pi when traveling along the equator along the rear side.
The inclination is 0~+pi when traveling from the top to the bottom of the sphere.
So given this non-continuous coordinate system, how do I create a decision function that says 'increase A' to travel over the shortest path?

You have a couple of alternatives. The first is to use a Haversine formulation. There is some Javascript source code here. It requires using more traditional lat / lon where the equator is at 0 latitude and the poles are at +/- π or +/- 90° latitude (depending on your units) and longitude is in the range [-180°, 180°) or [-π, π) again depending on your units. You can repeatedly find the midpoint until you have an approximate path that suites your needs. The azimuth / inclination vector would just be the difference in lat / lon between two adjacent points, though over time this will likely induce an error if you repeatedly apply those lat / lon deltas to the location of your agent.
Another approach that may work well for you is to transform your spherical coordinates of your starting and ending location to cartesian coordinates, call them points ub and ue for beginning and end points. The normal vector v of the great circle connecting the two points is the cross product of the two (i.e. v = ub x ue) and the angle θ is just the arccosine of the normalized inner product (ie. θ = cos-1( (ue ∙ ue) / (|ub||ue)). You can then use quaternion rotation and iterate from 0 to θ about the vector v to actually navigate the path. With this approach, the actual instantaneous vector at some point p along the path is just the p x v, or you can just approximate this by using the cartesian difference between two adjacent points along the path.

Related

how to calculate the coordinates on a polyline perpendicular to point (in 3D)?

I have two approximately parallel polylines representing railway tracks, consisting of hundreds (maybe thousands) of x, y, z coordinates. The two lines stay approximately 1.435m apart, but bend and curve as a railway would.
If I pick a point on one of the polylines, how do find the point which is perpendicular on the other parallel polyline?
I take it CAD programs use the cross product to find the distance / point and it chooses the line to snap to based on where your mouse is hovering.
I would like to achieve the same thing, but without hovering your mouse over the line.
Is there a way to simply compute the closest line segment on the parallel line? Or to see which segment of the polyline passes through a perpendicular plane at the selected point?
It isn't practical to loop through the segments as there are so many of them.
In python the input would be something like point x, y, z on rail1 and I would be looking to output point x, y, z on rail2.
Many thanks.
You want the point of the minimum distance to the other track.
If the other track is defined by line segments, each spanning two points with a parameter t going between 0 and 1
pos(t) => pos_1 + t * ( pos_2 - pos_1 )
You need to find the t value that produces the minimum distance to the point. Place a temporary coordinate system on the point and express the ends of each line segment pos_1 and pos_2 in relative coordinates to the point of interest.
The value of t is for the closest point is
dot(pos_1,pos_1) - dot(pos_1,pos_2)
t = ------------------------------------------------------
dot(pos_1,pos_1)-2*dot(pos_1,pos_2)+dot(pos_2,pos_2)
where dot(a,b)=ax*bx+ay*by+az*bz is the vector dot product.
Now if the resulting t is between 0 and 1, then the closest point is on this segment, and its coordinates are given by
pos(t) => pos_1 + t * ( pos_2 - pos_1 )

width of a frustum at a given distance from the near plane

I'm using CML to manage the 3D math in an OpenGL-based interface project I'm making for work. I need to know the width of the viewing frustum at a given distance from the eye point, which is kept as a part of a 4x4 matrix that represents the camera. My goal is to position gui objects along the apparent edge of the viewport, but at some distance into the screen from the near clipping plane.
CML has a function to extract the planes of the frustum, giving them back in Ax + By + Cz + D = 0 form. This frustum is perpendicular to the camera, which isn't necessarily aligned with the z axis of the perspective projection.
I'd like to extract x and z coordinates so as to pin graphical elements to the sides of the screen at different distances from the camera. What is the best way to go about doing it?
Thanks!
This seems to be a duplicate of Finding side length of a cross-section of a pyramid frustum/truncated pyramid, if you already have a cross-section of known width a known distance from the apex. If you don't have that and you want to derive the answer yourself you can follow these steps.
Take two adjacent planes and find
their line of intersection L1. You
can use the steps here. Really
what you need is the direction
vector of the line.
Take two more planes, one the same
as in the previous step, and find
their line of intersection L2.
Note that all planes of the form Ax + By + Cz + D = 0 go through the origin, so you know that L1 and L2
intersect.
Draw yourself a picture of the
direction vectors for L1 and L2,
tails at the origin. These form an
angle; call it theta. Find theta
using the formula for the angle
between two vectors, e.g. here.
Draw a bisector of that angle. Draw
a perpendicular to the bisector at
the distance d you want from the
origin (this creates an isosceles
triangle, bisected into two
congruent right triangles). The
length of the perpendicular is your
desired frustum width w. Note that w is
twice the length of one of the bases
of the right triangles.
Let r be the length of the
hypotenuses of the right triangles.
Then rcos(theta/2)=d and
rsin(theta/2)=w/2, so
tan(theta/2)=(w/2)/d which implies
w=2d*tan(theta/2). Since you know d
and theta, you are done.
Note that we have found the length of one side of a cross-section of a frustrum. This will work with any perpendicular cross-section of any frustum. This can be extended to adapt it to a non-perpendicular cross-section.

Zip Code Radius Search

I'm wondering if it's possible to find all points by longitude and latitude within X radius of one point?
So, if I provide a latitude/longitude of -76.0000, 38.0000, is it possible to simply find all the possible coordinates within (for example) a 10 mile radius of that?
I know that there's a way to calculate the distance between two points, which is why I'm not clear as to whether this is possible. Because, it seems like you need to know the center coordinates (-76 and 38 in this case) as well as the coordinates of every other point in order to determine whether it falls within the specified radius. Is that right?
#David's strategy is correct, his implementation is seriously flawed. I suggest that before you perform the calculations you transform your lat,long pair to UTM coordinates and work in distance, not angular, measurements. If you are not familiar with Universal Transverse Mercator, hit Google or Wikipedia.
I reckon that your point (-76,38) is at UTM 37C 472995 (Easting) 1564346 (Northing). So you want to do your calculations of distance from that point. You'll find it easier, working with UTM, to work in metres, so your distance is (if you are using statute miles of 5280 feet) 16040 metres.
Incidentally, (-76,38) is well outside the contintental US -- does the US Post Office define zip codes for Antarctica ?
If you accept that the Earth is a perfect sphere, you can obtain the spatial coordinates of a point by
x = R.cos(Lat).cos(Long)
y = R.cos(Lat).sin(Long)
z = R.sin(Lat)
Now, take two points and compute the angle they form with the center of the Earth (using a dot product):
cos(Phi) = (x'.x" + y'.y" + z'.z") / R²
(the value of R gets simplified).
In your case, the angular distance, Phi, equals 2Pi.D/R. (R=6 378.1 km).
A point P" is inside the ground distance (D) of P' when the dot product is larger than cos(Phi).
CAUTION: all angles must be in radians.
Depending on the precision, the data set of points within a certain distance may be extremely large or even infinite (impossible). In a given area of a circle with a positive radius you will have infinitely many points. Thus, it is trivial to determine if a point falls within a circle, however to enumerate over all the points is impossible.
If you do set a fixed precision (such as a single digit), you can loop over all possible latitude and longitude combinations and perform the distance test.
Kevin is correct. There is no reason to calculate every possible coordinate-pair in the radius.
If you start at the centerpoint pC = Point(-76.0000, 38.0000) and are testing to find out if arbitrary point pA = Point(Ax, Ay) is within a 10 mile radius... use the Pythagorean theorem:
xDist = abs( pCx - Ax )
yDist = abs ( pCy - Ay )
r^2 = (xDist)^2 + (yDist)^2
A reasonable approximation is to only query the points where
pAx >= (-76.0000 - 10.0000) && pAx <= (-76.0000 + 10.0000)
pAy >= ( 38.0000 - 10.0000) && pAy <= ( 38.0000 + 10.0000)
then perform the more intensive calculation above.

Calculating area enclosed by arbitrary polygon on Earth's surface

Say I have an arbitrary set of latitude and longitude pairs representing points on some simple, closed curve. In Cartesian space I could easily calculate the area enclosed by such a curve using Green's Theorem. What is the analogous approach to calculating the area on the surface of a sphere? I guess what I am after is (even some approximation of) the algorithm behind Matlab's areaint function.
There several ways to do this.
1) Integrate the contributions from latitudinal strips. Here the area of each strip will be (Rcos(A)(B1-B0))(RdA), where A is the latitude, B1 and B0 are the starting and ending longitudes, and all angles are in radians.
2) Break the surface into spherical triangles, and calculate the area using Girard's Theorem, and add these up.
3) As suggested here by James Schek, in GIS work they use an area preserving projection onto a flat space and calculate the area in there.
From the description of your data, in sounds like the first method might be the easiest. (Of course, there may be other easier methods I don't know of.)
Edit – comparing these two methods:
On first inspection, it may seem that the spherical triangle approach is easiest, but, in general, this is not the case. The problem is that one not only needs to break the region up into triangles, but into spherical triangles, that is, triangles whose sides are great circle arcs. For example, latitudinal boundaries don't qualify, so these boundaries need to be broken up into edges that better approximate great circle arcs. And this becomes more difficult to do for arbitrary edges where the great circles require specific combinations of spherical angles. Consider, for example, how one would break up a middle band around a sphere, say all the area between lat 0 and 45deg into spherical triangles.
In the end, if one is to do this properly with similar errors for each method, method 2 will give fewer triangles, but they will be harder to determine. Method 1 gives more strips, but they are trivial to determine. Therefore, I suggest method 1 as the better approach.
I rewrote the MATLAB's "areaint" function in java, which has exactly the same result.
"areaint" calculates the "suface per unit", so I multiplied the answer by Earth's Surface Area (5.10072e14 sq m).
private double area(ArrayList<Double> lats,ArrayList<Double> lons)
{
double sum=0;
double prevcolat=0;
double prevaz=0;
double colat0=0;
double az0=0;
for (int i=0;i<lats.size();i++)
{
double colat=2*Math.atan2(Math.sqrt(Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)+ Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)),Math.sqrt(1- Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)- Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)));
double az=0;
if (lats.get(i)>=90)
{
az=0;
}
else if (lats.get(i)<=-90)
{
az=Math.PI;
}
else
{
az=Math.atan2(Math.cos(lats.get(i)*Math.PI/180) * Math.sin(lons.get(i)*Math.PI/180),Math.sin(lats.get(i)*Math.PI/180))% (2*Math.PI);
}
if(i==0)
{
colat0=colat;
az0=az;
}
if(i>0 && i<lats.size())
{
sum=sum+(1-Math.cos(prevcolat + (colat-prevcolat)/2))*Math.PI*((Math.abs(az-prevaz)/Math.PI)-2*Math.ceil(((Math.abs(az-prevaz)/Math.PI)-1)/2))* Math.signum(az-prevaz);
}
prevcolat=colat;
prevaz=az;
}
sum=sum+(1-Math.cos(prevcolat + (colat0-prevcolat)/2))*(az0-prevaz);
return 5.10072E14* Math.min(Math.abs(sum)/4/Math.PI,1-Math.abs(sum)/4/Math.PI);
}
You mention "geography" in one of your tags so I can only assume you are after the area of a polygon on the surface of a geoid. Normally, this is done using a projected coordinate system rather than a geographic coordinate system (i.e. lon/lat). If you were to do it in lon/lat, then I would assume the unit-of-measure returned would be percent of sphere surface.
If you want to do this with a more "GIS" flavor, then you need to select an unit-of-measure for your area and find an appropriate projection that preserves area (not all do). Since you are talking about calculating an arbitrary polygon, I would use something like a Lambert Azimuthal Equal Area projection. Set the origin/center of the projection to be the center of your polygon, project the polygon to the new coordinate system, then calculate the area using standard planar techniques.
If you needed to do many polygons in a geographic area, there are likely other projections that will work (or will be close enough). UTM, for example, is an excellent approximation if all of your polygons are clustered around a single meridian.
I am not sure if any of this has anything to do with how Matlab's areaint function works.
I don't know anything about Matlab's function, but here we go. Consider splitting your spherical polygon into spherical triangles, say by drawing diagonals from a vertex. The surface area of a spherical triangle is given by
R^2 * ( A + B + C - \pi)
where R is the radius of the sphere, and A, B, and C are the interior angles of the triangle (in radians). The quantity in the parentheses is known as the "spherical excess".
Your n-sided polygon will be split into n-2 triangles. Summing over all the triangles, extracting the common factor of R^2, and bringing all of the \pi together, the area of your polygon is
R^2 * ( S - (n-2)\pi )
where S is the angle sum of your polygon. The quantity in parentheses is again the spherical excess of the polygon.
[edit] This is true whether or not the polygon is convex. All that matters is that it can be dissected into triangles.
You can determine the angles from a bit of vector math. Suppose you have three vertices A,B,C and are interested in the angle at B. We must therefore find two tangent vectors (their magnitudes are irrelevant) to the sphere from point B along the great circle segments (the polygon edges). Let's work it out for BA. The great circle lies in the plane defined by OA and OB, where O is the center of the sphere, so it should be perpendicular to the normal vector OA x OB. It should also be perpendicular to OB since it's tangent there. Such a vector is therefore given by OB x (OA x OB). You can use the right-hand rule to verify that this is in the appropriate direction. Note also that this simplifies to OA * (OB.OB) - OB * (OB.OA) = OA * |OB| - OB * (OB.OA).
You can then use the good ol' dot product to find the angle between sides: BA'.BC' = |BA'|*|BC'|*cos(B), where BA' and BC' are the tangent vectors from B along sides to A and C.
[edited to be clear that these are tangent vectors, not literal between the points]
Here is a Python 3 implementation, loosely inspired by the above answers:
def polygon_area(lats, lons, algorithm = 0, radius = 6378137):
"""
Computes area of spherical polygon, assuming spherical Earth.
Returns result in ratio of the sphere's area if the radius is specified.
Otherwise, in the units of provided radius.
lats and lons are in degrees.
"""
from numpy import arctan2, cos, sin, sqrt, pi, power, append, diff, deg2rad
lats = np.deg2rad(lats)
lons = np.deg2rad(lons)
# Line integral based on Green's Theorem, assumes spherical Earth
#close polygon
if lats[0]!=lats[-1]:
lats = append(lats, lats[0])
lons = append(lons, lons[0])
#colatitudes relative to (0,0)
a = sin(lats/2)**2 + cos(lats)* sin(lons/2)**2
colat = 2*arctan2( sqrt(a), sqrt(1-a) )
#azimuths relative to (0,0)
az = arctan2(cos(lats) * sin(lons), sin(lats)) % (2*pi)
# Calculate diffs
# daz = diff(az) % (2*pi)
daz = diff(az)
daz = (daz + pi) % (2 * pi) - pi
deltas=diff(colat)/2
colat=colat[0:-1]+deltas
# Perform integral
integrands = (1-cos(colat)) * daz
# Integrate
area = abs(sum(integrands))/(4*pi)
area = min(area,1-area)
if radius is not None: #return in units of radius
return area * 4*pi*radius**2
else: #return in ratio of sphere total area
return area
Please find a somewhat more explicit version (and with many more references and TODOs...) here.
You could also have a look at this code of the spherical_geometry package: Here and here. It does provide two different methods for calculating the area of a spherical polygon.

Triangle mathematics for game development

I'm trying to make a triangle (isosceles triangle) to move around the screen and at the same time slightly rotate it when a user presses a directional key (like right or left).
I would like the nose (top point) of the triangle to lead the triangle at all times. (Like that old asteroids game).
My problem is with the maths behind this. At every X time interval, I want the triangle to move in "some direction", I need help finding this direction (x and y increments/decrements).
I can find the center point (Centroid) of the triangle, and I have the top most x an y points, so I have a line vector to work with, but not a clue as to "how" to work with it.
I think it has something to do with the old Sin and Cos methods and the amount (angle) that the triangle has been rotated, but I'm a bit rusty on that stuff.
Any help is greatly appreciated.
The arctangent (inverse tangent) of vy/vx, where vx and vy are the components of your (centroid->tip) vector, gives you the angle the vector is facing.
The classical arctangent gives you an angle normalized to -90° < r < +90° degrees, however, so you have to add or subtract 90 degrees from the result depending on the sign of the result and the sign of vx.
Luckily, your standard library should proive an atan2() function that takes vx and vy seperately as parameters, and returns you an angle between 0° and 360°, or -180° and +180° degrees. It will also deal with the special case where vx=0, which would result in a division by zero if you were not careful.
See http://www.arctangent.net/atan.html or just search for "arctangent".
Edit: I've used degrees in my post for clarity, but Java and many other languages/libraries work in radians where 180° = π.
You can also just add vx and vy to the triangle's points to make it move in the "forward" direction, but make sure that the vector is normalized (vx² + vy² = 1), else the speed will depend on your triangle's size.
#Mark:
I've tried writing a primer on vectors, coordinates, points and angles in this answer box twice, but changed my mind on both occasions because it would take too long and I'm sure there are many tutorials out there explaining stuff better than I ever can.
Your centroid and "tip" coordinates are not vectors; that is to say, there is nothing to be gained from thinking of them as vectors.
The vector you want, vForward = pTip - pCentroid, can be calculated by subtracting the coordinates of the "tip" corner from the centroid point. The atan2() of this vector, i.e. atan2(tipY-centY, tipX-centX), gives you the angle your triangle is "facing".
As for what it's relative to, it doesn't matter. Your library will probably use the convention that the increasing X axis (---> the right/east direction on presumably all the 2D graphs you've seen) is 0° or 0π. The increasing Y (top, north) direction will correspond to 90° or (1/2)π.
It seems to me that you need to store the rotation angle of the triangle and possibly it's current speed.
x' = x + speed * cos(angle)
y' = y + speed * sin(angle)
Note that angle is in radians, not degrees!
Radians = Degrees * RadiansInACircle / DegreesInACircle
RadiansInACircle = 2 * Pi
DegressInACircle = 360
For the locations of the vertices, each is located at a certain distance and angle from the center. Add the current rotation angle before doing this calculation. It's the same math as for figuring the movement.
Here's some more:
Vectors represent displacement. Displacement, translation, movement or whatever you want to call it, is meaningless without a starting point, that's why I referred to the "forward" vector above as "from the centroid," and that's why the "centroid vector," the vector with the x/y components of the centroid point doesn't make sense. Those components give you the displacement of the centroid point from the origin. In other words, pOrigin + vCentroid = pCentroid. If you start from the 0 point, then add a vector representing the centroid point's displacement, you get the centroid point.
Note that:
vector + vector = vector
(addition of two displacements gives you a third, different displacement)
point + vector = point
(moving/displacing a point gives you another point)
point + point = ???
(adding two points doesn't make sense; however:)
point - point = vector
(the difference of two points is the displacement between them)
Now, these displacements can be thought of in (at least) two different ways. The one you're already familiar with is the rectangular (x, y) system, where the two components of a vector represent the displacement in the x and y directions, respectively. However, you can also use polar coordinates, (r, Θ). Here, Θ represents the direction of the displacement (in angles relative to an arbitary zero angle) and r, the distance.
Take the (1, 1) vector, for example. It represents a movement one unit to the right and one unit upwards in the coordinate system we're all used to seeing. The polar equivalent of this vector would be (1.414, 45°); the same movement, but represented as a "displacement of 1.414 units in the 45°-angle direction. (Again, using a convenient polar coordinate system where the East direction is 0° and angles increase counter-clockwise.)
The relationship between polar and rectangular coordinates are:
Θ = atan2(y, x)
r = sqrt(x²+y²) (now do you see where the right triangle comes in?)
and conversely,
x = r * cos(Θ)
y = r * sin(Θ)
Now, since a line segment drawn from your triangle's centroid to the "tip" corner would represent the direction your triangle is "facing," if we were to obtain a vector parallel to that line (e.g. vForward = pTip - pCentroid), that vector's Θ-coordinate would correspond to the angle that your triangle is facing.
Take the (1, 1) vector again. If this was vForward, then that would have meant that your "tip" point's x and y coordinates were both 1 more than those of your centroid. Let's say the centroid is on (10, 10). That puts the "tip" corner over at (11, 11). (Remember, pTip = pCentroid + vForward by adding "+ pCentroid" to both sides of the previous equation.) Now in which direction is this triangle facing? 45°, right? That's the Θ-coordinate of our (1, 1) vector!
keep the centroid at the origin. use the vector from the centroid to the nose as the direction vector. http://en.wikipedia.org/wiki/Coordinate_rotation#Two_dimensions will rotate this vector. construct the other two points from this vector. translate the three points to where they are on the screen and draw.
double v; // velocity
double theta; // direction of travel (angle)
double dt; // time elapsed
// To compute increments
double dx = v*dt*cos(theta);
double dy = v*dt*sin(theta);
// To compute position of the top of the triangle
double size; // distance between centroid and top
double top_x = x + size*cos(theta);
double top_y = y + size*sin(theta);
I can see that I need to apply the common 2d rotation formulas to my triangle to get my result, Im just having a little bit of trouble with the relationships between the different components here.
aib, stated that:
The arctangent (inverse tangent) of
vy/vx, where vx and vy are the
components of your (centroid->tip)
vector, gives you the angle the vector
is facing.
Is vx and vy the x and y coords of the centriod or the tip? I think Im getting confused as to the terminology of a "vector" here. I was under the impression that a Vector was just a point in 2d (in this case) space that represented direction.
So in this case, how is the vector of the centroid->tip calculated? Is it just the centriod?
meyahoocomlorenpechtel stated:
It seems to me that you need to store
the rotation angle of the triangle and
possibly it's current speed.
What is the rotation angle relative to? The origin of the triangle, or the game window itself? Also, for future rotations, is the angle the angle from the last rotation or the original position of the triangle?
Thanks all for the help so far, I really appreciate it!
you will want the topmost vertex to be the centroid in order to achieve the desired effect.
First, I would start with the centroid rather than calculate it. You know the position of the centroid and the angle of rotation of the triangle, I would use this to calculate the locations of the verticies. (I apologize in advance for any syntax errors, I have just started to dabble in Java.)
//starting point
double tip_x = 10;
double tip_y = 10;
should be
double center_x = 10;
double center_y = 10;
//triangle details
int width = 6; //base
int height = 9;
should be an array of 3 angle, distance pairs.
angle = rotation_angle + vertex[1].angle;
dist = vertex[1].distance;
p1_x = center_x + math.cos(angle) * dist;
p1_y = center_y - math.sin(angle) * dist;
// and the same for the other two points
Note that I am subtracting the Y distance. You're being tripped up by the fact that screen space is inverted. In our minds Y increases as you go up--but screen coordinates don't work that way.
The math is a lot simpler if you track things as position and rotation angle rather than deriving the rotation angle.
Also, in your final piece of code you're modifying the location by the rotation angle. The result will be that your ship turns by the rotation angle every update cycle. I think the objective is something like Asteroids, not a cat chasing it's tail!

Resources