Skip to content
MapWidget.cc 18 KiB
Newer Older
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);
//        // 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);
//        wpIndex.clear();
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);

    // emit signal mouse was clicked
    emit captureMapCoordinateClick(coordinate);
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;
    }
}
void MapWidget::clearPath()
{
    // Clear the previous WP track

    mc->layer("Waypoints")->clearGeometries();
    wps.clear();
    path->setPoints(wps);
    mc->layer("Waypoints")->addGeometry(path);
    wpIndex.clear();
    mc->updateRequestNew();
}