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:
parent
22c5c456cc
commit
c450fa9f4e
3 changed files with 76 additions and 36 deletions
|
@ -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;
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue