#include "ct_reader_trajectory.h"
#include "ct_view/tools/ct_textfileconfigurationdialog.h"
#include "ct_coordinates/tools/ct_coordinatesystemmanager.h"
#include "ct_log/ct_logmanager.h"
#define CHK_ERR(argFunc, argErrStr) if(!error && !argFunc) { error = true; PS_LOG->addErrorMessage(LogInterface::reader, argErrStr); }
#define X_COLUMN "Easting"
#define Y_COLUMN "Northing"
#define Z_COLUMN "Height"
#define GPS_TIME_COLUMN "GPStime"
CT_Reader_Trajectory::CT_Reader_Trajectory(int subMenuLevel) : SuperClass(subMenuLevel)
{
m_firstConfiguration = true;
m_columnXIndex = -1;
m_columnYIndex = -1;
m_columnZIndex = -1;
m_columnGPSTimeIndex = -1;
m_nLinesToSkip = 0;
m_hasHeader = false;
m_separator = ".";
m_localeName = "";
addNewReadableFormat(FileFormat(QStringList() << "xyz" << "asc" << "txt" << "csv" << "ptx", tr("Fichiers ascii")));
setToolTip(tr("Chargement d'un fichier de trajectoire au format ASCII.
"
"L'import est configurable, le fichier devant contenir les champs suivants :
"
"- X : Coordonnée X du points
"
"- Y : Coordonnée Y du point
"
"- Z : Coordonnée Y du point
"
"- GPSTime : Temps GPS du point
"));
}
CT_Reader_Trajectory::CT_Reader_Trajectory(const CT_Reader_Trajectory &other) : SuperClass(other)
{
m_firstConfiguration = other.m_firstConfiguration;
m_columnXIndex = other.m_columnXIndex;
m_columnYIndex = other.m_columnYIndex;
m_columnZIndex = other.m_columnZIndex;
m_columnGPSTimeIndex = other.m_columnGPSTimeIndex;
m_nLinesToSkip = other.m_nLinesToSkip;
m_hasHeader = other.m_hasHeader;
m_separator = other.m_separator;
m_localeName = other.m_localeName;
}
QString CT_Reader_Trajectory::displayableName() const
{
return tr("Trajectoire, Fichiers ASCII");
}
bool CT_Reader_Trajectory::configure()
{
// Columns that can be found in the ascii file
QList fieldList;
fieldList.append(CT_TextFileConfigurationFields(X_COLUMN, QRegExp(" *[xX] *| *[eE][aA][sS][tT] *"))); // x
fieldList.append(CT_TextFileConfigurationFields(Y_COLUMN, QRegExp(" *[yY] *| *[nN][oO][rR][tT][hH] *"))); // y
fieldList.append(CT_TextFileConfigurationFields(Z_COLUMN, QRegExp(" *[zZ] *| *[hH][eE][iI][gG][hH][tT] *| *[aA][lL][tT][iI] *"))); // z
fieldList.append(CT_TextFileConfigurationFields(GPS_TIME_COLUMN, QRegExp("[gG][pP][sS] *| *[tT][iI][mM][eE] *"))); // GPS Time
// a configurable dialog that help the user to select the right column and auto-detect some columns
CT_TextFileConfigurationDialog dialog(fieldList, nullptr, filepath());
dialog.setFileExtensionAccepted(readableFormats());
dialog.setFilePathCanBeModified(filePathCanBeModified());
if (QFileInfo(filepath()).suffix() == "ptx")
{
m_hasHeader = false;
m_nLinesToSkip = 6;
m_separator = " ";
dialog.setHeader(m_hasHeader);
dialog.setNLinesToSkip(m_nLinesToSkip);
dialog.setSeparator(m_separator);
}
// table that link a sought column to a column in the ascii file
QMap corresp;
// if it is not the first configuration
if(!m_firstConfiguration)
{
// we will set by default columns detected in last configuration
corresp.insert(X_COLUMN, m_columnXIndex);
corresp.insert(Y_COLUMN, m_columnYIndex);
corresp.insert(Z_COLUMN, m_columnZIndex);
corresp.insert(GPS_TIME_COLUMN, m_columnGPSTimeIndex);
// and other elements like header, number of lines to skip, etc...
dialog.setHeader(m_hasHeader);
dialog.setNLinesToSkip(m_nLinesToSkip);
dialog.setSeparator(m_separator);
dialog.setQLocale(m_localeName);
dialog.setFieldColumnsSelected(corresp);
}
if(dialog.exec() != QDialog::Accepted)
return false;
// get the link between sought columns and column in the ascii file selected by the user
corresp = dialog.getNeededFieldColumns();
if((corresp.value(X_COLUMN, -1) < 0)
|| (corresp.value(Y_COLUMN, -1) < 0)
|| (corresp.value(Z_COLUMN, -1) < 0))
return false;
m_firstConfiguration = false;
int columnXIndex = corresp.value(X_COLUMN);
int columnYIndex = corresp.value(Y_COLUMN);
int columnZIndex = corresp.value(Z_COLUMN);
int columnGPSTimeIndex = corresp.value(GPS_TIME_COLUMN, -1);
m_columnXIndex = columnXIndex;
m_columnYIndex = columnYIndex;
m_columnZIndex = columnZIndex;
m_columnGPSTimeIndex = columnGPSTimeIndex;
m_nLinesToSkip = dialog.getNlinesToSkip();
m_hasHeader = dialog.hasHeader();
m_separator = dialog.getSeparator();
m_localeName = dialog.getQLocaleName();
if(!filePathCanBeModified() && !filepath().isEmpty())
return true;
return setFilePath(dialog.getFileNameWithPath());
}
void CT_Reader_Trajectory::saveSettings(SettingsWriterInterface &writer) const
{
SuperClass::saveSettings(writer);
writer.addParameter(this, "HasHeader", m_hasHeader);
writer.addParameter(this, "NLinesToSkip", m_nLinesToSkip);
writer.addParameter(this, "Separator", m_separator);
writer.addParameter(this, "LocaleName", m_localeName);
writer.addParameter(this, "ColumnXIndex", m_columnXIndex);
writer.addParameter(this, "ColumnYIndex", m_columnYIndex);
writer.addParameter(this, "ColumnZIndex", m_columnZIndex);
writer.addParameter(this, "ColumnGPSTimeIndex", m_columnGPSTimeIndex);
}
bool CT_Reader_Trajectory::restoreSettings(SettingsReaderInterface &reader)
{
if(!SuperClass::restoreSettings(reader))
return false;
QVariant value;
if(reader.parameter(this, "HasHeader", value))
m_hasHeader = value.toBool();
if(reader.parameter(this, "NLinesToSkip", value))
m_nLinesToSkip = value.toInt();
if(reader.parameter(this, "Separator", value))
m_separator = value.toString();
if(reader.parameter(this, "LocaleName", value))
m_localeName = value.toString();
if(reader.parameter(this, "ColumnXIndex", value))
m_columnXIndex = value.toInt();
if(reader.parameter(this, "ColumnYIndex", value))
m_columnYIndex = value.toInt();
if(reader.parameter(this, "ColumnZIndex", value))
m_columnZIndex = value.toInt();
if(reader.parameter(this, "ColumnGPSTimeIndex", value))
m_columnGPSTimeIndex = value.toInt();
return true;
}
void CT_Reader_Trajectory::setXColumnIndex(int index)
{
m_columnXIndex = index;
}
void CT_Reader_Trajectory::setYColumnIndex(int index)
{
m_columnYIndex = index;
}
void CT_Reader_Trajectory::setZColumnIndex(int index)
{
m_columnZIndex = index;
}
void CT_Reader_Trajectory::setGPSTimeColumnIndex(int index)
{
m_columnGPSTimeIndex = index;
}
void CT_Reader_Trajectory::setFirstConfiguration(bool first)
{
m_firstConfiguration = first;
}
void CT_Reader_Trajectory::setLinesToSkip(int skip)
{
m_nLinesToSkip = skip;
}
void CT_Reader_Trajectory::setHasHeader(bool hasHeader)
{
m_hasHeader = hasHeader;
}
void CT_Reader_Trajectory::setValueSeparator(QString sep)
{
m_separator = sep;
}
void CT_Reader_Trajectory::setLocaleName(QString locale)
{
m_localeName = locale;
}
int CT_Reader_Trajectory::xColumnIndex() const
{
return m_columnXIndex;
}
int CT_Reader_Trajectory::yColumnIndex() const
{
return m_columnYIndex;
}
int CT_Reader_Trajectory::zColumnIndex() const
{
return m_columnZIndex;
}
int CT_Reader_Trajectory::gpsTimeColumnIndex() const
{
return m_columnGPSTimeIndex;
}
bool CT_Reader_Trajectory::canLoadPoints() const
{
return (m_columnXIndex >= 0) && (m_columnYIndex >= 0) && (m_columnZIndex >= 0) && (m_columnGPSTimeIndex >= 0);
}
void CT_Reader_Trajectory::internalDeclareOutputModels(CT_ReaderOutModelStructureManager& manager)
{
manager.addItem(m_hOutScanPath, tr("ScanPath"));
}
bool CT_Reader_Trajectory::internalReadFile(CT_StandardItemGroup* group)
{
if(!canLoadPoints())
return false;
if(QFile::exists(filepath()))
{
QFile f(filepath());
if(f.open(QIODevice::ReadOnly))
{
QTextStream stream(&f);
QString currentLine;
QStringList wordsOfLine;
CT_Point point;
int nLine = 0;
int maxIndex = maxColumnIndex();
// Getting the file size to set progress
qint64 fileSize = f.size();
qint64 currentSizeRead = 0;
skipLines(stream, currentSizeRead);
QLocale locale(m_localeName);
bool error = false;
QFileInfo info(filepath());
CT_ScanPath* scanPath = new CT_ScanPath(info.baseName());
// While we did not reached the end of file
while(!stream.atEnd() && !isStopped() && !error) {
// Read the currentLine
++nLine;
currentLine = stream.readLine();
currentSizeRead += currentLine.size();
setProgress(int((float(currentSizeRead)*100.0f) / float(fileSize)));
if(currentLine.isEmpty())
continue;
// Read each word separately
wordsOfLine = currentLine.split(m_separator, QT_SKIP_EMPTY_PARTS);
// Checking for a valid line
if(maxIndex >= wordsOfLine.size()) {
PS_LOG->addErrorMessage(LogInterface::reader, tr("Error loading at line %1: missing columns.").arg(nLine));
error = true;
}
Eigen::Vector3d pt;
double gpsTime = 0;
CHK_ERR(readPoint(wordsOfLine, locale, pt, gpsTime), tr("Error loading point at line %1").arg(nLine));
scanPath->addPathPoint(gpsTime, pt);
}
f.close();
group->addSingularItem(m_hOutScanPath, scanPath);
return !error;
}
}
return false;
}
int CT_Reader_Trajectory::maxColumnIndex() const
{
int index = -1;
index = qMax(index, m_columnXIndex);
index = qMax(index, m_columnYIndex);
index = qMax(index, m_columnZIndex);
index = qMax(index, m_columnGPSTimeIndex);
return index;
}
void CT_Reader_Trajectory::skipLines(QTextStream &stream, qint64 ¤tSizeRead) const
{
for(int i = 0 ; i < m_nLinesToSkip; ++i)
currentSizeRead += stream.readLine().size();
if (m_hasHeader)
currentSizeRead += stream.readLine().size();
}
bool CT_Reader_Trajectory::readPoint(const QStringList &wordsOfLine, const QLocale &locale, Eigen::Vector3d &point, double &gpsTime) const
{
bool ok;
point(0) = locale.toDouble(wordsOfLine.at(m_columnXIndex), &ok);
if(!ok)
return false;
point(1) = locale.toDouble(wordsOfLine.at(m_columnYIndex), &ok);
if(!ok)
return false;
point(2) = locale.toDouble(wordsOfLine.at(m_columnZIndex), &ok);
if(!ok)
return false;
gpsTime = locale.toDouble(wordsOfLine.at(m_columnGPSTimeIndex), &ok);
if(!ok)
return false;
return true;
}