Taking altitude into account when calculating geodesic distance - geometry

i´m currently dealing with gps data combined with precise altitude measurement.
I want to calculate the distance between two consecuting points. There is a lot
of information out there about calculating distance between two points using the WGS84 ellipsoid and so on.
however, i did not find any information that takes Altitude changes into account for this
distance calculation.
does anyone know about some websites, papers, books etc. that describes such a method?
thanks
edit: Sql Server 2008 geographic extensions also neglect altitude information when calculating distance.

I implemented a WGS84 distance function using the average of the start and end altitude as the constant altitude. If you are certain that there will be relatively little altitude variation along your path this works acceptably well (error is relative to the altitude difference of your two LLA points).
Here's my code (C#):
/// <summary>
/// Gets the geodesic distance between two pathpoints in the current mode's coordinate system
/// </summary>
/// <param name="point1">First point</param>
/// <param name="point2">Second point</param>
/// <param name="mode">Coordinate mode that both points are in</param>
/// <returns>Distance between the two points in the current coordinate mode</returns>
public static double GetGeodesicDistance(PathPoint point1, PathPoint point2, CoordMode mode) {
// calculate proper geodesics for LLA paths
if (mode == CoordMode.LLA) {
// meeus approximation
double f = (point1.Y + point2.Y) / 2 * LatLonAltTransformer.DEGTORAD;
double g = (point1.Y - point2.Y) / 2 * LatLonAltTransformer.DEGTORAD;
double l = (point1.X - point2.X) / 2 * LatLonAltTransformer.DEGTORAD;
double sinG = Math.Sin(g);
double sinL = Math.Sin(l);
double sinF = Math.Sin(f);
double s, c, w, r, d, h1, h2;
// not perfect but use the average altitude
double a = (LatLonAltTransformer.A + point1.Z + LatLonAltTransformer.A + point2.Z) / 2.0;
sinG *= sinG;
sinL *= sinL;
sinF *= sinF;
s = sinG * (1 - sinL) + (1 - sinF) * sinL;
c = (1 - sinG) * (1 - sinL) + sinF * sinL;
w = Math.Atan(Math.Sqrt(s / c));
r = Math.Sqrt(s * c) / w;
d = 2 * w * a;
h1 = (3 * r - 1) / 2 / c;
h2 = (3 * r + 1) / 2 / s;
return d * (1 + (1 / LatLonAltTransformer.RF) * (h1 * sinF * (1 - sinG) - h2 * (1 - sinF) * sinG));
}
PathPoint diff = new PathPoint(point2.X - point1.X, point2.Y - point1.Y, point2.Z - point1.Z, 0);
return Math.Sqrt(diff.X * diff.X + diff.Y * diff.Y + diff.Z * diff.Z);
}
In practice we've found that the altitude difference rarely makes a large difference, our paths are typically 1-2km long with altitude varying on the order of 100m and we see about ~5m change on average versus using the WGS84 ellipsoid unmodified.
Edit:
To add to this, if you do expect large altitude changes, you can convert your WGS84 coordinates to ECEF (earth centered earth fixed) and evaluate straight-line paths as shown at the bottom of my function. Converting a point to ECEF is simple to do:
/// <summary>
/// Converts a point in the format (Lon, Lat, Alt) to ECEF
/// </summary>
/// <param name="point">Point as (Lon, Lat, Alt)</param>
/// <returns>Point in ECEF</returns>
public static PathPoint WGS84ToECEF(PathPoint point) {
PathPoint outPoint = new PathPoint(0);
double lat = point.Y * DEGTORAD;
double lon = point.X * DEGTORAD;
double e2 = 1.0 / RF * (2.0 - 1.0 / RF);
double sinLat = Math.Sin(lat), cosLat = Math.Cos(lat);
double chi = A / Math.Sqrt(1 - e2 * sinLat * sinLat);
outPoint.X = (chi + point.Z) * cosLat * Math.Cos(lon);
outPoint.Y = (chi + point.Z) * cosLat * Math.Sin(lon);
outPoint.Z = (chi * (1 - e2) + point.Z) * sinLat;
return outPoint;
}
Edit 2:
I was asked about some of the other variables in my code:
// RF is the eccentricity of the WGS84 ellipsoid
public const double RF = 298.257223563;
// A is the radius of the earth in meters
public const double A = 6378137.0;
LatLonAltTransformer is a class I used to convert from LatLonAlt coordinates to ECEF coordinates and defines the constants above.

