diff --git a/src/Resources/xml/video-layout.xml b/src/Resources/xml/video-layout.xml index 257882b6b..0fa5730e2 100644 --- a/src/Resources/xml/video-layout.xml +++ b/src/Resources/xml/video-layout.xml @@ -189,6 +189,7 @@ 16 + http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png diff --git a/src/Train/MeterWidget.cpp b/src/Train/MeterWidget.cpp index 0ad41d463..33448c877 100644 --- a/src/Train/MeterWidget.cpp +++ b/src/Train/MeterWidget.cpp @@ -22,8 +22,9 @@ #include "ErgFile.h" #include "Context.h" #include "Units.h" - -#include +#include "LocationInterpolation.h" +#include +#include MeterWidget::MeterWidget(QString Name, QWidget *parent, QString Source) : QWidget(parent), m_Name(Name), m_container(parent), m_Source(Source) { @@ -52,7 +53,6 @@ MeterWidget::MeterWidget(QString Name, QWidget *parent, QString Source) : QWidge m_RangeMax = 100; m_Angle = 180.0; m_SubRange = 10; - m_Zoom = 16; boundingRectVisibility = false; backgroundVisibility = false; forceSquareRatio = true; @@ -115,6 +115,17 @@ QSize MeterWidget::minimumSize() const return QSize(m_Width, m_Height); } +void MeterWidget::startPlayback(Context* context) +{ + +} + +void MeterWidget::stopPlayback() +{ + +} + + void MeterWidget::paintEvent(QPaintEvent* paintevent) { Q_UNUSED(paintevent); @@ -421,12 +432,12 @@ void ElevationMeterWidget::lazySetup(void) void ElevationMeterWidget::paintEvent(QPaintEvent* paintevent) { + MeterWidget::paintEvent(paintevent); + // TODO : show Power when not in slope simulation mode if (!context || !context->currentErgFile() || context->currentErgFile()->Points.size()<=1) return; - MeterWidget::paintEvent(paintevent); - m_MainBrush = QBrush(m_MainColor); m_BackgroundBrush = QBrush(m_BackgroundColor); m_OutlinePen = QPen(m_OutlineColor); @@ -498,86 +509,231 @@ void ElevationMeterWidget::paintEvent(QPaintEvent* paintevent) painter.drawText(distanceDrawX, distanceDrawY, distanceString); } - -LiveMapWidget::LiveMapWidget(QString Name, QWidget *parent, QString Source) : MeterWidget(Name, parent, Source) +LiveMapWidget::LiveMapWidget(QString Name, QWidget* parent, QString Source, Context* context) : MeterWidget(Name, parent, Source), context(context) { + m_Zoom = 16; + m_osmURL = "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"; forceSquareRatio = false; liveMapView = new QWebEngineView(this); - liveMapInitialized = false; + webPage = new QWebEnginePage(liveMapView); + liveMapView->setPage(webPage); + routeInitialized = false; + mapInitialized = false; + // Connect JS to C++ + webobj = new WebClass(); + channel = new QWebChannel(this); + channel->registerObject("webobj", webobj); + liveMapView->page()->setWebChannel(channel); + loadingLiveMap(); } -void LiveMapWidget::resizeEvent(QResizeEvent *) +void LiveMapWidget::startPlayback(Context* context) +{ + MeterWidget::startPlayback(context); + if (context->currentErgFile()) + { + buildRouteArrayLatLngs(context); + if (!mapInitialized) { + initLiveMap(context); + mapInitialized = true; + } + } + else + { + qDebug() << "Error: LiveMap cannot find Ergfile"; + } +} + +LiveMapWidget::~LiveMapWidget() +{ + delete webobj; + delete channel; + delete webPage; + delete liveMapView; +} + +void LiveMapWidget::stopPlayback() +{ + MeterWidget::stopPlayback(); + loadingLiveMap(); + mapInitialized = false; +} + +void LiveMapWidget::resizeEvent(QResizeEvent*) { liveMapView->resize(m_Width, m_Height); } +void LiveMapWidget::loadingLiveMap() +{ + //Set initial page to display loading map. + currentPage = QString("\n" + "

Loading map...

\n" + "\n"); + liveMapView->page()->setHtml(currentPage); +} + +// Initialize map, build route and show it +void LiveMapWidget::initLiveMap(Context* context) +{ + QString startingLat = QVariant(context->currentErgFile()->Points[0].lat).toString(); + QString startingLon = QVariant(context->currentErgFile()->Points[0].lon).toString(); + + if (startingLat == "0" && startingLon == "0") + { + currentPage = QString("\n" + "

Ride contains invalid data

