1
0
Fork 0

Fix route-path display of long legs.

When leg spans more than a threshold (currently 0.5 degrees) of longitude, interpolate the actual path flown by the GPS/RM, which is a true great-circle. Previous we rendered a Rhumb line which does not agree at all. (Especially noticeable in the MapWidget and NavDisplay code, both of which use RoutePath to tesselate the route before rendering)
This commit is contained in:
James Turner 2012-12-31 17:21:05 +00:00
parent 6c3853fd0d
commit 5ff8311acc
2 changed files with 57 additions and 5 deletions

View file

@ -52,6 +52,25 @@ double pointsKnownDistanceFromGC(const SGGeoc& a, const SGGeoc&b, const SGGeoc&
return SGMiscd::min(dp1Nm, dp2Nm);
}
// http://williams.best.vwh.net/avform.htm#Int
double latitudeForGCLongitude(const SGGeoc& a, const SGGeoc& b, double lon)
{
#if 0
Intermediate points {lat,lon} lie on the great circle connecting points 1 and 2 when:
lat=atan((sin(lat1)*cos(lat2)*sin(lon-lon2)
-sin(lat2)*cos(lat1)*sin(lon-lon1))/(cos(lat1)*cos(lat2)*sin(lon1-lon2)))
#endif
double lonDiff = a.getLongitudeRad() - b.getLongitudeRad();
double cosLat1 = cos(a.getLatitudeRad()),
cosLat2 = cos(b.getLatitudeRad());
double x = sin(a.getLatitudeRad()) * cosLat2 * sin(lon - b.getLongitudeRad());
double y = sin(b.getLatitudeRad()) * cosLat1 * sin(lon - a.getLongitudeRad());
double denom = cosLat1 * cosLat2 * sin(lonDiff);
double lat = atan((x - y) / denom);
return lat;
}
RoutePath::RoutePath(const flightgear::WayptVec& wpts) :
_waypts(wpts)
{
@ -89,17 +108,24 @@ SGGeodVec RoutePath::pathForIndex(int index) const
}
SGGeodVec r;
SGGeod pos;
if (!computedPositionForIndex(index-1, pos)) {
SGGeod from, to;
if (!computedPositionForIndex(index-1, from)) {
return SGGeodVec();
}
r.push_back(pos);
if (!computedPositionForIndex(index, pos)) {
r.push_back(from);
if (!computedPositionForIndex(index, to)) {
return SGGeodVec();
}
r.push_back(pos);
// compute rounding offset, we want to round towards the direction of travel
// which depends on the east/west sign of the longitude change
double lonDelta = to.getLongitudeDeg() - from.getLongitudeDeg();
if (fabs(lonDelta) > 0.5) {
interpolateGreatCircle(from, to, r);
}
r.push_back(to);
if (_waypts[index]->type() == "runway") {
// runways get an extra point, at the end. this is particularly
@ -111,6 +137,30 @@ SGGeodVec RoutePath::pathForIndex(int index) const
return r;
}
void RoutePath::interpolateGreatCircle(const SGGeod& aFrom, const SGGeod& aTo, SGGeodVec& r) const
{
SGGeoc gcFrom = SGGeoc::fromGeod(aFrom),
gcTo = SGGeoc::fromGeod(aTo);
double lonDelta = gcTo.getLongitudeRad() - gcFrom.getLongitudeRad();
if (fabs(lonDelta) < 1e-3) {
return;
}
lonDelta = SGMiscd::normalizeAngle2(lonDelta);
int steps = static_cast<int>(fabs(lonDelta) * SG_RADIANS_TO_DEGREES * 2);
double lonStep = (lonDelta / steps);
double lon = gcFrom.getLongitudeRad() + lonStep;
for (int s=0; s < (steps - 1); ++s) {
double lat = latitudeForGCLongitude(gcFrom, gcTo, lon);
r.push_back(SGGeod::fromGeoc(SGGeoc::fromRadM(lon, lat, SGGeodesy::EQURAD)));
//SG_LOG(SG_GENERAL, SG_INFO, "lon:" << lon * SG_RADIANS_TO_DEGREES << " gives lat " << lat * SG_RADIANS_TO_DEGREES);
lon += lonStep;
}
}
SGGeod RoutePath::positionForIndex(int index) const
{
SGGeod r;

View file

@ -55,6 +55,8 @@ private:
double computeAltitudeForIndex(int index) const;
double computeTrackForIndex(int index) const;
void interpolateGreatCircle(const SGGeod& aFrom, const SGGeod& aTo, SGGeodVec& r) const;
/**
* Find the distance (in Nm) to climb/descend a height in feet
*/