You likely don't care about altitude for large 2D distance separatiions. So if the dist you get is over say 20 (or perhaps 50)km, then who cares about the altitude diff (depends on your needs case). Under say 20km, feed in the simple pythagorean addition to the altitude difference. Feed it in smoothly.
Distance between two geo-points?

I would suggest that over any distance where using the WGS84 would give you significantly better accuracy that the difference in altitude won't matter. And over any distance where the difference in altitudes matters you should probably just use straight line approximation.

In order to do this the first issue you have to address is how to define change in altitude. The normal equations work because they are on a two dimensional surface, however adding the third dimension means that the simple definition of shortest distance is no longer applicable, for example now the thrid dimension is 'in play' your shortest distance could cut through the original ellipsoid.
It's a bit quick and dirty, but your best solution might be to assume that the rate of change of alltitude is constant along the original 2D path on the ellipsoid. You can then calculate the 2D distance as a length, work out the rate of change of altitude and then simply use Pythagoras to calculate the increase in length with one side of the triangle being the 2D distance, and the altitude being the second length.

For starters, you need a model that tells you how the altitude changes on the line between the two points. Without such a model, you don't have any consistent definition of the distance between two points.
If you had a linear model (traveling 50% of the distance between the points also means you went upwards through 50% of the altitude), then you can probably pretend that the entire thing was a right-triangle; i.e. you act as though the world is flat for purposes of determining how the altitude shift affects the distance. The distance along the ground is the base, the altitude change is the height of the triangle, and the hypotenuse is your estimated true travel distance from point to point.
If you want to refine that further, then you can note that the model above is perfectly good for infinitesimal distances, which means that you can iterate across individual deltas of the distance, calculus-style, each time using the current altitude to compute the ground distance and then using the same trigonometric ratio to compute the altitudinal-change contribution to the distance traveled. I'd probably do this in a for() loop with 10 to 100 pieces of the segment, and possibly by trial and error figure out the number of pieces required to get within epsilon of the true value. It would also be possible to work out the line integral to figure out the actual distance between the two points under this model.

Related

GLSL cube signed distance field implementation explanation?

