1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-19 22:35:12 +03:00

Fixes #2326: Google Earth export GPS glitch filtering and lat/lon synchronization (ported from master)

This commit is contained in:
Damjan Adamic 2015-06-19 18:39:45 +02:00
parent 6a3f277fde
commit c0afcb51f7
4 changed files with 195 additions and 68 deletions

View file

@ -1060,3 +1060,55 @@ bool caseInsensitiveLessThan(const QString &s1, const QString &s2)
{
return s1.toLower() < s2.toLower();
}
bool GpsGlitchFilter::isGlitch(double latitude, double longitude)
{
if ((fabs(latitude) < 0.1) && (fabs(longitude) < 0.1)) {
return true;
}
if (lastValid) {
if (fabs(latitude - lastLat) > 0.01) {
// qDebug() << "GpsGlitchFilter(): latitude glitch " << latitude << lastLat;
if ( ++glitchCount < 10) {
return true;
}
}
if (fabs(longitude - lastLon) > 0.01) {
// qDebug() << "GpsGlitchFilter(): longitude glitch " << longitude << lastLon;
if ( ++glitchCount < 10) {
return true;
}
}
}
lastLat = latitude;
lastLon = longitude;
lastValid = true;
glitchCount = 0;
return false;
}
bool GpsLatLonFilter::isValid(const QString & latitude, const QString & longitude)
{
if (lastLat == latitude) {
return false;
}
if (lastLon == longitude) {
return false;
}
lastLat = latitude;
lastLon = longitude;
return true;
}
double toDecimalCoordinate(const QString & value)
{
if (value.isEmpty()) return 0.0;
double temp = int(value.left(value.length()-1).toDouble() / 100);
double result = temp + (value.left(value.length() - 1).toDouble() - temp * 100) / 60.0;
QChar direction = value.at(value.size()-1);
if ((direction == 'S') || (direction == 'W')) {
result = -result;
}
return result;
}

View file