\n" + "\n"); + liveMapView->page()->setHtml(currentPage); + } + else + { + QString js = ("
\n"); + routeLatLngs = "["; + QString code = ""; + + for (int pt = 0; pt < context->currentErgFile()->Points.size() - 1; pt++) { + + geolocation geoloc(context->currentErgFile()->Points[pt].lat, context->currentErgFile()->Points[pt].lon, context->currentErgFile()->Points[pt].y); + if (geoloc.IsReasonableGeoLocation()) { + if (pt == 0) { routeLatLngs += "["; } + else { routeLatLngs += ",["; } + routeLatLngs += QVariant(context->currentErgFile()->Points[pt].lat).toString(); + routeLatLngs += ","; + routeLatLngs += QVariant(context->currentErgFile()->Points[pt].lon).toString(); + routeLatLngs += "]"; + } + + } + routeLatLngs += "]"; + // We can either setHTML page or runJavaScript but not both. + // So we create divs with the 2 methods we need to run when the document loads + code = QString("showRoute (" + routeLatLngs + ");"); + js += ("
\n"); + createHtml(m_osmURL, js); + liveMapView->page()->setHtml(currentPage); + routeInitialized = true; + } +} + +// Show route or move the marker at the next location void LiveMapWidget::plotNewLatLng(double dLat, double dLon) { - if ( ! liveMapInitialized ) initLiveMap(dLat, dLon); - + QString code = ""; QString sLat = QString::number(dLat); QString sLon = QString::number(dLon); - QString code = QString("moveMarker(" + sLat + " , " + sLon + ")"); + QString sMapZoom = QString::number(m_Zoom); - liveMapView->page()->runJavaScript(code); + if (!routeInitialized) + { + code = QString("showRoute(" + routeLatLngs + ");"); + liveMapView->page()->runJavaScript(code); + routeInitialized = true; + } + else { + code += QString("moveMarker(" + sLat + " , " + sLon + ");"); + liveMapView->page()->runJavaScript(code); + } } -void LiveMapWidget::initLiveMap(double dLat, double dLon) +// Build LatLon array for selected workout +void LiveMapWidget::buildRouteArrayLatLngs(Context* context) { - createHtml(dLat, dLon, m_Zoom); - liveMapView->page()->setHtml(currentPage); - liveMapView->show(); - liveMapInitialized = true; + routeLatLngs = "["; + for (int pt = 0; pt < context->currentErgFile()->Points.size() - 1; pt++) { + if (pt == 0) { routeLatLngs += "["; } + else { routeLatLngs += ",["; } + routeLatLngs += QVariant(context->currentErgFile()->Points[pt].lat).toString(); + routeLatLngs += ","; + routeLatLngs += QVariant(context->currentErgFile()->Points[pt].lon).toString(); + routeLatLngs += "]"; + } + routeLatLngs += "]"; } -void LiveMapWidget::createHtml(double dLat, double dLon, int iMapZoom) + +// Build HTML code with all the javascript functions to be called later +// to update the postion on the map +void LiveMapWidget::createHtml(QString sBaseUrl, QString autoRunJS) { - QString sLat = QString::number(dLat); - QString sLon = QString::number(dLon); - QString sWidth = QString::number(m_Width); - QString sHeight = QString::number(m_Height); - QString sMapZoom = QString::number(iMapZoom); currentPage = ""; currentPage = QString("\n" - " \n" - "\n" - "GoldenCheetah LiveMap - TrainView\n"); - //Leaflet CSS and JS - currentPage += QString("\n" - "\n" - "\n"); - - // local functions - currentPage += QString("
\n" - "\n" + "\n" + "
\n" + "\n" - "\n"); + " doubleClickZoom : false }\n" + " mymap = L.map('mapid', mapOptions);\n" + " myscale = L.control.scale().addTo(mymap);\n" + " mylayer = new L.tileLayer('" + sBaseUrl + "');\n" + " mymap.addLayer(mylayer);\n" + "}\n" + "function showMyMarker(myLat, myLon) {\n" + " mymarker = new L.marker([myLat, myLon], {\n" + " draggable: false,\n" + " title : \"GoldenCheetah - Workout LiveMap\",\n" + " alt : \"GoldenCheetah - Workout LiveMap\",\n" + " riseOnHover : true\n" + " }).addTo(mymap);\n" + "}\n" + "function centerMap(myLat, myLon, myZoom) {\n" + " latlng = L.latLng(myLat, myLon);\n" + " mymap.setView(latlng, myZoom)\n" + "}\n" + "function showRoute(myRouteLatlngs) {\n" + " routepolyline = L.polyline(myRouteLatlngs, { color: 'red' }).addTo(mymap);\n" + //" mymap.fitBounds(routepolyline.getBounds());\n" + "}\n" + "\n" + + autoRunJS + + "\n" + ); +} + +// This method is called from the HTML page javascript using: webobj.jscallme(#); +void WebClass::jscallme(const QString &datafromjs) +{ + //qDebug() << "INFO: ===> I was called by js function: " << datafromjs; + switch (datafromjs.toInt()) { + case 0: + jsFuncinitMapLoaded = false; + break; + case 1: + jsFuncinitMapLoaded = true; + break; + case 2: + jsFuncmoveMarkerLoaded = true; + break; + case 3: + jsFuncshowMyMarkerLoaded = true; + break; + case 4: + jsFunccenterMapLoaded = true; + break; + case 5: + jsFuncshowRouteLoaded = true; + break; + } + } diff --git a/src/Train/MeterWidget.h b/src/Train/MeterWidget.h index 261210db8..7add253e5 100644 --- a/src/Train/MeterWidget.h +++ b/src/Train/MeterWidget.h @@ -21,9 +21,9 @@ #include #include "Context.h" +#include #include #include -#include class MeterWidget : public QWidget { @@ -45,6 +45,8 @@ class MeterWidget : public QWidget virtual void AdjustSizePos(); virtual void ComputeSize(); virtual void paintEvent(QPaintEvent* paintevent); + virtual void startPlayback(Context* context); + virtual void stopPlayback(); virtual QSize sizeHint() const; virtual QSize minimumSize() const; void setColor(QColor mainColor); @@ -67,7 +69,6 @@ class MeterWidget : public QWidget float m_RangeMin, m_RangeMax; float m_Angle; int m_SubRange; - int m_Zoom; bool forceSquareRatio; QColor m_MainColor; @@ -137,20 +138,53 @@ class ElevationMeterWidget : public MeterWidget float gradientValue; }; +// Connect JS with C++ +class WebClass : public QObject +{ + Q_OBJECT + //Q_PROPERTY(QString jsFuncName MEMBER m_jsFuncName) +public slots: + Q_INVOKABLE void jscallme(const QString &datafromjs); + //Q_INVOKABLE void jscallme(); + +public: + QString jsFuncReturn; + bool jsFuncinitMapLoaded, jsFuncmoveMarkerLoaded, jsFuncshowMyMarkerLoaded, jsFunccenterMapLoaded, jsFuncshowRouteLoaded = false; +}; + +// Display live map as overlay on video class LiveMapWidget : public MeterWidget { - public: - explicit LiveMapWidget(QString name, QWidget *parent = 0, QString Source = QString("None")); - void plotNewLatLng (double newLat, double newLong); + Q_OBJECT + Context* context; - protected: - void createHtml(double sLon, double sLat, int mapZoom); - void initLiveMap(double sLon, double sLat); - void resizeEvent(QResizeEvent *); +public: + explicit LiveMapWidget(QString name, QWidget* parent = 0, QString Source = QString("None"), Context* context = NULL); + ~LiveMapWidget(); + virtual void startPlayback(Context* context); + virtual void stopPlayback(); + void setContext(Context* context) { this->context = context; } + void plotNewLatLng(double newLat, double newLong); + void initLiveMap(Context* context); + void loadingLiveMap(); + int m_Zoom; + QString m_osmURL; - bool liveMapInitialized; +private slots: + +protected: + void createHtml(QString sBaseUrl, QString autoRunJS); + void buildRouteArrayLatLngs(Context* context); + void resizeEvent(QResizeEvent*); QWebEngineView *liveMapView; + QWebEnginePage *webPage; + WebClass *webobj; + QWebChannel *channel; + + QString routeLatLngs; QString currentPage; + bool routeInitialized, mapInitialized; + }; #endif // _MeterWidget_h diff --git a/src/Train/VideoLayoutParser.cpp b/src/Train/VideoLayoutParser.cpp index 4c12b0380..49f66a89b 100644 --- a/src/Train/VideoLayoutParser.cpp +++ b/src/Train/VideoLayoutParser.cpp @@ -288,8 +288,6 @@ bool VideoLayoutParser::endElement( const QString&, const QString&, const QStrin meterWidget->m_Angle = buffer.toFloat(); else if (qName == "SubRange") meterWidget->m_SubRange = buffer.toInt(); - else if (qName == "Zoom") - meterWidget->m_Zoom = buffer.toInt(); else if (qName == "Text") meterWidget->Text = QString(buffer); else if (qName == "AltText") @@ -302,6 +300,15 @@ bool VideoLayoutParser::endElement( const QString&, const QString&, const QStrin meterWidget = NULL; } + LiveMapWidget* liveMapWidget = dynamic_cast(meterWidget); + if (liveMapWidget != NULL) + { + if (qName == "Zoom") + liveMapWidget->m_Zoom = buffer.toInt(); + else if (qName == "osmURL") + liveMapWidget->m_osmURL = QString(buffer); + } + return true; } else diff --git a/src/Train/VideoWindow.cpp b/src/Train/VideoWindow.cpp index ca41b9911..e8f7f30c1 100644 --- a/src/Train/VideoWindow.cpp +++ b/src/Train/VideoWindow.cpp @@ -240,6 +240,7 @@ void VideoWindow::showMeters() p_meterWidget->update(); p_meterWidget->raise(); p_meterWidget->show(); + p_meterWidget->startPlayback(context); } prevPosition = mapToGlobal(pos()); } @@ -295,8 +296,10 @@ void VideoWindow::stopPlayback() #ifdef GC_VIDEO_QT5 mp->stop(); #endif - foreach(MeterWidget* p_meterWidget , m_metersWidget) + foreach(MeterWidget * p_meterWidget, m_metersWidget) { + p_meterWidget->stopPlayback(); p_meterWidget->hide(); + } } void VideoWindow::pausePlayback()