I've been looking at and trying to understand the following bit of code
float sdBox( vec3 p, vec3 b )
{
vec3 d = abs(p) - b;
return min(max(d.x,max(d.y,d.z)),0.0) +
length(max(d,0.0));
}
I understand that length(d) handles the SDF case where the point is off to the 'corner' (ie. all components of d are positive) and that max(d.x, d.y, d.z) gives us the proper distance in all other cases. What I don't understand is how these two are combined here without the use of an if statement to check the signs of d's components.
When all of the d components are positive, the return expression can be reduced to length(d) because of the way min/max will evaluate - and when all of the d components are negative, we get max(d.x, d.y, d.z). But how am I supposed to understand the in-between cases? The ones where the components of d have mixed signs?
I've been trying to graph it out to no avail. I would really appreciate it if someone could explain this to me in geometrical/mathematical terms. Thanks.
If you like to know how It works It's better do the following steps:
1.first of all you should know definitions of shapes
2.It's always better to consider 2D shape of them, because three dimensions may be complex for you.
so let me to explain some shapes:
Circle
A circle is a simple closed shape. It is the set of all points in a plane that are at a given distance from a given point, the center.
You can use distance(), length() or sqrt() to calculate the distance to the center of the billboard.
The book of shaders - Chapter 7
Square
In geometry, a square is a regular quadrilateral, which means that it has four equal sides and four equal angles (90-degree angles).
I describe 2D shapes In before section now let me to describe 3D definition.
Sphere
A sphere is a perfectly round geometrical object in three-dimensional space that is the surface of a completely round ball.
Like a circle, which geometrically is an object in two-dimensional space, a sphere is defined mathematically as the set of points that are all at the same distance r from a given point, but in three-dimensional space.
Refrence - Wikipedia
Cube
In geometry, a cube is a three-dimensional solid object bounded by six square faces, facets or sides, with three meeting at each vertex.
Refrence : Wikipedia
Modeling with distance functions
now It's time to understanding modeling with distance functions
Sphere
As mentioned In last sections.In below code length() used to calculate the distance to the center of the billboard , and you can scale this shape by s parameter.
//Sphere - signed - exact
/// <param name="p">Position.</param>
/// <param name="s">Scale.</param>
float sdSphere( vec3 p, float s )
{
return length(p)-s;
}
Box
// Box - unsigned - exact
/// <param name="p">Position.</param>
/// <param name="b">Bound(Scale).</param>
float udBox( vec3 p, vec3 b )
{
return length(max(abs(p)-b,0.0));
}
length() used like previous example.
next we have max(x,0) It called Positive and negative parts
this is mean below code is equivalent:
float udBox( vec3 p, vec3 b )
{
vec3 value = abs(p)-b;
if(value.x<0.){
value.x = 0.;
}
if(value.y<0.){
value.y = 0.;
}
if(value.z<0.){
value.z = 0.;
}
return length(value);
}
step 1
if(value.x<0.){
value.x = 0.;
}
step 2
if(value.y<0.){
value.y = 0.;
}
step 3
if(value.z<0.){
value.z = 0.;
}
step 4
next we have absolution function.It used to remove additional parts.
Absolution Steps
Absolution step 1
if(value.x < -1.){
value.x = 1.;
}
Absolution step 2
if(value.y < -1.){
value.y = 1.;
}
Absolution step 3
if(value.z < -1.){
value.z = 1.;
}
Also you can make any shape by using Constructive solid geometry.
CSG is built on 3 primitive operations: intersection ( ∩ ), union ( ∪ ), and difference ( - ).
It turns out these operations are all concisely expressible when combining two surfaces expressed as SDFs.
float intersectSDF(float distA, float distB) {
return max(distA, distB);
}
float unionSDF(float distA, float distB) {
return min(distA, distB);
}
float differenceSDF(float distA, float distB) {
return max(distA, -distB);
}
I figured it out a while ago and wrote about this extensively in a blog post here: http://fabricecastel.github.io/blog/2016-02-11/main.html
Here's an excerpt (see the full post for a full explanation):
Consider the four points, A, B, C and D. Let's crudely reduce the distance function to try and get rid of the min/max functions in order to understand their effect (since that's what's puzzling about this function). The notation below is a little sloppy, I'm using square brackets to denote 2D vectors.
// 2D version of the function
d(p) = min(max(p.x, p.y), 0)
+ length(max(p, 0))
---
d(A) = min(max(-1, -1), 0)
+ length(max([-1, -1], 0))
d(A) = -1 + length[0, 0]
---
d(B) = min(max(1, 1), 0)
+ length(max([1, 1], 0))
d(B) = 0 + length[1, 1]
Ok, so far nothing special. When A is inside the square, we essentially get our first distance function based on planes/lines and when B is in the area where our first distance function is inaccurate, it gets zeroed out and we get the second distance function (the length). The trick lies in the other two cases C and D. Let's work them out.
d(C) = min(max(-1, 1), 0)
+ length(max([-1, 1], 0))
d(C) = 0 + length[0, 1]
---
d(D) = min(max(1, -1), 0)
+ length(max([-1, 1], 0))
d(D) = 0 + length[1, 0]
If you look back to the graph above, you'll note C' and D'. Those points have coordinates [0,1] and [1,0], respectively. This method uses the fact that both distance fields intersect on the axes - that D and D' lie at the same distance from the square.
If we zero out all negative component of a vector and take its length we will get the proper distance between the point and the square (for points outside of the square only). This is what max(d,0.0) does; a component-wise max operation. So long as the vector has at least one positive component, min(max(d.x,d.y),0.0) will resolve to 0 leaving us with only the second part of the equation. In the event that the point is inside the square, we want to return the first part of the equation (since it represents our first distance function). If all components of the vector are negative it's easy to see our condition will be met.
This understanding should tranlsate back into 3D seamlessly once you wrap your head around it. You may or may not have to draw a few graphs by hand to really "get" it - I know I did and would encourage you to do so if you're dissatisfied with my explanation.
Working this implementation into our own code, we get this:
float distanceToNearestSurface(vec3 p){
float s = 1.0;
vec3 d = abs(p) - vec3(s);
return min(max(d.x, max(d.y,d.z)), 0.0)
+ length(max(d,0.0));
}
And there you have it.

