1
0
Fork 0

Flight-plan: improve GPX loading

- support elevation values
- parse the first/last points in case they are airports, and set
as the plan airports in this case.

https://sourceforge.net/p/flightgear/codetickets/2066/
This commit is contained in:
James Turner 2018-10-08 15:09:46 +01:00
parent 22c5c456cc
commit c450fa9f4e
3 changed files with 76 additions and 36 deletions

View file

@ -100,7 +100,7 @@ public:
if (leg->waypoint()->source()) { if (leg->waypoint()->source()) {
return QString::fromStdString(leg->waypoint()->source()->name()); return QString::fromStdString(leg->waypoint()->source()->name());
} }
break; return QString{}; // avoud undefined-value QML error if we return a null variant
} }
case LegTerminatorTypeRole: case LegTerminatorTypeRole:
@ -163,6 +163,7 @@ public:
{ {
QTimer::singleShot(0, p->_legs, &LegsModel::waypointsChanged); QTimer::singleShot(0, p->_legs, &LegsModel::waypointsChanged);
p->waypointsChanged(); p->waypointsChanged();
p->infoChanged();
} }
FlightPlanController* p; FlightPlanController* p;

View file

@ -58,6 +58,11 @@ Item {
var ok = _launcher.flightPlan.loadPlan(); var ok = _launcher.flightPlan.loadPlan();
if (ok) { if (ok) {
route.text = _launcher.flightPlan.icaoRoute; route.text = _launcher.flightPlan.icaoRoute;
// we don't use a binding here, manually synchronise
// these values
departureEntry.selectAirport(_launcher.flightPlan.departure.guid);
destinationICAO.selectAirport(_launcher.flightPlan.destination.guid);
} }
} }
} }
@ -192,6 +197,7 @@ Item {
spacing: Style.margin spacing: Style.margin
AirportEntry { AirportEntry {
id: departureEntry
label: qsTr("Departure airport:") label: qsTr("Departure airport:")
Component.onCompleted: { Component.onCompleted: {

View file

@ -860,29 +860,28 @@ class GpxXmlVisitor : public XMLVisitor
public: public:
GpxXmlVisitor(FlightPlan* fp) : _fp(fp), _lat(-9999), _lon(-9999) {} GpxXmlVisitor(FlightPlan* fp) : _fp(fp), _lat(-9999), _lon(-9999) {}
virtual void startElement (const char * name, const XMLAttributes &atts); void startElement (const char * name, const XMLAttributes &atts) override;
virtual void endElement (const char * name); void endElement (const char * name) override;
virtual void data (const char * s, int length); void data (const char * s, int length) override;
const WayptVec& waypoints() const { return _waypoints; }
private: private:
FlightPlan* _fp; FlightPlan* _fp;
double _lat, _lon; double _lat, _lon, _elevationM;
string _element; string _element;
string _waypoint; string _waypoint;
WayptVec _waypoints;
}; };
void GpxXmlVisitor::startElement(const char * name, const XMLAttributes &atts) void GpxXmlVisitor::startElement(const char * name, const XMLAttributes &atts)
{ {
_element = name; _element = name;
if (strcmp(name, "rtept")==0) if (!strcmp(name, "rtept")) {
{ _waypoint.clear();
_waypoint = ""; _lat = _lon = _elevationM = -9999;
_lat = _lon = -9999;
const char* slat = atts.getValue("lat"); const char* slat = atts.getValue("lat");
const char* slon = atts.getValue("lon"); const char* slon = atts.getValue("lon");
if (slat && slon) if (slat && slon) {
{
_lat = atof(slat); _lat = atof(slat);
_lon = atof(slon); _lon = atof(slon);
} }
@ -892,25 +891,42 @@ void GpxXmlVisitor::startElement(const char * name, const XMLAttributes &atts)
void GpxXmlVisitor::data(const char * s, int length) void GpxXmlVisitor::data(const char * s, int length)
{ {
// use "name" when given, otherwise use "cmt" (comment) as ID // use "name" when given, otherwise use "cmt" (comment) as ID
if ((_element == "name")|| if ((_element == "name") ||
((_waypoint == "")&&(_element == "cmt"))) ((_waypoint.empty()) && (_element == "cmt")))
{ {
char* buf = (char*) malloc(length+1); _waypoint = std::string(s, static_cast<size_t>(length));
memcpy(buf, s, length); }
buf[length] = 0;
_waypoint = buf; if (_element == "ele") {
free(buf); _elevationM = atof(s);
} }
} }
void GpxXmlVisitor::endElement(const char * name) void GpxXmlVisitor::endElement(const char * name)
{ {
_element = ""; _element.clear();
if (strcmp(name, "rtept") == 0) if (!strcmp(name, "rtept")) {
{ if (_lon > -9990.0) {
if (_lon > -9990.0) const auto geod = SGGeod::fromDeg(_lon, _lat);
{ auto pos = FGPositioned::findClosestWithIdent(_waypoint, geod);
_fp->insertWayptAtIndex(new BasicWaypt(SGGeod::fromDeg(_lon, _lat), _waypoint.c_str(), NULL), -1); WayptRef wp;
if (pos) {
// check distance
const auto dist = SGGeodesy::distanceM(geod, pos->geod());
if (dist < 800.0) {
wp = new NavaidWaypoint(pos, _fp);
}
}
if (!wp) {
wp = new BasicWaypt(geod, _waypoint, _fp);
}
if (_elevationM > -9990.0) {
wp->setAltitude(_elevationM * SG_METER_TO_FEET, RESTRICT_AT);
}
_waypoints.push_back(wp);
} }
} }
} }
@ -918,31 +934,48 @@ void GpxXmlVisitor::endElement(const char * name)
/** Load a flightplan in GPX format */ /** Load a flightplan in GPX format */
bool FlightPlan::loadGpxFormat(const SGPath& path) bool FlightPlan::loadGpxFormat(const SGPath& path)
{ {
if (path.lower_extension() != "gpx") if (path.lower_extension() != "gpx") {
{
// not a valid GPX file // not a valid GPX file
return false; return false;
} }
_legs.clear(); GpxXmlVisitor gpxVisitor(this);
GpxXmlVisitor gpxVistor(this); try {
try readXML(path, gpxVisitor);
{ } catch (sg_exception& e) {
readXML(path, gpxVistor);
} catch (sg_exception& e)
{
// XML parsing fails => not a GPX XML file // XML parsing fails => not a GPX XML file
SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan in GPX format: '" << e.getOrigin() SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan in GPX format: '" << e.getOrigin()
<< "'. " << e.getMessage()); << "'. " << e.getMessage());
return false; return false;
} }
if (numLegs() == 0) if (gpxVisitor.waypoints().empty()) {
{
SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan in GPX format. No route found."); SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan in GPX format. No route found.");
return false; return false;
} }
clear();
// copy in case we need to modify
WayptVec wps = gpxVisitor.waypoints();
// detect airports
const auto depApt = FGAirport::findByIdent(wps.front()->ident());
const auto destApt = FGAirport::findByIdent(wps.back()->ident());
if (depApt) {
wps.erase(wps.begin());
setDeparture(depApt);
}
// for a single-element waypoint list consisting of a single airport ID,
// don't crash
if (destApt && !wps.empty()) {
wps.pop_back();
setDestination(destApt);
}
insertWayptsAtIndex(wps, -1);
return true; return true;
} }