Skip to content
Snippets Groups Projects
MapWidget.cc 17.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • pixhawk's avatar
    pixhawk committed
    /*=====================================================================
    
    
    lm's avatar
    lm committed
    QGroundControl Open Source Ground Control Station
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
    (c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
    This file is part of the QGROUNDCONTROL project
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
        QGROUNDCONTROL is free software: you can redistribute it and/or modify
    
    pixhawk's avatar
    pixhawk committed
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
    
    lm's avatar
    lm committed
        QGROUNDCONTROL is distributed in the hope that it will be useful,
    
    pixhawk's avatar
    pixhawk committed
        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
    
    lm's avatar
    lm committed
        along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
    
    pixhawk's avatar
    pixhawk committed
    
    ======================================================================*/
    
    /**
     * @file
    
    lm's avatar
    lm committed
     *   @brief Implementation of MapWidget
    
    pixhawk's avatar
    pixhawk committed
     *
     *   @author Lorenz Meier <mavteam@student.ethz.ch>
    
    lm's avatar
    lm committed
     *   @author Mariano Lizarraga
    
    pixhawk's avatar
    pixhawk committed
     *
     */
    
    
    lm's avatar
    lm committed
    #include <QComboBox>
    #include <QGridLayout>
    
    
    pixhawk's avatar
    pixhawk committed
    #include "MapWidget.h"
    #include "ui_MapWidget.h"
    
    lm's avatar
    lm committed
    #include "UASInterface.h"
    #include "UASManager.h"
    
    pixhawk's avatar
    pixhawk committed
    
    
    pixhawk's avatar
    pixhawk committed
    #include "MG.h"
    
    
    pixhawk's avatar
    pixhawk committed
    MapWidget::MapWidget(QWidget *parent) :
            QWidget(parent),
            zoomLevel(0),
    
            uasIcons(),
            uasTrails(),
    
    pixhawk's avatar
    pixhawk committed
            m_ui(new Ui::MapWidget)
    {
        m_ui->setupUi(this);
    
    pixhawk's avatar
    pixhawk committed
        // Accept focus by clicking or keyboard
        this->setFocusPolicy(Qt::StrongFocus);
    
        // create MapControl
        mc = new MapControl(QSize(320, 240));
        mc->showScale(true);
    
    pixhawk's avatar
    pixhawk committed
        mc->enablePersistentCache();
    
        mc->setMouseTracking(true); // required to update the mouse position for diplay and capture
    
    pixhawk's avatar
    pixhawk committed
    
        // create MapAdapter to get maps from
    
    lm's avatar
    lm committed
        //TileMapAdapter* osmAdapter = new TileMapAdapter("tile.openstreetmap.org", "/%1/%2/%3.png", 256, 0, 17);
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
        MapAdapter* mapadapter_overlay = new YahooMapAdapter("us.maps3.yimg.com", "/aerial.maps.yimg.com/png?v=2.2&t=h&s=256&x=%2&y=%3&z=%1");
    
        // MAP BACKGROUND
        mapadapter = new GoogleSatMapAdapter();
        l = new MapLayer("Google Satellite", mapadapter);
        mc->addLayer(l);
    
        // STREET OVERLAY
        overlay = new MapLayer("Overlay", mapadapter_overlay);
        overlay->setVisible(false);
        mc->addLayer(overlay);
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
        // WAYPOINT LAYER
        // create a layer with the mapadapter and type GeometryLayer (for waypoints)
        geomLayer = new GeometryLayer("Waypoints", mapadapter);
    
    lm's avatar
    lm committed
    
    //
    //    Layer* gsatLayer = new Layer("Google Satellite", gsat, Layer::MapLayer);
    //    mc->addLayer(gsatLayer);
    
        // SET INITIAL POSITION AND ZOOM
        // Set default zoom level
        mc->setZoom(16);
        // Zurich, ETH
        mc->setView(QPointF(8.548056,47.376389));
    
        // Add controls to select map provider
        /////////////////////////////////////////////////
        QActionGroup* mapproviderGroup = new QActionGroup(this);
    
        osmAction = new QAction(QIcon(":/images/mapproviders/openstreetmap.png"), tr("OpenStreetMap"), mapproviderGroup);
        yahooActionMap = new QAction(QIcon(":/images/mapproviders/yahoo.png"), tr("Yahoo: Map"), mapproviderGroup);
        yahooActionSatellite = new QAction(QIcon(":/images/mapproviders/yahoo.png"), tr("Yahoo: Satellite"), mapproviderGroup);
        googleActionMap = new QAction(QIcon(":/images/mapproviders/google.png"), tr("Google: Map"), mapproviderGroup);
        googleSatAction = new QAction(QIcon(":/images/mapproviders/google.png"), tr("Google: Sat"), mapproviderGroup);
    
    lm's avatar
    lm committed
        osmAction->setCheckable(true);
        yahooActionMap->setCheckable(true);
        yahooActionSatellite->setCheckable(true);
        googleActionMap->setCheckable(true);
        googleSatAction->setCheckable(true);
        googleSatAction->setChecked(true);
        connect(mapproviderGroup, SIGNAL(triggered(QAction*)),
                this, SLOT(mapproviderSelected(QAction*)));
    
        // Overlay seems currently broken
    //    yahooActionOverlay = new QAction(tr("Yahoo: street overlay"), this);
    //    yahooActionOverlay->setCheckable(true);
    //    yahooActionOverlay->setChecked(overlay->isVisible());
    //    connect(yahooActionOverlay, SIGNAL(toggled(bool)),
    //            overlay, SLOT(setVisible(bool)));
    
    //    mapproviderGroup->addAction(googleSatAction);
    //    mapproviderGroup->addAction(osmAction);
    //    mapproviderGroup->addAction(yahooActionOverlay);
    //    mapproviderGroup->addAction(googleActionMap);
    //    mapproviderGroup->addAction(yahooActionMap);
    //    mapproviderGroup->addAction(yahooActionSatellite);
    
        // Create map provider selection menu
        mapMenu = new QMenu(this);
        mapMenu->addActions(mapproviderGroup->actions());
        mapMenu->addSeparator();
    //    mapMenu->addAction(yahooActionOverlay);
    
        mapButton = new QPushButton(this);
        mapButton->setText("Map Source");
        mapButton->setMenu(mapMenu);
    
    pixhawk's avatar
    pixhawk committed
    
        // display the MapControl in the application
    
    lm's avatar
    lm committed
        QGridLayout* layout = new QGridLayout(this);
    
    pixhawk's avatar
    pixhawk committed
        layout->setMargin(0);
    
    lm's avatar
    lm committed
        layout->setSpacing(2);
        layout->addWidget(mc, 0, 0, 1, 2);
        layout->addWidget(mapButton, 1, 0);
        layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding), 1, 1);
        layout->setRowStretch(0, 100);
        layout->setRowStretch(1, 1);
        layout->setColumnStretch(0, 1);
        layout->setColumnStretch(1, 50);
    
    pixhawk's avatar
    pixhawk committed
        setLayout(layout);
    
    
        // create buttons to control the map (zoom, GPS tracking and WP capture)
    
    pixhawk's avatar
    pixhawk committed
        QPushButton* zoomin = new QPushButton(QIcon(":/images/actions/list-add.svg"), "", this);
        QPushButton* zoomout = new QPushButton(QIcon(":/images/actions/list-remove.svg"), "", this);
    
        createPath = new QPushButton(QIcon(":/images/actions/go-bottom.svg"), "", this);
    
    pixhawk's avatar
    pixhawk committed
        followgps = new QPushButton(QIcon(":/images/actions/system-lock-screen.svg"), "", this);
    
    pixhawk's avatar
    pixhawk committed
        zoomin->setMaximumWidth(50);
        zoomout->setMaximumWidth(50);
    
    pixhawk's avatar
    pixhawk committed
        followgps->setMaximumWidth(50);
    
    
        // Set checkable buttons
        // TODO: Currently checked buttons are are very difficult to distinguish when checked.
        //       create a style and the slots to change the background so it is easier to distinguish
        followgps->setCheckable(true);
        createPath->setCheckable(true);
    
        // add buttons to control the map (zoom, GPS tracking and WP capture)
    
    lm's avatar
    lm committed
        QGridLayout* innerlayout = new QGridLayout(mc);
        innerlayout->setMargin(5);
        innerlayout->setSpacing(5);
        innerlayout->addWidget(zoomin, 0, 0);
        innerlayout->addWidget(zoomout, 1, 0);
        innerlayout->addWidget(followgps, 2, 0);
        innerlayout->addWidget(createPath, 3, 0);
        // Add spacers to compress buttons on the top left
        innerlayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding), 4, 0);
        innerlayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, 1, 0, 5);
        innerlayout->setRowStretch(0, 1);
        innerlayout->setRowStretch(1, 100);
    
    pixhawk's avatar
    pixhawk committed
        mc->setLayout(innerlayout);
    
    
        connect(zoomin, SIGNAL(clicked(bool)),
                mc, SLOT(zoomIn()));
    
        connect(zoomout, SIGNAL(clicked(bool)),
                mc, SLOT(zoomOut()));
    
    
        connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)),
                this, SLOT(addUAS(UASInterface*)));
    
        connect(mc, SIGNAL(mouseEventCoordinate(const QMouseEvent*, const QPointF)),
                this, SLOT(captureMapClick(const QMouseEvent*, const QPointF)));
    
        connect(createPath, SIGNAL(clicked(bool)),
    
                this, SLOT(createPathButtonClicked(bool)));
    
        connect(geomLayer, SIGNAL(geometryClicked(Geometry*,QPoint)),
                this, SLOT(captureGeometryClick(Geometry*, QPoint)));
    
    
        connect(geomLayer, SIGNAL(geometryDragged(Geometry*, QPointF)),
                this, SLOT(captureGeometryDrag(Geometry*, QPointF)));
    
        connect(geomLayer, SIGNAL(geometryEndDrag(Geometry*, QPointF)),
                this, SLOT(captureGeometryEndDrag(Geometry*, QPointF)));
    
    
        // Configure the WP Path's pen
        pointPen = new QPen(QColor(0, 255,0));
        pointPen->setWidth(3);
    
        path = new LineString (wps, "UAV Path", pointPen);
    
    lm's avatar
    lm committed
        mc->layer("Waypoints")->addGeometry(path);
    
    lm's avatar
    lm committed
    void MapWidget::mapproviderSelected(QAction* action)
    {
        //delete mapadapter;
        mapButton->setText(action->text());
        if (action == osmAction)
        {
            int zoom = mapadapter->adaptedZoom();
            mc->setZoom(0);
    
    lm's avatar
    lm committed
            mapadapter = new OSMMapAdapter();
            l->setMapAdapter(mapadapter);
            geomLayer->setMapAdapter(mapadapter);
    
    lm's avatar
    lm committed
            mc->updateRequestNew();
            mc->setZoom(zoom);
    
    //        yahooActionOverlay->setEnabled(false);
    
    lm's avatar
    lm committed
            overlay->setVisible(false);
    
    //        yahooActionOverlay->setChecked(false);
    
    lm's avatar
    lm committed
        }
        else if (action == yahooActionMap)
        {
            int zoom = mapadapter->adaptedZoom();
            mc->setZoom(0);
    
            mapadapter = new YahooMapAdapter();
            l->setMapAdapter(mapadapter);
            geomLayer->setMapAdapter(mapadapter);
    
            mc->updateRequestNew();
            mc->setZoom(zoom);
    
    //        yahooActionOverlay->setEnabled(false);
    
    lm's avatar
    lm committed
            overlay->setVisible(false);
    
    //        yahooActionOverlay->setChecked(false);
    
    lm's avatar
    lm committed
        }
        else if (action == yahooActionSatellite)
        {
            int zoom = mapadapter->adaptedZoom();
            QPointF a = mc->currentCoordinate();
            mc->setZoom(0);
    
    lm's avatar
    lm committed
            mapadapter = new YahooMapAdapter("us.maps3.yimg.com", "/aerial.maps.yimg.com/png?v=1.7&t=a&s=256&x=%2&y=%3&z=%1");
            l->setMapAdapter(mapadapter);
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
            mc->updateRequestNew();
            mc->setZoom(zoom);
    
    //        yahooActionOverlay->setEnabled(true);
    
    lm's avatar
    lm committed
        }
        else if (action == googleActionMap)
        {
            int zoom = mapadapter->adaptedZoom();
            mc->setZoom(0);
            mapadapter = new GoogleMapAdapter();
            l->setMapAdapter(mapadapter);
            geomLayer->setMapAdapter(mapadapter);
    
            mc->updateRequestNew();
            mc->setZoom(zoom);
    
    //        yahooActionOverlay->setEnabled(false);
    
    lm's avatar
    lm committed
            overlay->setVisible(false);
    
    //        yahooActionOverlay->setChecked(false);
    
    lm's avatar
    lm committed
        }
        else if (action == googleSatAction)
        {
            int zoom = mapadapter->adaptedZoom();
            mc->setZoom(0);
            mapadapter = new GoogleSatMapAdapter();
            l->setMapAdapter(mapadapter);
            geomLayer->setMapAdapter(mapadapter); 
    
            mc->updateRequestNew();
            mc->setZoom(zoom);
    
    //        yahooActionOverlay->setEnabled(false);
    
    lm's avatar
    lm committed
            overlay->setVisible(false);
    
    //        yahooActionOverlay->setChecked(false);
    
    lm's avatar
    lm committed
        }
        else
        {
            mapButton->setText("Select..");
        }
    
    void MapWidget::createPathButtonClicked(bool checked)
    
    lm's avatar
    lm committed
        if (createPath->isChecked())
        {
            // change the cursor shape
            this->setCursor(Qt::PointingHandCursor);
    
            mc->setMouseMode(qmapcontrol::MapControl::None);
    
    lm's avatar
    lm committed
    
            // Clear the previous WP track
            // TODO: Move this to an actual clear track button and add a warning dialog
            mc->layer("Waypoints")->clearGeometries();
            wps.clear();
            path->setPoints(wps);
            mc->layer("Waypoints")->addGeometry(path);
    
    lm's avatar
    lm committed
        } else {
            this->setCursor(Qt::ArrowCursor);
    
            mc->setMouseMode(qmapcontrol::MapControl::Panning);
    
    
    void MapWidget::captureMapClick(const QMouseEvent* event, const QPointF coordinate){
    
      if (QEvent::MouseButtonRelease == event->type() && createPath->isChecked()){
    
        str = QString("WP%1").arg(path->numberOfPoints());
    
    
        // create the WP and set everything in the LineString to display the path
    
        CirclePoint* tempCirclePoint = new CirclePoint(coordinate.x(), coordinate.y(), 10, str);
        mc->layer("Waypoints")->addGeometry(tempCirclePoint);
    
        Point* tempPoint = new Point(coordinate.x(), coordinate.y(),str);
        wps.append(tempPoint);
        path->addPoint(tempPoint);
    
        wpIndex.insert(str,tempPoint);
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    void MapWidget::captureGeometryClick(Geometry* geom, QPoint point){
    
      Q_UNUSED(geom);
      Q_UNUSED(point);
    
      mc->setMouseMode(qmapcontrol::MapControl::None);
    
    
    }
    
    void MapWidget::captureGeometryDrag(Geometry* geom, QPointF coordinate){
    
      Q_UNUSED(coordinate);
    
      Point* point2Find;
      point2Find = wpIndex[geom->name()];
      point2Find->setCoordinate(coordinate);
    
      point2Find = dynamic_cast <Point*> (geom);
      point2Find->setCoordinate(coordinate);
    
      // Refresh the screen
      mc->updateRequestNew();
    }
    
    void MapWidget::captureGeometryEndDrag(Geometry* geom, QPointF coordinate){
      mc->setMouseMode(qmapcontrol::MapControl::Panning);
    
    //  qDebug() << geom->name();
    //  qDebug() << geom->GeometryType;
    //  qDebug() << point;
    
    pixhawk's avatar
    pixhawk committed
    MapWidget::~MapWidget()
    {
        delete m_ui;
    }
    
    lm's avatar
    lm committed
    /**
     *
     * @param uas the UAS/MAV to monitor/display with the HUD
     */
    void MapWidget::addUAS(UASInterface* uas)
    {
        mav = uas;
    
        connect(uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
    
    lm's avatar
    lm committed
    /**
     * Updates the global position of one MAV and append the last movement to the trail
     *
     * @param uas The unmanned air system
     * @param lat Latitude in WGS84 ellipsoid
     * @param lon Longitutde in WGS84 ellipsoid
     * @param alt Altitude over mean sea level
     * @param usec Timestamp of the position message in milliseconds FIXME will move to microseconds
     */
    
    lm's avatar
    lm committed
    void MapWidget::updateGlobalPosition(UASInterface* uas, double lat, double lon, double alt, quint64 usec)
    {
        Q_UNUSED(usec);
    
        Q_UNUSED(alt); // FIXME Use altitude
    
    pixhawk's avatar
    pixhawk committed
        quint64 currTime = MG::TIME::getGroundTimeNow();
    
        if (currTime - lastUpdate > 90)
    
    pixhawk's avatar
    pixhawk committed
        {
            lastUpdate = currTime;
    
            // create a LineString
            //QList<Point*> points;
            // Points with a circle
            // A QPen can be used to customize the
            //pointpen->setWidth(3);
            //points.append(new CirclePoint(lat, lon, 10, uas->getUASName(), Point::Middle, pointpen));
    
            if (!uasIcons.contains(uas->getUASID()))
            {
                // Get the UAS color
                QColor uasColor = uas->getColor();
    
                // Icon
                QPen* pointpen = new QPen(uasColor);
                CirclePoint* p = new CirclePoint(lat, lon, 10, uas->getUASName(), Point::Middle, pointpen);
                uasIcons.insert(uas->getUASID(), p);
    
    lm's avatar
    lm committed
                geomLayer->addGeometry(p);
    
    
                // Line
                // A QPen also can use transparency
    
                QList<Point*> points;
                points.append(new Point(lat, lon, QString("lat: %1 lon: %2").arg(lat, lon)));
                QPen* linepen = new QPen(uasColor.darker());
                linepen->setWidth(2);
                // Add the Points and the QPen to a LineString
                LineString* ls = new LineString(points, uas->getUASName(), linepen);
                uasTrails.insert(uas->getUASID(), ls);
    
                // Add the LineString to the layer
    
    lm's avatar
    lm committed
                geomLayer->addGeometry(ls);
    
            }
            else
            {
                CirclePoint* p = uasIcons.value(uas->getUASID());
                p->setCoordinate(QPointF(lat, lon));
                // Extend trail
                uasTrails.value(uas->getUASID())->addPoint(new Point(lat, lon, QString("lat: %1 lon: %2").arg(lat, lon)));
            }
    
            //    points.append(new CirclePoint(8.275145, 50.016992, 15, "Wiesbaden-Mainz-Kastel, Johannes-Goßner-Straße", Point::Middle, pointpen));
            //    points.append(new CirclePoint(8.270476, 50.021426, 15, "Wiesbaden-Mainz-Kastel, Ruthof", Point::Middle, pointpen));
            //    // "Blind" Points
            //    points.append(new Point(8.266445, 50.025913, "Wiesbaden-Mainz-Kastel, Mudra Kaserne"));
            //    points.append(new Point(8.260378, 50.030345, "Wiesbaden-Mainz-Amoneburg, Dyckerhoffstraße"));
    
            // Connect click events of the layer to this object
    
            // connect(osmLayer, SIGNAL(geometryClicked(Geometry*, QPoint)),
    
            //                  this, SLOT(geometryClicked(Geometry*, QPoint)));
    
            // Sets the view to the interesting area
            //QList<QPointF> view;
            //view.append(QPointF(8.24764, 50.0319));
            //view.append(QPointF(8.28412, 49.9998));
            // mc->setView(view);
            updatePosition(0, lat, lon);
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    pixhawk's avatar
    pixhawk committed
    
    
    lm's avatar
    lm committed
    /**
     * Center the view on this position
     */
    
    lm's avatar
    lm committed
    void MapWidget::updatePosition(float time, double lat, double lon)
    
    pixhawk's avatar
    pixhawk committed
    {
    
    lm's avatar
    lm committed
        Q_UNUSED(time);
    
        //gpsposition->setText(QString::number(time) + " / " + QString::number(lat) + " / " + QString::number(lon));
    
    pixhawk's avatar
    pixhawk committed
        if (followgps->isChecked())
        {
    
    lm's avatar
    lm committed
            mc->setView(QPointF(lat, lon));
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    void MapWidget::wheelEvent(QWheelEvent *event)
    {
        int numDegrees = event->delta() / 8;
        int numSteps = numDegrees / 15;
        // Calculate new zoom level
        int newZoom = mc->currentZoom()+numSteps;
        // Set new zoom level, level is bounded by map control
        mc->setZoom(newZoom);
        // Detail zoom level is the number of steps zoomed in further
        // after the bounding has taken effect
        detailZoom = qAbs(qMin(0, mc->currentZoom()-newZoom));
    }
    
    void MapWidget::keyPressEvent(QKeyEvent *event)
    {
        switch (event->key()) {
        case Qt::Key_Plus:
            mc->zoomIn();
            break;
        case Qt::Key_Minus:
            mc->zoomOut();
            break;
        case Qt::Key_Left:
            mc->scrollLeft(this->width()/scrollStep);
            break;
        case Qt::Key_Right:
            mc->scrollRight(this->width()/scrollStep);
            break;
        case Qt::Key_Down:
            mc->scrollDown(this->width()/scrollStep);
            break;
        case Qt::Key_Up:
            mc->scrollUp(this->width()/scrollStep);
            break;
        default:
            QWidget::keyPressEvent(event);
        }
    }
    
    
    void MapWidget::resizeEvent(QResizeEvent* event )
    
    pixhawk's avatar
    pixhawk committed
    {
    
        Q_UNUSED(event);
        mc->resize(this->size());
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    void MapWidget::changeEvent(QEvent *e)
    {
        QWidget::changeEvent(e);
        switch (e->type()) {
        case QEvent::LanguageChange:
            m_ui->retranslateUi(this);
            break;
        default:
            break;
        }
    }