NON orthogonal projection : projecting a point onto a line at given direction (2d)

I need a solution to project a 2d point onto a 2d line at certain Direction .Here's what i've got so far : This is how i do orthogonal projection :
CVector2d project(Line line , CVector2d point)
{
CVector2d A = line.end - line.start;
CVector2d B = point - line start;
float dot = A.dotProduct(B);
float mag = A.getMagnitude();
float md = dot/mag;
return CVector2d (line.start + A * md);
}
Result :
(Projecting P onto line and the result is Pr):
but i need to project the point onto the line at given DIRECTION which should return a result like this (project point P1 onto line at specific Direction calculate Pr) :
How should I take Direction vector into account to calculate Pr ?
I can come up with 2 methods out of my head.
Personally I would do this using affine transformations (but seems you don not have this concept as you are using vectors not points). The procedure with affine transformations is easy. Rotate the points to one of the cardinal axes read the coordinate of your point zero the other value and inverse transform back. The reason for this strategy is that nearly all transformation procedures reduce to very simple human understandable operations with the affine transformation scheme. So no real work to do once you have the tools and data structures at hand.
However since you didn't see this coming I assume you want to hear a vector operation instead (because you either prefer the matrix operation or run away when its suggested, tough its the same thing). So you have the following situation:
This expressed as a equation system looks like (its intentionally this way to show you that it is NOT code but math at this point):
line.start.x + x*(line.end.x - line.start.x)+ y*direction.x = point.x
line.start.y + x*(line.end.y - line.start.y)+ y*direction.y = point.y
now this can be solved for x (and y)
x = (direction.y * line.start.x - direction.x * line.start.y -
direction.y * point.x + direction.x * point.y) /
(direction.y * line.end.x - direction.x * line.end.y -
direction.y * line.start.x + direction.x * line.start.y);
// the solution for y can be omitted you dont need it
y = -(line.end.y * line.start.x - line.end.x * line.start.y -
line.end.y * point.x + line.start.y * point.x + line.end.x * point.y -
line.start.x point.y)/
(-direction.y * line.end.x + direction.x * line.end.y +
direction.y * line.start.x - direction.x * line.start.y)
Calculation done with mathematica if I didn't copy anything wrong it should work. But I would never use this solution because its not understandable (although it is high school grade math, or at least it is where I am). But use space transformation as described above.

Un/pack additional set of UV coordinates into a 32bit RGBA field

I'm modding a game called Mount&Blade, currently trying to implement lightmapping through custom shaders.
As the in-game format doesn't allows more than one UV map per model and I need to carry the info of a second, non-overlapping parametrization somewhere, a field of four uints (RGBA, used for per-vertex coloring) is my only possibility.
At first thought about just using U,V=R,G but the precision isn't good enough.
Now I'm trying to encode them with the maximum precision available, using two fields (16bit) per coordinate. Snip of my Python exporter:
def decompose(a):
a=int(a*0xffff) #fill the entire range to get the maximum precision
aa =(a&0xff00)>>8 #decompose the first half and save it as an 8bit uint
ab =(a&0x00ff) #decompose the second half
return aa,ab
def compose(na,nb):
return (na<<8|nb)/0xffff
I'd like to know how to do the second part (composing, or unpacking it) in HLSL (DX9, shader model 2.0). Here's my try, close, but doesn't works:
//compose UV from n=(na<<8|nb)/0xffff
float2 thingie = float2(
float( ((In.Color.r*255.f)*256.f)+
(In.Color.g*255.f) )/65535.f,
float( ((In.Color.b*255.f)*256.f)+
(In.Color.w*255.f) )/65535.f
);
//sample the lightmap at that position
Output.RGBColor = tex2D(MeshTextureSamplerHQ, thingie);
Any suggestion or ingenious alternative is welcome.
Remember to normalize aa and ab after you decompose a.
Something like this:
(u1, u2) = decompose(u)
(v1, v2) = decompose(v)
color.r = float(u1) / 255.f
color.g = float(u2) / 255.f
color.b = float(v1) / 255.f
color.a = float(v2) / 255.f
The pixel shader:
float2 texc;
texc.x = (In.Color.r * 256.f + In.Color.g) / 257.f;
texc.y = (In.Color.b * 256.f + In.Color.a) / 257.f;

Calculate distance between two points in bing maps

