/* * Copyright (c) 2011, 2014 Damien Grauser (Damien.Grauser@pev-geneve.ch) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "RouteWindow.h" #include "Athlete.h" #include "GoogleMapControl.h" #include "RideItem.h" #include "Route.h" #include "RouteItem.h" #include #include using namespace std; #define GOOGLE_KEY "ABQIAAAAS9Z2oFR8vUfLGYSzz40VwRQ69UCJw2HkJgivzGoninIyL8-QPBTtnR-6pM84ljHLEk3PDql0e2nJmg" RouteWindow::RouteWindow(Context *context) : GcWindow(context), context(context) { this->setTitle("Routes"); view = new QWebView(); QVBoxLayout *layoutV = new QVBoxLayout(); QHBoxLayout *layoutH = new QHBoxLayout(); layout = new QVBoxLayout(); //QLabel *label = new QLabel("Route segments"); //layout->addWidget(label); layout->addWidget(view); // RIDES treeWidget = new QTreeWidget; treeWidget->setMinimumWidth(270); treeWidget->setColumnCount(3); treeWidget->setSelectionMode(QAbstractItemView::SingleSelection); treeWidget->header()->resizeSection(0,130); treeWidget->header()->resizeSection(1,70); treeWidget->header()->resizeSection(2,70); treeWidget->header()->hide(); treeWidget->setAlternatingRowColors (true); treeWidget->setIndentation(10); treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); /*QWebView* rideSummary = new QWebView(this); rideSummary->setContentsMargins(0,0,0,0); rideSummary->page()->view()->setContentsMargins(0,0,0,0); rideSummary->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); rideSummary->setAcceptDrops(false); rideSummary->page()->mainFrame()->setHtml("TEST"); rideSummary->rect().setHeight(50);*/ rideTable = new QTableWidget; rideTable->setColumnCount(5); rideTable->horizontalHeader()->hide(); rideTable->horizontalHeader()->resizeSection(0,130); rideTable->verticalHeader()->setDefaultSectionSize(15); rideTable->verticalHeader()->hide(); rideTable->setSelectionMode(QAbstractItemView::SingleSelection); rideTable->setSelectionBehavior(QAbstractItemView::SelectRows); rideTable->setAlternatingRowColors (true); rideTable->setMaximumHeight(300); rideTable->setShowGrid(false); //rideTable->setHorizontalHeaderItem(0, item); layoutH->addLayout(layout); layoutH->addWidget(treeWidget); layoutV->addLayout(layoutH); //layoutV->addWidget(rideTable); setLayout(layoutV); // now we're up and runnning lets connect the signals connect(view, SIGNAL(loadStarted()), this, SLOT(loadStarted())); //connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); connect(context->athlete->routes, SIGNAL(routesChanged()), this, SLOT(resetRoutes())); connect(treeWidget,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showTreeContextMenuPopup(const QPoint &))); connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(rideTreeWidgetSelectionChanged())); connect(treeWidget, SIGNAL(itemChanged()), this, SLOT(routeChanged())); connect(treeWidget, SIGNAL(itemPressed()), this, SLOT(routeChanged())); connect(treeWidget, SIGNAL(itemActivated()), this, SLOT(routeChanged())); webBridge = new WebBridgeForRoute(context, this); connect(view->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(updateFrame())); loadingPage = false; resetRoutes(); } void RouteWindow::loadStarted() { loadingPage = true; } /// called after the load is finished /*void RouteWindow::loadFinished(bool) { loadingPage = false; loadRide(); }*/ void RouteWindow::loadRide() { createHtml(); view->page()->mainFrame()->setHtml(currentPage); // Fill ride table /* rideTable->setRowCount(route->getRides().count()); int i = 0; foreach (const RouteRide &ride, route->getRides()) { QDateTime dateTime = ride.startTime.addSecs(ride.start); //RouteItem* item = new RouteItem(route, RIDE_TYPE, mainWindow->home.path(), "test", dateTime, mainWindow); RouteItem* item = new RouteItem(route, &ride, mainWindow->home.path(), mainWindow); QTableWidgetItem* item0 = new QTableWidgetItem(0); item0->setText(dateTime.toString("MMM d, yyyy")); rideTable->setItem(i, 0, item0); QTableWidgetItem* item1 = new QTableWidgetItem(0); item1->setText(dateTime.toString("h:mm AP")); rideTable->setItem(i, 1, item1); QTableWidgetItem* item2 = new QTableWidgetItem(0); item2->setText(QString("%1m").arg(item->ride()->dataPoints().at(0)->alt)); rideTable->setItem(i, 2, item2); i++; } */ } void RouteWindow::updateFrame() { view->page()->mainFrame()->addToJavaScriptWindowObject("webBridge", webBridge); } void RouteWindow::createHtml() { qDebug() << "route->getId() " << route->id(); currentPage = ""; double minLat, minLon, maxLat, maxLon; minLat = minLon = 1000; maxLat = maxLon = -1000; // larger than 360 // get bounding co-ordinates for ride foreach(RoutePoint rfp, route->getPoints()) { if (rfp.lat || rfp.lon) { minLat = std::min(minLat,rfp.lat); maxLat = std::max(maxLat,rfp.lat); minLon = std::min(minLon,rfp.lon); maxLon = std::max(maxLon,rfp.lon); } } // No GPS data, so sorry no map if(!route) { currentPage = "No Route Data Present"; return; } // load the Google Map v3 API currentPage = QString(" \n" "\n" "\n" " \n" "\n" "Golden Cheetah Map\n" " \n" " \n"); // local functions currentPage += QString("\n").arg(minLat,0,'g',11).arg(minLon,0,'g',11).arg(maxLat,0,'g',11).arg(maxLon,0,'g',11); // the main page is rather trivial currentPage += QString("\n" "\n" "
\n" "\n" "\n"); } string RouteWindow::CreateMapToolTipJavaScript() { qDebug() << "CreateMapToolTipJavaScript()"; ostringstream oss; oss << " "<< endl; return oss.str(); } /// create the ride line void RouteWindow::drawShadedRoute() { qDebug() << "CreatePolyLine()"; int count=0; // how many samples ? QString code; foreach(RoutePoint rfp, route->getPoints()) { if (count == 0) { code = QString("{\n\nvar polyline = new google.maps.Polyline();\n" " polyline.setMap(map);\n" " path = polyline.getPath();\n"); } else { code += QString("path.push(new google.maps.LatLng(%1,%2));\n").arg(rfp.lat,0,'g',11).arg(rfp.lon,0,'g',11); } count++; } QColor color = Qt::red; // color the polyline code += QString("var polyOptions = {\n" " strokeColor: '%1',\n" " strokeWeight: 3,\n" " strokeOpacity: 0.5,\n" // for out and backs, we need both " zIndex: 0,\n" "}\n" "polyline.setOptions(polyOptions);\n" "}\n").arg(color.name()); view->page()->mainFrame()->evaluateJavaScript(code); } void RouteWindow::resetRoutes() { qDebug() << "resetRoutes"; treeWidget->clear(); routes = context->athlete->routes; if (routes->routes.count()>0) { for (int n=0;nroutes.count();n++) { route = &routes->routes[n]; RouteItem *allRides = new RouteItem(route, FOLDER_TYPE, treeWidget, context) ; //new QTreeWidgetItem(treeWidget, FOLDER_TYPE); allRides->setText(0, route->getName()); RouteItem *last = NULL; QStringListIterator i(RideFileFactory::instance().listRideFiles(context->athlete->home)); while (i.hasNext()) { QString name = i.next(), notesFileName; QDateTime dt; for (int j=0;jgetRides().count();j++) { if ((route->parseRideFileName(context, name, ¬esFileName, &dt)) && (dt == route->getRides()[j].startTime)) { // QDateTime dateTime = dt.addSecs(route->getRides()[j].start); //last = new RouteItem(route, RIDE_TYPE, mainWindow->home.path(), name, dateTime, mainWindow); last = new RouteItem(route, &route->getRides()[j], context->athlete->home.path(), context); QString time = QTime(0,0,0,0).addSecs(route->getRides()[j].stop- route->getRides()[j].start).toString("hh:mm:ss"); last->setText(0, dateTime.toString("MMM d, yyyy")); last->setText(1, dateTime.toString("h:mm AP")); last->setText(2, time); allRides->addChild(last); } } } treeWidget->expandItem(allRides); treeWidget->setFirstItemColumnSpanned (allRides, true); } route = &routes->routes[0]; newRideToLoad = true; loadRide(); } } void RouteWindow::rideTreeWidgetSelectionChanged() { qDebug() << "rideTreeWidgetSelectionChanged"; //if (routeToSave) // routes->writeRoutes(); assert(treeWidget->selectedItems().size() <= 1); if (treeWidget->selectedItems().isEmpty()) route = NULL; else { QTreeWidgetItem *which = treeWidget->selectedItems().first(); if (which->type() == ROUTE_TYPE) { RouteSegment *newroute = &routes->routes[treeWidget->indexOfTopLevelItem(which)]; if (newroute != route) { route = newroute; newRideToLoad = true; loadRide(); } } else { RouteSegment *newroute = &routes->routes[treeWidget->indexOfTopLevelItem(which->parent())]; if (newroute != route) { // New RouteSegment route = newroute; routeItem = (RouteItem *)which; newRideToLoad = true; loadRide(); } else if (which != routeItem) { // New Ride for this route routeItem = (RouteItem *)which; newRideToLoad = true; loadRide(); } } } } void RouteWindow::showTreeContextMenuPopup(const QPoint &pos) { routeItem = (RouteItem *)treeWidget->selectedItems().first(); if (routeItem != NULL && routeItem->type() == ROUTE_TYPE) { QMenu menu(treeWidget); QAction *actRenRoute = new QAction(tr("Rename route"), treeWidget); connect(actRenRoute, SIGNAL(triggered(void)), this, SLOT(renameRoute())); QAction *actDeleteRoute = new QAction(tr("Delete route"), treeWidget); connect(actDeleteRoute, SIGNAL(triggered(void)), this, SLOT(deleteRoute())); menu.addAction(actRenRoute); menu.addAction(actDeleteRoute); menu.exec(treeWidget->mapToGlobal( pos )); } } void RouteWindow::deleteRoute() { // renumber remaining /*int oindex = activeInterval->displaySequence; for (int i=0; ichildCount(); i++) { IntervalItem *it = (IntervalItem *)allIntervals->child(i); int ds = it->displaySequence; if (ds > oindex) it->setDisplaySequence(ds-1); } // now delete! int index = allIntervals->indexOfChild(activeInterval); delete allIntervals->takeChild(index); this->; // will emit intervalChanged() signal*/ } void RouteWindow::renameRoute() { // go edit the name routeItem->setFlags(routeItem->flags() | Qt::ItemIsEditable); treeWidget->editItem(routeItem, 0); routeToSave = true; } void RouteWindow::routeChanged(QTreeWidgetItem *item, int column) { qDebug() << "routeChanged"; routes->writeRoutes(); } // quick diag, used to debug code only void WebBridgeForRoute::call(int count) { qDebug()<<"webBridge call:"<routeItem == NULL || gm->routeItem->type() != ROUTE_TYPE) return highlighted; // not inited yet! RideFile *rideFile = gm->routeItem->ride(); if (context->athlete->allIntervalItems() == NULL || rideFile == NULL ) return 0; // not inited yet! for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); if (current != NULL) { if (current->isSelected() == true) { ++highlighted; } } } return highlighted; } // get a latlon array for the i'th selected interval QVariantList WebBridgeForRoute::getLatLons(int i) { QVariantList latlons; if (gm->routeItem == NULL || gm->routeItem->type() == ROUTE_TYPE) return latlons; // not inited yet! int highlighted=0; RideFile *rideFile = gm->routeItem->ride(); if (context->athlete->allIntervalItems() == NULL || rideFile == NULL) return latlons; // not inited yet! if (i) { // get for specific interval for (int j=0; jathlete->allIntervalItems()->childCount(); j++) { IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(j)); if (current != NULL) { if (current->isSelected() == true) { ++highlighted; // this one! if (highlighted==i) { // so this one is the interval we need.. lets // snaffle up the points in this section foreach (RideFilePoint *p1, rideFile->dataPoints()) { if (p1->secs+rideFile->recIntSecs() > current->start && p1->secs< current->stop) { if (p1->lat || p1->lon) { latlons << p1->lat; latlons << p1->lon; } } } return latlons; } } } } } else { // get latlons for entire route if (rideFile && rideFile != NULL) foreach (RideFilePoint *p1, rideFile->dataPoints()) { if (p1->lat || p1->lon) { latlons << p1->lat; latlons << p1->lon; } } } return latlons; } // once the basic map and route have been marked, overlay markers, shaded areas etc void WebBridgeForRoute::drawOverlays() { // overlay the markers //gm->createMarkers(); // overlay a shaded route gm->drawShadedRoute(); }