@ -181,4 +181,31 @@ QSet<QString> getFilesSet(const QString &path, const QStringList &filter, int ma
bool caseInsensitiveLessThan(const QString &s1, const QString &s2);
class GpsGlitchFilter
{
public:
GpsGlitchFilter() : lastValid(false), glitchCount(0) {};
bool isGlitch(double latitude, double longitude);
private:
bool lastValid;
int glitchCount;
double lastLat;
double lastLon;
};
class GpsLatLonFilter
{
public:
GpsLatLonFilter() {};
bool isValid(const QString & latitude, const QString & longitude);
private:
QString lastLat;
QString lastLon;
};
double toDecimalCoordinate(const QString & value);
#endif // HELPERS_H

View file

@ -221,38 +221,88 @@ void logsDialog::selectionChanged()
}
}
void logsDialog::on_mapsButton_clicked()
QList<QStringList> logsDialog::filterGePoints(const QList<QStringList> & input)
{
int n = csvlog.count(); // number of points in graph
if (n==0) return;
int latcol=0, longcol=0, altcol=0, speedcol=0;
int itemSelected=0.;
bool rangeSelected=false;
ui->FieldsTW->setDisabled(true);
ui->logTable->setDisabled(true);
QList<QStringList> result;
QString gePath=g.gePath();
if (gePath.isEmpty() || !QFile(gePath).exists()) {
ui->FieldsTW->setDisabled(false);
ui->logTable->setDisabled(false);
return;
int n = input.count();
if (n == 0) {
return result;
}
QSet<int> nondataCols;
for (int i=1; i<csvlog.at(0).count(); i++) {
int latcol=0, longcol=0;
for (int i=1; i<input.at(0).count(); i++) {
//Long,Lat,Course,GPS Speed,GPS Alt
if (csvlog.at(0).at(i).contains("Long")) {
if (input.at(0).at(i).contains("Long")) {
longcol=i;
}
if (input.at(0).at(i).contains("Lat")) {
latcol=i;
}
}
if (longcol==0 || latcol==0) {
return result;
}
result.append(input.at(0));
bool rangeSelected = ui->logTable->selectionModel()->selectedRows().length() > 0;
GpsGlitchFilter glitchFilter;
GpsLatLonFilter latLonFilter;
for (int i = 1; i < n; i++) {
if ((ui->logTable->item(i-1, 1)->isSelected() && rangeSelected) || !rangeSelected) {
QString latitude = input.at(i).at(latcol).trimmed();
QString longitude = input.at(i).at(longcol).trimmed();
double flatitude = toDecimalCoordinate(latitude);
double flongitude = toDecimalCoordinate(longitude);
// glitch filter
if ( glitchFilter.isGlitch(flatitude, flongitude) ) {
qDebug() << "filterGePoints(): GPS glitch detected at" << i << flatitude << flongitude;
continue;
}
// lat long pair filter
if ( !latLonFilter.isValid(latitude, longitude) ) {
// qDebug() << "filterGePoints(): Lat-Lon pair wrong, skipping at" << i << latitude << longitude;
continue;
}
// qDebug() << "point " << latitude << longitude;
result.append(input.at(i));
}
}
qDebug() << "filterGePoints(): filtered from" << input.count() << "to " << result.count() << "points";
return result;
}
void logsDialog::exportToGoogleEarth()
{
// filter data points
QList<QStringList> dataPoints = filterGePoints(csvlog);
int n = dataPoints.count(); // number of points to export
if (n==0) return;
int latcol=0, longcol=0, altcol=0, speedcol=0;
QSet<int> nondataCols;
for (int i=1; i<dataPoints.at(0).count(); i++) {
//Long,Lat,Course,GPS Speed,GPS Alt
if (dataPoints.at(0).at(i).contains("Long")) {
longcol=i;
nondataCols << i;
}
if (csvlog.at(0).at(i).contains("Lat")) {
if (dataPoints.at(0).at(i).contains("Lat")) {
latcol=i;
nondataCols << i;
}
if (csvlog.at(0).at(i).contains("GPS Alt")) {
if (dataPoints.at(0).at(i).contains("GPS Alt")) {
altcol=i;
nondataCols << i;
}
if (csvlog.at(0).at(i).contains("GPS Speed")) {
if (dataPoints.at(0).at(i).contains("GPS Speed")) {
speedcol=i;
nondataCols << i;
}
@ -260,15 +310,6 @@ void logsDialog::on_mapsButton_clicked()
if (longcol==0 || latcol==0 || altcol==0) {
return;
}
for (int i=1; i<n; i++) {
if (ui->logTable->item(i-1,1)->isSelected()) {
rangeSelected=true;
itemSelected++;
}
}
if (itemSelected==0) {
itemSelected=n-1;
}
QString geIconFilename = generateProcessUniqueTempFileName("track0.png");
if (QFile::exists(geIconFilename)) {
@ -286,25 +327,25 @@ void logsDialog::on_mapsButton_clicked()
tr("Cannot write file %1:\n%2.")
.arg(geFilename)
.arg(geFile.errorString()));
ui->FieldsTW->setDisabled(false);
ui->logTable->setDisabled(false);
return;
}
QString latitude,longitude;
double flatitude, flongitude,temp;
QTextStream outputStream(&geFile);
// file header
outputStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\" xmlns:gx=\"http://www.google.com/kml/ext/2.2\">\n";
outputStream << "\t<Document>\n\t\t<name>" << logFilename << "</name>\n";
outputStream << "\t\t<Style id=\"multiTrack_n\">\n\t\t\t<IconStyle>\n\t\t\t\t<Icon>\n\t\t\t\t\t<href>file://" << geIconFilename << "</href>\n\t\t\t\t</Icon>\n\t\t\t</IconStyle>\n\t\t\t<LineStyle>\n\t\t\t\t<color>991081f4</color>\n\t\t\t\t<width>6</width>\n\t\t\t</LineStyle>\n\t\t</Style>\n";
outputStream << "\t\t<Style id=\"multiTrack_h\">\n\t\t\t<IconStyle>\n\t\t\t\t<scale>0</scale>\n\t\t\t\t<Icon>\n\t\t\t\t\t<href>file://" << QDir::tempPath() << "/track0.png</href>\n\t\t\t\t</Icon>\n\t\t\t</IconStyle>\n\t\t\t<LineStyle>\n\t\t\t\t<color>991081f4</color>\n\t\t\t\t<width>8</width>\n\t\t\t</LineStyle>\n\t\t</Style>\n";
outputStream << "\t\t<Style id=\"multiTrack_h\">\n\t\t\t<IconStyle>\n\t\t\t\t<scale>0</scale>\n\t\t\t\t<Icon>\n\t\t\t\t\t<href>file://" << geIconFilename << "</href>\n\t\t\t\t</Icon>\n\t\t\t</IconStyle>\n\t\t\t<LineStyle>\n\t\t\t\t<color>991081f4</color>\n\t\t\t\t<width>8</width>\n\t\t\t</LineStyle>\n\t\t</Style>\n";
outputStream << "\t\t<StyleMap id=\"multiTrack\">\n\t\t\t<Pair>\n\t\t\t\t<key>normal</key>\n\t\t\t\t<styleUrl>#multiTrack_n</styleUrl>\n\t\t\t</Pair>\n\t\t\t<Pair>\n\t\t\t\t<key>highlight</key>\n\t\t\t\t<styleUrl>#multiTrack_h</styleUrl>\n\t\t\t</Pair>\n\t\t</StyleMap>\n";
outputStream << "\t\t<Style id=\"lineStyle\">\n\t\t\t<LineStyle>\n\t\t\t\t<color>991081f4</color>\n\t\t\t\t<width>6</width>\n\t\t\t</LineStyle>\n\t\t</Style>\n";
outputStream << "\t\t<Schema id=\"schema\">\n";
outputStream << "\t\t\t<gx:SimpleArrayField name=\"GPSSpeed\" type=\"float\">\n\t\t\t\t<displayName>GPS Speed</displayName>\n\t\t\t</gx:SimpleArrayField>\n";
// declare additional fields
for (int i=0; i<csvlog.at(0).count()-2; i++) {
for (int i=0; i<dataPoints.at(0).count()-2; i++) {
if (ui->FieldsTW->item(0,i)->isSelected() && !nondataCols.contains(i+2)) {
QString origName = csvlog.at(0).at(i+2);
QString origName = dataPoints.at(0).at(i+2);
QString safeName = origName;
safeName.replace(" ","_");
outputStream << "\t\t\t<gx:SimpleArrayField name=\""<< safeName <<"\" ";
@ -312,7 +353,6 @@ void logsDialog::on_mapsButton_clicked()
outputStream << ">\n\t\t\t\t<displayName>" << origName << "</displayName>\n\t\t\t</gx:SimpleArrayField>\n";
}
}
outputStream << "\t\t</Schema>\n";
QString planeName;
if (logFilename.indexOf("-")>0) {
@ -320,60 +360,55 @@ void logsDialog::on_mapsButton_clicked()
} else {
planeName=logFilename;
}
outputStream << "\t\t</Schema>\n";
outputStream << "\t\t<Folder>\n\t\t\t<name>Log Data</name>\n\t\t\t<Placemark>\n\t\t\t\t<name>" << planeName << "</name>";
outputStream << "\n\t\t\t\t<styleUrl>#multiTrack</styleUrl>";
outputStream << "\n\t\t\t\t<gx:Track>\n";
outputStream << "\n\t\t\t\t\t<altitudeMode>absolute</altitudeMode>\n";
// time data points
for (int i=1; i<n; i++) {
if ((ui->logTable->item(i-1,1)->isSelected() &&rangeSelected) || !rangeSelected) {
QString tstamp=csvlog.at(i).at(0)+QString("T")+csvlog.at(i).at(1)+QString("Z");
QString tstamp=dataPoints.at(i).at(0)+QString("T")+dataPoints.at(i).at(1)+QString("Z");
outputStream << "\t\t\t\t\t<when>"<< tstamp <<"</when>\n";
}
// coordinate data points
for (int i=1; i<n; i++) {
QString latitude=dataPoints.at(i).at(latcol).trimmed();
QString longitude=dataPoints.at(i).at(longcol).trimmed();
latitude.sprintf("%3.8f", toDecimalCoordinate(latitude));
longitude.sprintf("%3.8f", toDecimalCoordinate(longitude));
outputStream << "\t\t\t\t\t<gx:coord>" << longitude << " " << latitude << " " << dataPoints.at(i).at(altcol).toFloat() << " </gx:coord>\n" ;
}
for (int i=1; i<n; i++) {
if ((ui->logTable->item(i-1,1)->isSelected() &&rangeSelected) || !rangeSelected) {
latitude=csvlog.at(i).at(latcol).trimmed();
longitude=csvlog.at(i).at(longcol).trimmed();
temp=int(latitude.left(latitude.length()-1).toDouble()/100);
flatitude=temp+(latitude.left(latitude.length()-1).toDouble()-temp*100)/60.0;
temp=int(longitude.left(longitude.length()-1).toDouble()/100);
flongitude=temp+(longitude.left(longitude.length()-1).toDouble()-temp*100)/60.0;
if (latitude.right(1)!="N") {
flatitude*=-1;
}
if (longitude.right(1)!="E") {
flongitude*=-1;
}
latitude.sprintf("%3.8f", flatitude);
longitude.sprintf("%3.8f", flongitude);
outputStream << "\t\t\t\t\t<gx:coord>" << longitude << " " << latitude << " " << csvlog.at(i).at(altcol).toFloat() << " </gx:coord>\n" ;
}
}
// additional data for data points
outputStream << "\t\t\t\t\t<ExtendedData>\n\t\t\t\t\t\t<SchemaData schemaUrl=\"#schema\">\n";
// gps speed data points
outputStream << "\t\t\t\t\t\t\t<gx:SimpleArrayData name=\"GPSSpeed\">\n";
for (int i=1; i<n; i++) {
if ((ui->logTable->item(i-1,1)->isSelected() &&rangeSelected) || !rangeSelected) {
outputStream << "\t\t\t\t\t\t\t\t<gx:value>"<< csvlog.at(i).at(speedcol) <<"</gx:value>\n";
}
outputStream << "\t\t\t\t\t\t\t\t<gx:value>"<< dataPoints.at(i).at(speedcol) <<"</gx:value>\n";
}
outputStream << "\t\t\t\t\t\t\t</gx:SimpleArrayData>\n";
// add values for additional fields
for (int i=0; i<csvlog.at(0).count()-2; i++) {
for (int i=0; i<dataPoints.at(0).count()-2; i++) {
if (ui->FieldsTW->item(0,i)->isSelected() && !nondataCols.contains(i+2)) {
QString safeName = csvlog.at(0).at(i+2);;
QString safeName = dataPoints.at(0).at(i+2);;
safeName.replace(" ","_");
outputStream << "\t\t\t\t\t\t\t<gx:SimpleArrayData name=\""<< safeName <<"\">\n";
for (int j=1; j<n; j++) {
if ((ui->logTable->item(j-1,1)->isSelected() &&rangeSelected) || !rangeSelected) {
outputStream << "\t\t\t\t\t\t\t\t<gx:value>"<< csvlog.at(j).at(i+2) <<"</gx:value>\n";
}
outputStream << "\t\t\t\t\t\t\t\t<gx:value>"<< dataPoints.at(j).at(i+2) <<"</gx:value>\n";
}
outputStream << "\t\t\t\t\t\t\t</gx:SimpleArrayData>\n";
}
}
outputStream << "\t\t\t\t\t\t</SchemaData>\n\t\t\t\t\t</ExtendedData>\n\t\t\t\t</gx:Track>\n\t\t\t</Placemark>\n\t\t</Folder>\n\t</Document>\n</kml>";
geFile.close();
QString gePath = g.gePath();
QStringList parameters;
#ifdef __APPLE__
parameters << "-a";
@ -383,6 +418,15 @@ void logsDialog::on_mapsButton_clicked()
parameters << geFilename;
QProcess *process = new QProcess(this);
process->start(gePath, parameters);
}
void logsDialog::on_mapsButton_clicked()
{
ui->FieldsTW->setDisabled(true);
ui->logTable->setDisabled(true);
exportToGoogleEarth();
ui->FieldsTW->setDisabled(false);
ui->logTable->setDisabled(false);
}

View file

@ -67,7 +67,6 @@ private:
Ui::logsDialog *ui;
QCPAxisRect *axisRect;
QCPLegend *rightLegend;
bool cvsFileParse();
bool plotLock;
QString logFilename;
@ -76,6 +75,11 @@ private:
double yAxesRatios[AXES_LIMIT];
minMax yAxesRanges[AXES_LIMIT];
bool cvsFileParse();
QList<QStringList> filterGePoints(const QList<QStringList> & input);
void exportToGoogleEarth();
};
#endif // LOGSDIALOG_H