Ihave a bing map, and a two points :
Point1,Point2 and i want to calculate the distance between these two points? is that possible?
and if i want to put a circle on the two third of the path between point1 and point2 and near point2 ...how can i make it?
Microsoft has a GeoCoordinate.GetDistanceTo Method, which uses the Haversine formula.
For me other implementation return NaN for distances that are too small. I haven't run into any issues with the built in function yet.
See Haversine or even better the Vincenty formula how to solve this problem.
The following code uses haversines way to get the distance:
public double GetDistanceBetweenPoints(double lat1, double long1, double lat2, double long2)
{
double distance = 0;
double dLat = (lat2 - lat1) / 180* Math.PI;
double dLong = (long2 - long1) / 180 * Math.PI;
double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2)
+ Math.Cos(lat1 / 180* Math.PI) * Math.Cos(lat2 / 180* Math.PI)
* Math.Sin(dLong/2) * Math.Sin(dLong/2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
//Calculate radius of earth
// For this you can assume any of the two points.
double radiusE = 6378135; // Equatorial radius, in metres
double radiusP = 6356750; // Polar Radius
//Numerator part of function
double nr = Math.Pow(radiusE * radiusP * Math.Cos(lat1 / 180 * Math.PI), 2);
//Denominator part of the function
double dr = Math.Pow(radiusE * Math.Cos(lat1 / 180 * Math.PI), 2)
+ Math.Pow(radiusP * Math.Sin(lat1 / 180 * Math.PI), 2);
double radius = Math.Sqrt(nr / dr);
//Calculate distance in meters.
distance = radius * c;
return distance; // distance in meters
}
You can find a good site with infos here.
You can use a geographic library for (re-)projection and calculation operations if you need more accurate results or want to do some math operations (e.g. transform a circle onto a sperioid/projection). Take a look at DotSpatial or SharpMap and the samples/unittests/sources there... this might help to solve your problem.
Anyway if you know the geodesic distance and bearing you can also calculate where resulting target position (center of your circle) is, e.g. see "Direct Problem" of Vincenty's algorithms. Here are also some useful algorithm implementations for silverlight/.net
You might also consider to post your questions at GIS Stackexchange. They discuss GIS related problems like yours. Take a look at the question for calculating lat long x-miles from point (as you already know the whole distance now) or see the discussion here about distance calculations. This question is related to the problem how to draw a point on a line in a given distance and is nearly the same (cause you need a center and radius).
Another option is to use ArcGIS API for Silverlight which can also display Bing Maps. It is open source and you can learn the things you need there (or just use them, cause they already exists in the SDK). See the Utilities examples tab within the samples.
As I already mentioned: Take a look at this page to get more infos regarding your problem. There you'll find a formula, Javascript code and an Excel sample for calculating a destination point by a given distance and bearing from start point (see headlines there).
It shouldn't be difficult to "transform" the code to your c#-world.

Calculating co-ordinate of a point on a path given a distance

I'm working on a project that surveys the condition of a road or highway using a calibrated trip computer connected to a rugged-PC. An operator keys in defect codes as they travel along a pre-defined route.
I need to show an indicator on the map screen that shows the vehicles current position, taking into account the distance data from the trip computer.
I know the exact lat lon co-ordinates at the starting point of each section of road, and the road is made up of a series of points.
The question is: how can I calculate the lat lon co-ordinates of the vehicle assuming that it has continued on the route and traveled a certain distance (e.g. 1.4km). The co-ordinates would be 'locked onto' the road line, as shown in blue on the diagram below.
Thanks,
Alex
Here is some Java-ish pseudocode, giving a solution using linear interpolation between points.
inputs: distance, points
// construct a list of segments from the points
segments = [];
for(point in points) {
if(not first point) {
seg = new segment(last_point, point)
add seg to segments
}
last_point = point
}
// calculate current lat and lon
for(segment in segments) {
if(distance < segment.length) {
alpha = distance / segment.length
lat = segment.start.lat * (1.0 - alpha) + segment.end.lat * alpha
lon = segment.start.lon * (1.0 - alpha) + segment.end.lon * alpha
return (lat, lon)
} else {
distance = distance - segment.length
}
}
You might also want to consider spline interpolation, which could be more accurate. It will require some more maths, but the above idea can still be applied.

Resources