diff options
author | Rainer Keller <rainer.keller@nokia.com> | 2011-02-08 11:19:25 +0000 |
---|---|---|
committer | Rainer Keller <rainer.keller@nokia.com> | 2011-02-17 08:20:23 +0000 |
commit | 30b310edb7f2a925aa82ff1dbd16f39d6e309e69 (patch) | |
tree | f0122807f3d966cd9aa120524fbfea58cb8fe586 | |
parent | 31c7f1967646a347b3dfafa357329c5aeb052b7d (diff) |
Add GPS replay support
In the scripting section one can now open NMEA and GPX files as scripts.
-rw-r--r-- | library/components/locationui.cpp | 189 | ||||
-rw-r--r-- | library/components/locationui.h | 60 | ||||
-rw-r--r-- | library/components/scriptui.cpp | 40 | ||||
-rw-r--r-- | library/components/scriptui.h | 1 | ||||
-rw-r--r-- | library/remotecontrolwidget.pro | 4 | ||||
-rw-r--r-- | library/scriptadapter.cpp | 70 | ||||
-rw-r--r-- | library/scriptadapter.h | 5 |
7 files changed, 363 insertions, 6 deletions
diff --git a/library/components/locationui.cpp b/library/components/locationui.cpp index 8d8972d..3f1d919 100644 --- a/library/components/locationui.cpp +++ b/library/components/locationui.cpp @@ -48,6 +48,10 @@ #include <QtGui/QFormLayout> #include <QtGui/QInputDialog> #include <QtGui/QCheckBox> +#include <QtXml/QDomDocument> +#include <QtCore/QFile> +#include <QtCore/QTimer> +#include <QtLocation/QNmeaPositionInfoSource> LocationUi::LocationUi(QWidget *parent) : ToolBoxPage(parent) @@ -703,3 +707,188 @@ bool LocationScriptInterface::setInUse(int prn, bool inUse) } return false; } + +QObject *LocationScriptInterface::loadGPX(const QString &fileName) +{ + QGpxPositionInfoSource *source = new QGpxPositionInfoSource(); + QFile *device = new QFile(source); + device->setFileName(fileName); + + if (!device->open(QFile::ReadOnly)) + delete device; + else + source->setDevice(device); + + return source; +} + +QObject *LocationScriptInterface::loadNMEA(const QString &fileName) +{ + QNmeaPositionInfoSourceWithFinish *source = new QNmeaPositionInfoSourceWithFinish(QtMobility::QNmeaPositionInfoSource::SimulationMode); + QFile *device = new QFile(source); + device->setFileName(fileName); + + if (!device->open(QFile::ReadOnly)) + delete device; + else + source->setDevice(device); + + return source; +} + +QGpxPositionInfoSource::QGpxPositionInfoSource(QObject *parent) + :QGeoPositionInfoSource(parent) + ,mDevice(0) + ,mTimer(new QTimer(this)) +{ + connect(mTimer, SIGNAL(timeout()), this, SLOT(doUpdate())); +} + +QGpxPositionInfoSource::~QGpxPositionInfoSource() +{ +} + +QtMobility::QGeoPositionInfoSource::PositioningMethods QGpxPositionInfoSource::supportedPositioningMethods() const +{ + return QtMobility::QGeoPositionInfoSource::SatellitePositioningMethods; +} + +QIODevice * QGpxPositionInfoSource::device() const +{ + return mDevice; +} + +void QGpxPositionInfoSource::setDevice(QIODevice *device) +{ + mPositions.clear(); + mDevice = device; + + QDomDocument doc("gpx"); + if (!doc.setContent(mDevice)) + return; + + QDomElement docElem = doc.documentElement(); + if (docElem.tagName() != "gpx") + return; + QDomNodeList track = docElem.elementsByTagName("trk"); + if (track.count() == 0) + return; + if (!track.at(0).isElement()) + return; + QString name = track.at(0).toElement().elementsByTagName("name").at(0).toElement().text(); + QDomNodeList trackpoints = track.at(0).toElement().elementsByTagName("trkpt"); + + for (int i = 0; i<trackpoints.count(); i++) { + QDomElement trackpoint = trackpoints.at(i).toElement(); + if (trackpoint.isNull()) + continue; + + QtMobility::QGeoPositionInfo item; + QtMobility::QGeoCoordinate coordinate; + QDateTime timestamp; + bool ok; + + if (trackpoint.hasAttribute("lat")) { + double lat = trackpoint.attribute("lat").toDouble(&ok); + if (ok) + coordinate.setLatitude(lat); + } + if (trackpoint.hasAttribute("lon")) { + double lon = trackpoint.attribute("lon").toDouble(&ok); + if (ok) + coordinate.setLongitude(lon); + } + + QDomElement elevationElement = trackpoint.firstChildElement("ele"); + if (!elevationElement.isNull()) { + double ele; + ele = elevationElement.text().toDouble(&ok); + if (ok) + coordinate.setAltitude(ele); + + item.setCoordinate(coordinate); + + QDomElement timestampElement = trackpoint.firstChildElement("time"); + if (timestampElement.isNull()) + continue; + + timestamp = QDateTime::fromString(timestampElement.text(), Qt::ISODate); + if (!timestamp.isValid()) + continue; + item.setTimestamp(timestamp); + + } + if (item.isValid()) + mPositions += item; + } +} + +void QGpxPositionInfoSource::startUpdates() +{ + if (mPositions.isEmpty()) + return; + + doUpdate(); +} + +void QGpxPositionInfoSource::stopUpdates() +{ + mTimer->stop(); +} + +void QGpxPositionInfoSource::requestUpdate(int /*timeout*/) +{ + startUpdates(); // Should consider timeout +} + +QtMobility::QGeoPositionInfo QGpxPositionInfoSource::lastKnownPosition(bool /*fromSatellitePositioningMethodsOnly*/) const +{ + return mLastKnownPosition; +} + +int QGpxPositionInfoSource::minimumUpdateInterval() const +{ + return 100; +} + +void QGpxPositionInfoSource::setUpdateInterval(int msec) +{ + QGeoPositionInfoSource::setUpdateInterval(msec); +} +void QGpxPositionInfoSource::doUpdate() +{ + if (mPositions.isEmpty()) { + mTimer->stop(); + return; + } + + mLastKnownPosition = mPositions.first(); + mPositions.pop_front(); + + if (!mPositions.isEmpty()) { + mTimer->setInterval(mLastKnownPosition.timestamp().secsTo(mPositions[0].timestamp())*1000); + mTimer->start(); + } + + emit positionUpdated(mLastKnownPosition); + + if (mPositions.isEmpty()) { + emit finished(); + } +} + +QNmeaPositionInfoSourceWithFinish::QNmeaPositionInfoSourceWithFinish(QtMobility::QNmeaPositionInfoSource::UpdateMode mode, QObject *parent) + :QNmeaPositionInfoSource(mode, parent) +{ + connect(this, SIGNAL(positionUpdated(const QGeoPositionInfo &)), this, SLOT(positionUpdatedWrapper())); +} + +QNmeaPositionInfoSourceWithFinish::~QNmeaPositionInfoSourceWithFinish() +{ +} + +void QNmeaPositionInfoSourceWithFinish::positionUpdatedWrapper() +{ + if (device()->atEnd()) + emit finished(); +} diff --git a/library/components/locationui.h b/library/components/locationui.h index d4aa0ed..2d214b5 100644 --- a/library/components/locationui.h +++ b/library/components/locationui.h @@ -39,6 +39,8 @@ #include <QtCore/QDateTime> #include <QtCore/QHash> +#include <QtLocation/QGeoPositionInfoSource> +#include <QtLocation/QNmeaPositionInfoSource> class LocationScriptInterface; class QLineEdit; @@ -46,6 +48,11 @@ class QDateTimeEdit; class QRadioButton; class QComboBox; class QCheckBox; +class QGeoPositionInfo; + +namespace QtMobility { + class QNmeaPositionInfoSource; +} class REMOTECONTROLWIDGETSHARED_EXPORT LocationUi : public ToolBoxPage { @@ -89,6 +96,9 @@ public: LocationData locationData() const; SatelliteData satelliteData() const; + QString scriptFromGPX(const QString &); + QString scriptFromNMEA(const QString &); + public slots: void setLocation(const LocationUi::LocationData &location); void setDisplayedLocation(const LocationUi::LocationData &location); @@ -168,6 +178,8 @@ public: Q_INVOKABLE qreal azimuth(int satellite) const; Q_INVOKABLE int signalStrength(int satellite) const; Q_INVOKABLE bool inUse(int satellite) const; + Q_INVOKABLE QObject *loadGPX(const QString &fileName); + Q_INVOKABLE QObject *loadNMEA(const QString &fileName); double latitude() const; void setLatitude(double); @@ -216,4 +228,52 @@ private: LocationUi *ui; }; +class QNmeaPositionInfoSourceWithFinish : public QtMobility::QNmeaPositionInfoSource +{ + Q_OBJECT + +public: + QNmeaPositionInfoSourceWithFinish(QtMobility::QNmeaPositionInfoSource::UpdateMode, QObject *parent = 0); + virtual ~QNmeaPositionInfoSourceWithFinish(); + +private slots: + void positionUpdatedWrapper(); + +signals: + void finished(); +}; + +class QGpxPositionInfoSource : public QtMobility::QGeoPositionInfoSource +{ + Q_OBJECT + +public: + explicit QGpxPositionInfoSource(QObject *parent = 0); + virtual ~QGpxPositionInfoSource(); + + QtMobility::QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + int minimumUpdateInterval() const; + QtMobility::QGeoPositionInfoSource::PositioningMethods supportedPositioningMethods() const; + QIODevice *device() const; + void setDevice(QIODevice *device); + void setUpdateInterval(int msec); + +signals: + void finished(); + +public slots: + void requestUpdate(int timeout = 0); + void startUpdates(); + void stopUpdates(); + +private slots: + void doUpdate(); + +private: + QIODevice *mDevice; + QList<QtMobility::QGeoPositionInfo> mPositions; + QTimer *mTimer; + QtMobility::QGeoPositionInfo mLastKnownPosition; +}; + #endif // LOCATIONUI_H diff --git a/library/components/scriptui.cpp b/library/components/scriptui.cpp index 43ab657..ada8161 100644 --- a/library/components/scriptui.cpp +++ b/library/components/scriptui.cpp @@ -91,7 +91,7 @@ ScriptUi::ScriptUi(ScriptAdapter *adapter, QWidget *parent) QDir scriptsDir(QCoreApplication::applicationDirPath()); scriptsDir.cd(QLatin1String("scripts")); QStringList nameFilters; - nameFilters << "*.js" << "*.qs"; + nameFilters << "*.js" << "*.qs" << "*.nmea" << "*.gpx"; mFileModel->setNameFilters(nameFilters); mFileModel->setNameFilterDisables(false); mFileModel->setRootPath(scriptsDir.path()); @@ -111,7 +111,7 @@ ScriptUi::ScriptUi(ScriptAdapter *adapter, QWidget *parent) connect(runButton, SIGNAL(clicked()), this, SLOT(runSelectedScript())); scriptRunnerButtonLayout->addWidget(runButton); - QPushButton *openDirectoryButton = new QPushButton(tr("Open Folder")); + QPushButton *openDirectoryButton = new QPushButton(tr("Show Folder Content")); connect(openDirectoryButton, SIGNAL(clicked()), this, SLOT(openScriptDirectory())); scriptRunnerButtonLayout->addWidget(openDirectoryButton); @@ -154,7 +154,12 @@ void ScriptUi::runSelectedScript() if (index.isValid() && !mFileModel->isDir(index)) { QString scriptFilePath = mFileModel->filePath(index); - runScript(scriptFilePath); + if (scriptFilePath.endsWith(".gpx")) + generateLocationReplayScript("GPX", scriptFilePath); + else if (scriptFilePath.endsWith(".nmea")) + generateLocationReplayScript("NMEA", scriptFilePath); + else + runScript(scriptFilePath); } } @@ -218,3 +223,32 @@ void ScriptUi::openScriptDirectory() { QDesktopServices::openUrl(QUrl::fromLocalFile(mFileModel->rootPath())); } + +void ScriptUi::generateLocationReplayScript(const QString &functionSuffix, const QString &fileName) +{ + QString script = "var finished = 0;" + "function myFinished() {" + " finished = 1;" + "}" + "function myFunc(update) {" + " location.latitude = update.latitude;" + " location.longitude = update.longitude;" + " location.elevation = update.altitude;" + " location.direction = update.direction;" + " location.groundSpeed = update.groundSpeed;" + " location.verticalSpeed = update.verticalSpeed;" + " location.magneticVariation = update.magneticVariation;" + " location.horizontalAccuracy = update.horizontalAccuracy;" + " location.verticalAccuracy = update.verticalAccuracy;" + " location.timestamp = update.timestamp;" + "}" + "var source = location.load%2(\"%1\");" + "source.positionUpdated.connect(myFunc);" + "source.finished.connect(myFinished);" + "source.startUpdates();" + "while(finished==0) yield(250)" + ; + + script = script.arg(fileName).arg(functionSuffix); + mAdapter->runCode(fileName, script); +} diff --git a/library/components/scriptui.h b/library/components/scriptui.h index 0ba0305..d9bbb69 100644 --- a/library/components/scriptui.h +++ b/library/components/scriptui.h @@ -65,6 +65,7 @@ private slots: void updatePauseButtonText(); void openScriptDirectory(); + void generateLocationReplayScript(const QString &functionSuffix, const QString &fileName); private: ScriptAdapter *mAdapter; diff --git a/library/remotecontrolwidget.pro b/library/remotecontrolwidget.pro index ed18905..e151251 100644 --- a/library/remotecontrolwidget.pro +++ b/library/remotecontrolwidget.pro @@ -9,12 +9,14 @@ TEMPLATE = lib include(adjusttarget.pri) CONFIG += hide_symbols +CONFIG += mobility DEFINES += REMOTECONTROLWIDGET_LIBRARY INCLUDEPATH += . -QT += core gui script +QT += core gui script xml +MOBILITY+=location SOURCES += \ remotecontrolwidget.cpp \ diff --git a/library/scriptadapter.cpp b/library/scriptadapter.cpp index b04cca6..bdeef23 100644 --- a/library/scriptadapter.cpp +++ b/library/scriptadapter.cpp @@ -43,11 +43,62 @@ #include <QtCore/QDir> #include <QtCore/QDebug> #include <QtGui/QMessageBox> +#include <QtLocation/QGeoPositionInfo> #ifdef Q_OS_WIN #include <windows.h> #endif + +QScriptValue toScriptValue(QScriptEngine *engine, const QtMobility::QGeoPositionInfo &s) +{ + QScriptValue obj = engine->newObject(); + obj.setProperty("latitude", s.coordinate().latitude()); + obj.setProperty("longitude", s.coordinate().longitude()); + obj.setProperty("altitude", s.coordinate().altitude()); + obj.setProperty("timestamp", engine->newDate(s.timestamp())); + if (s.hasAttribute(QGeoPositionInfo::Direction)) + obj.setProperty("direction", s.attribute(QGeoPositionInfo::Direction)); + if (s.hasAttribute(QGeoPositionInfo::GroundSpeed)) + obj.setProperty("groundSpeed", s.attribute(QGeoPositionInfo::GroundSpeed)); + if (s.hasAttribute(QGeoPositionInfo::VerticalSpeed)) + obj.setProperty("verticalSpeed", s.attribute(QGeoPositionInfo::VerticalSpeed)); + if (s.hasAttribute(QGeoPositionInfo::MagneticVariation)) + obj.setProperty("magneticVariation", s.attribute(QGeoPositionInfo::MagneticVariation)); + if (s.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + obj.setProperty("horizontalAccuracy", s.attribute(QGeoPositionInfo::HorizontalAccuracy)); + if (s.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) + obj.setProperty("verticalAccuracy", s.attribute(QGeoPositionInfo::VerticalAccuracy)); + return obj; +} + +void fromScriptValue(const QScriptValue &obj, QtMobility::QGeoPositionInfo &s) +{ + QtMobility::QGeoCoordinate coord; + if (obj.property("latitude").isValid()) + coord.setLatitude(obj.property("latitude").toNumber()); + if (obj.property("longitude").isValid()) + coord.setLongitude(obj.property("longitude").toNumber()); + if (obj.property("altitude").isValid()) + coord.setLongitude(obj.property("altitude").toNumber()); + s.setCoordinate(coord); + + if (obj.property("direction").isValid()) + s.setAttribute(QGeoPositionInfo::Direction, obj.property("direction").toNumber()); + if (obj.property("groundSpeed").isValid()) + s.setAttribute(QGeoPositionInfo::GroundSpeed, obj.property("groundSpeed").toNumber()); + if (obj.property("verticalSpeed").isValid()) + s.setAttribute(QGeoPositionInfo::VerticalSpeed, obj.property("verticalSpeed").toNumber()); + if (obj.property("magneticVariation").isValid()) + s.setAttribute(QGeoPositionInfo::MagneticVariation, obj.property("magneticVariation").toNumber()); + if (obj.property("horizontalAccuracy").isValid()) + s.setAttribute(QGeoPositionInfo::HorizontalAccuracy, obj.property("horizontalAccuracy").toNumber()); + if (obj.property("verticalAccuracy").isValid()) + s.setAttribute(QGeoPositionInfo::VerticalAccuracy, obj.property("verticalAccuracy").toNumber()); + if (obj.property("timestamp").isValid()) + s.setTimestamp(obj.property("timestamp").toDateTime()); +} + ScriptAdapter::ScriptAdapter(QObject *parent) : QObject(parent) { @@ -109,7 +160,12 @@ Script *ScriptAdapter::run(const QString &filePath) } QFileInfo fileInfo(file); - Script *script = new Script(fileInfo.baseName(), file.readAll(), this); + return runCode(fileInfo.baseName(), file.readAll()); +} + +Script * ScriptAdapter::runCode(const QString &name, const QString &scriptCode) +{ + Script *script = new Script(name, scriptCode, this); script->start(); mActiveScripts += script; @@ -174,6 +230,8 @@ void Script::setupScriptEngine() mEngine->globalObject().setProperty(functionName, functionValue); } + qScriptRegisterMetaType(mEngine, toScriptValue, fromScriptValue); + // setup script interfaces QHashIterator<QString, QObject *> iter(mAdapter->mScriptInterfaces); while (iter.hasNext()) { @@ -208,7 +266,14 @@ void Script::run() QScriptValue value = mEngine->evaluate(mScriptCode); if (value.isError()) { - messageBox(value.toString(), tr("Script errors")); + QString errorText; + errorText += value.toString(); + if (mEngine->hasUncaughtException()) { + int line = mEngine->uncaughtExceptionLineNumber(); + errorText += " in line " + QString::number(line); + } + + messageBox(errorText, tr("Script errors")); } mMutex.unlock(); @@ -235,6 +300,7 @@ void Script::suspend() mSuspended = true; mWaitCondition.wait(&mMutex); + QCoreApplication::processEvents(); mStopTimer->start(); } diff --git a/library/scriptadapter.h b/library/scriptadapter.h index e36b02f..d86218e 100644 --- a/library/scriptadapter.h +++ b/library/scriptadapter.h @@ -43,6 +43,10 @@ #include <QtCore/QMutex> #include <QtCore/QWaitCondition> #include <QtScript/QScriptEngine> +#include <QtLocation/QGeoPositionInfo> + +using namespace QtMobility; +Q_DECLARE_METATYPE(QGeoPositionInfo); class Script; class QMessageBox; @@ -61,6 +65,7 @@ public: public slots: Script *run(const QString &filePath); + Script *runCode(const QString &name, const QString &scriptCode); signals: void scriptStart(Script *script) const; |