I'm trying to convert spherical coordinates (namely latitude and longitude from a GPS device) into Cartesian coordinates. I'm following this simple conversion derived from the polar coordinates conversion equations.
Then I'm calculating the distance between the two point applying the euclidean distance but the value I'm finding is not always the same as the distance I can calculate using the haversine formula. In particular I'm noticing that given different longitudes but same latitudes leads to the same distances computed by the two algorithms, whereas having the same longitude and changing the latitude carries different values.
Here is the C code I am using:
double ComputeDistance(double lat1,double lon1, double lat2, double lon2)
{
double dlon, dlat, a, c;
dlon = lon2- lon1;
dlat = lat2 - lat1;
a = pow(sin(dlat/2),2) + cos(lat1) * cos(lat2) * pow(sin(dlon/2),2);
c = 2 * atan2(sqrt(a), sqrt(1-a));
return 6378140 * c; /* 6378140 is the radius of the Earth in meters*/
}
int main (int argc, const char * argv[]) {
double lat1 = 41.788251028649575;
double lat2 = 41.788251028649575;
double long1 = -118.1457209154;
double long2 = -118.1407209154;//just ~10 meters distant
lat1 = DEGREES_TO_RADIANS(lat1);
lat2 = DEGREES_TO_RADIANS(lat2);
long1 = DEGREES_TO_RADIANS(long1);
long2 = DEGREES_TO_RADIANS(long2);
//transform in cartesian coordinates
double x = 6378140 * cos(lat1) * cos(long1);
double y = 6378140 * cos(lat1) * sin(long1);
double x2 = 6378140 * cos(lat2) * cos(long2);
double y2 = 6378140 * cos(lat2) * sin(long2);
double dist = sqrt(pow(x2 - x, 2) + pow(y2 - y, 2));
printf("DIST %lf\n", dist);
printf("NDIST %lf\n", ComputeDistance(lat1, long1, lat2, long2));
return 0;
}
Am I doing something incorrect or there is some math behind it I am not seeing (and maybe ask this on Mathoverflow boards?). UPDATE It is not necessary to cross boards, as someone correctly pointed this conversion is not meaningful for computing the exact distance between two points (the distance between the two poles is zero). So I am reformulating it as: why at small deltas (0.0001 which corresponds to 10 mts more or less) of latitudes the distance appear to be so different from the haversine formula (20-25%)?
UPDATE 2:
As Oli Charlesworth pointed out, not considering the z axis makes this conversion a projection that does not mind the north-south difference. This is also the cause of the difference in deltas I was pointing out. In fact, in the correct transform the z is related to the latitude and if you consider it , then compute the euclidean distance between the two points (in a 3d space now), both latitude and longitude will lead to a good approximation for small deltas.
For Example for a degree of latitude the error is ~ 1.41 meters.
From this, there is no 2D map projection where distance is preserved. Calculating the distance from the 2D projection of points is useless.
Related
Programming Language: C
I'm currently in the process of implementing a 3D wireframe model represented through isometric projection.
My current understanding of the project is to:
parse a text map containing the x,y,z coordinates of the wireframe model
Transforming the 3D coordinates to 2D using isometric projection
Drawing the line using the Bresenham Line Algo and a few functions out of my graphic library of choice.
I'm done with Step 1 however I've been stuck on Step 2 for the last few days.
I understand that isometric projection is the process of projecting a 2D plane in a angle that it looks like it's 3D even though we are only working with x,y when drawing the lines. That is def. not the best way of describing it and if I'm incorrect please correct me.
Example of a text map:
0 0 0
0 5 0
0 0 0
My data structure of choice (implemented as array of structs)
typedef struct point
{
float x;
float y;
float z;
bool is_last;
int color; // Implemented after mandatory part
} t_point;
I pretty much just read out the rows, column and values of the text map and store them in x,y,z values respectively.
Now that I have to transform them I've tried the following formulas:
const double angle = 30 * M_PI / 180.0;
void isometric(t_dot *dot, double angle)
{
dot->x = (dot->x - dot->y) * cos(angle);
dot->y = (dot->x + dot->y) * sin(angle) - dot->z;
}
static void iso(int x, int y, int z)
{
int previous_x;
int previous_y;
previous_x = x;
previous_y = y;
x = (previous_x - previous_y) * cos(0.523599);
y = -z + (previous_x + previous_y) * sin(0.523599);
}
t_point *calc_isometric(t_point *pts, int max_pts)
{
float x;
float y;
float z;
const double angle = 30 * M_PI / 180.0;
int num_pts;
num_pts = 0;
while (num_pts < max_pts)
{
x = pts[num_pts].x;
y = pts[num_pts].y;
z = pts[num_pts].z;
printf("x: %f y: %f z: %f\n", x, y, z);
pts[num_pts].x = (x - y) * cos(angle);
pts[num_pts].y = (x + y) * sin(angle) - z;
printf("x_iso %f\ty_iso %f\n\n", pts[num_pts].x, pts[num_pts].y);
num_pts++;
}
return (pts);
}
It spits out various things which makes no sense to me. I could just go one and try to implement the Line Algo. from here and hope for the best but I would like to understand what I'm actually doing here.
Next to that I learned through me research that I need to set up my camera in a certain way to create the projection.
All in all I'm just very lost and my question boils down to this.
Please help me understand the concept of isometric projection.
How to transform 3D coordinates (x,y,z) into coordinates using isometric projection.
I see it like this:
// constants:
float deg = M_PI/180.0;
float ax = 30*deg;
float ay =150*deg;
vec2 X = vec2(cos(ax),-sin(ax)); // x axis
vec2 Y = vec2(cos(ay),-sin(ay)); // y axis
vec2 Z = vec2( 0.0,- 1.0); // z axis
vec2 O = vec2(0,0); // position of point (0,0,0) on screen
// conversion:
vec3 p=vec3(?,?,?); // input point
vec2 q=O+(p.x*X)+(p.y*Y)+(p.z*Y); // output point
the coordinatewise version:
float Xx = cos(ax);
float Xy = -sin(ax);
float Yx = cos(ay);
float Yy = -sin(ay);
float Zx = 0.0;
float Zy = - 1.0;
float Ox = 0;
float Oy = 0;
// conversion:
float px=?,py=?,pz=?; // input point
float qx=Ox+(px*Xx)+(py*Yx)+(pz*Yx); // output point
float qy=Oy+(px*Xy)+(py*Yy)+(pz*Yy); // output point
Asuming x axis going to right and y axis going down ... the O is usually set to center of screen instead of (0,0) unless you add pan capabilities of your isometric world.
In case you want to add arbitrary rotations within the "3D" XY plane see this:
How can I warp a shader matrix to match isometric perspective in a 3d scene?
So you just compute the X,Y vectors on the ellipse (beware they will not be unit anymore!!!) So if I see it right it would be:
float ax=?,ay=ax+90*deg;
float Xx = cos(ax) ;
float Xy = -sin(ax)*0.5;
float Yx = cos(ay) ;
float Yy = -sin(ay)*0.5;
where ax is the rotation angle...
i have two Objects in a 3D World and want to make the one object facing the other object. I already calculated all the angles and stuff (pitch angle and yaw angle).
The problem is i have no functions to set the yaw or pitch individually which means that i have to do it by a quaternion. As the only function i have is: SetEnetyQuaternion(float x, float y, float z, float w). This is my pseudocode i have yet:
float px, py, pz;
float tx, ty, tz;
float distance;
GetEnetyCoordinates(ObjectMe, &px, &py, &pz);
GetEnetyCoordinates(TargetObject, &tx, &ty, &tz);
float yaw, pitch;
float deltaX, deltaY, deltaZ;
deltaX = tx - px;
deltaY = ty - py;
deltaZ = tz - pz;
float hyp = SQRT((deltaX*deltaX) + (deltaY*deltaY) + (deltaZ*deltaZ));
yaw = (ATAN2(deltaY, deltaX));
if(yaw < 0) { yaw += 360; }
pitch = ATAN2(-deltaZ, hyp);
if (pitch < 0) { pitch += 360; }
//here is the part where i need to do a calculation to convert the angles
SetEnetyQuaternion(ObjectMe, pitch, 0, yaw, 0);
What i tried yet was calculating the sinus from those angles devided with 2 but this didnt work - i think this is for euler angles or something like that but didnt help me. The roll(y axis) and the w argument can be left out i think as i dont want my object to have a roll. Thats why i put 0 in.
If anyone has any idea i would really appreciate help.
Thank you in advance :)
Let's suppose that the quaternion you want describes the attitude of the player relative to some reference attitude. It is then essential to know what the reference attitude is.
Moreover, you need to understand that an object's attitude comprises more than just its facing -- it also comprises the object's orientation around that facing. For example, imagine the player facing directly in the positive x direction of the position coordinate system. This affords many different attitudes, from the one where the player is standing straight up to ones where he is horizontal on either his left or right side, to one where he is standing on his head, and all those in between.
Let's suppose that the appropriate reference attitude is the one facing parallel to the positive x direction, and with "up" parallel to the positive z direction (we'll call this "vertical"). Let's also suppose that among the attitudes in which the player is facing the target, you want the one having "up" most nearly vertical. We can imagine the wanted attitude change being performed in two steps: a rotation about the coordinate y axis followed by a rotation about the coordinate z axis. We can write a unit quaternion for each of these, and the desired quaternion for the overall rotation is the Hamilton product of these quaternions.
The quaternion for a rotation of angle θ around the unit vector described by coordinates (x, y, z) is (cos θ/2, x sin θ/2, y sin θ/2, z sin θ/2). Consider then, the first quaternion you want, corresponding to the pitch. You have
double semiRadius = sqrt(deltaX * deltaX + deltaY * deltaY);
double cosPitch = semiRadius / hyp;
double sinPitch = deltaZ / hyp; // but note that we don't actually need this
. But you need the sine and cosine of half that angle. The half-angle formulae come in handy here:
double sinHalfPitch = sqrt((1 - cosPitch) / 2) * ((deltaZ < 0) ? -1 : 1);
double cosHalfPitch = sqrt((1 + cosPitch) / 2);
The cosine will always be nonnegative because the pitch angle must be in the first or fourth quadrant; the sine will be positive if the object is above the player, or negative if it is below. With all that being done, the first quaternion is
(cosHalfPitch, 0, sinHalfPitch, 0)
Similar analysis applies to the second quaternion. The cosine and sine of the full rotation angle are
double cosYaw = deltaX / semiRadius;
double sinYaw = deltaY / semiRadius; // again, we don't actually need this
We can again apply the half-angle formulae, but now we need to account for the full angle to be in any quadrant. The half angle, however, can be only in quadrant 1 or 2, so its sine is necessarily non-negative:
double sinHalfYaw = sqrt((1 - cosYaw) / 2);
double cosHalfYaw = sqrt((1 + cosYaw) / 2) * ((deltaY < 0) ? -1 : 1);
That gives us an overall second quaternion of
(cosHalfYaw, 0, 0, sinHalfYaw)
The quaternion you want is the Hamilton product of these two, and you must take care to compute it with the correct operand order (qYaw * qPitch), because the Hamilton product is not commutative. All the zeroes in the two factors make the overall expression much simpler than it otherwise would be, however:
(cosHalfYaw * cosHalfPitch,
-sinHalfYaw * sinHalfPitch,
cosHalfYaw * sinHalfPitch,
sinHalfYaw * cosHalfPitch)
At this point I remind you that we started with an assumption about the reference attitude for the quaternion system, and the this result depends on that choice. I also remind you that I made an assumption about the wanted attitude, and that also affects this result.
Finally, I observe that this approach breaks down where the target object is very nearly directly above or directly below the player (corresponding to semiRadius taking a value very near zero) and where the player is very nearly on top of the target (corresponding to hyp taking a value very near zero). There is a non-zero chance of causing a division by zero if you use these formulae exactly as given, so you'll want to think about how to deal with that.)
I want to check if a point P(x1,y1) belongs , is inside , a square with center C(x,y) and horizontal diagonal r.
Square with the above characteristics:
Function that calculates the distance between two points
float calculate_distance (float x1,float y1,float x2 ,float y2)
{
float distance;
float distance_x = x1-x2;
float distance_y = y1- y2;
distance = sqrt( (distance_x * distance_x) + (distance_y * distance_y));
return distance;
}
You don't need the Euclidian distance between points here.
Just as for a circle (at the origin) you know that x2+y2 is some constant (r2), here you know that |x|+|y| is some constant (r again), which is even simpler. Actually you can interpolate between these shapes by using exponents between 1 and 2.
So to check whether a point (x,y) is inside the diamond (which without loss of generality can be assumed to be centered on the origin), just test
fabsf(x)+fabsf(y) <= r
I'm attempting to render a simple axes display like the following, except using simple 2D lines.
I have angle values for the pitch, yaw, and roll that the axes should display. (e.g. PI/2, PI/4, PI*3/2)
I have a function that can render 2D lines given start and end points in 2D space.
How can I properly render a rotated set of axes given the angles? I don't care about z-indexing (so if sometimes the lines incorrectly show up on top of each other, that is OK as long as they point the correct direction).
What I've tried
I know that the start points for all the lines will just be in the center of the axis, and we'll say the center is at (0, 0) and the length of the axes will be 100. That leaves me to calculate the endpoints for each of the 3 axes.
I have defined the X axis to be left-to-right, the Y axis to be up-and-down and the Z axis to be back-forward (i.e. out of the screen).
Pitch is rotation around the X axis, roll is rotation around Z axis, and yaw is rotation around Y axis.
To calculate the X-axis end point I've done:
x = cos(roll) * cos(yaw) * 100;
y = sin(-roll) * 100;
To calculate the Y-axis end point I've done:
x = cos(roll + PI/2) * 100;
y = sin(-roll - PI/2) * sin(PI/2 - pitch) * 100;
To calculate the Z-axis end point I've done:
x = cos(PI/2 - yaw) * 100;
y = sin(PI - pitch) * 100;
It's probably evident that I don't really know what I'm doing. I feel like I am taking a very naive approach when I should be using something more advanced like matrices. If it makes a difference, I'm using C, but psuedocode is fine. Any help would be appreciated.
First, you need to agree on an order of the rotations. In the following, I assume the order x, y, z (pitch, yaw, roll).
The end points are simply the column vectors of the according rotation matrix. Then, you need to project the 3d points onto the 2d screen. It seems as if you use a simple orthogonal projection (removing the z-coordinate). So here are the results:
x1 = 100 (cos yaw * cos roll)
y1 = 100 (cos pitch * sin roll + cos roll * sin pitch * sin yaw)
x2 = 100 (-cos yaw * sin roll)
y2 = 100 (cos pitch * cos roll - sin pitch * sin yaw * sin roll)
x3 = 100 (sin yaw)
y3 = 100 (-cos yaw * sin pitch)
If I have two GPS locations, say 51.507222, -0.1275 and 48.856667, 2.350833, what formula could I use to calculate the distance between the two? I've heard a lot about a haversine formula, but can't find any information about it, or how to apply it to C.
I've written the following code, however, it's very innacurate. Anybody know why? I can't figure it out. The problem comes from the function itself, but I don't know what it is.
float calcDistance(float A, float B, float C, float D)
{
float dLat;
float dLon;
dLat = (C - A);
dLon = (D - B);
dLat /= 57.29577951;
dLon /= 57.29577951;
float v_a;
float v_c;
float distance;
v_a = sin(dLat/2) * sin(dLat/2) + cos(A) * cos(C) * sin(dLon/2) * sin(dLon/2);
v_c = 2 * atan2(sqrt(v_a),sqrt(1-v_a));
distance = r * v_c;
return distance;
}
Your code is absolutely correct.
I would rename parameters A, B, C and D to lat1, lon1, lat2 and lon2.
It returns the distance in kilometers. You need to define r as 6371, roughly the radius of the earth.
What you're looking for is the great circle distance.
This page has a block of Javascript which I'm sure you could adapt easily enough in C:
var R = 6371e3; // metres
var φ1 = lat1.toRadians();
var φ2 = lat2.toRadians();
var Δφ = (lat2-lat1).toRadians();
var Δλ = (lon2-lon1).toRadians();
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
Maybee its too late, but this is the reason of the inaccurate calculation:
-> A and C (lat1 and lat2) must also be converted from degree to radians
dLat /= 57.295779513082325;
dLon /= 57.295779513082325;
lat1 /= 57.295779513082325;
lat2 /= 57.295779513082325;
regards,
omeier