You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
908 lines
32 KiB
908 lines
32 KiB
#include "QGCMapWidget.h" |
|
#include "QGCMapToolBar.h" |
|
#include "UASInterface.h" |
|
#include "UASManager.h" |
|
#include "MAV2DIcon.h" |
|
#include "Waypoint2DIcon.h" |
|
#include "UASWaypointManager.h" |
|
#include "ArduPilotMegaMAV.h" |
|
|
|
QGCMapWidget::QGCMapWidget(QWidget *parent) : |
|
mapcontrol::OPMapWidget(parent), |
|
firingWaypointChange(NULL), |
|
maxUpdateInterval(2.1f), // 2 seconds |
|
followUAVEnabled(false), |
|
trailType(mapcontrol::UAVTrailType::ByTimeElapsed), |
|
trailInterval(2.0f), |
|
followUAVID(0), |
|
mapInitialized(false), |
|
mapPositionInitialized(false), |
|
homeAltitude(0), |
|
uas(NULL) |
|
{ |
|
currWPManager = UASManager::instance()->getActiveUASWaypointManager(); |
|
waypointLines.insert(0, new QGraphicsItemGroup(map)); |
|
connect(currWPManager, SIGNAL(waypointEditableListChanged(int)), this, SLOT(updateWaypointList(int))); |
|
connect(currWPManager, SIGNAL(waypointEditableChanged(int, Waypoint*)), this, SLOT(updateWaypoint(int,Waypoint*))); |
|
connect(this, SIGNAL(waypointCreated(Waypoint*)), currWPManager, SLOT(addWaypointEditable(Waypoint*))); |
|
connect(this, SIGNAL(waypointChanged(Waypoint*)), currWPManager, SLOT(notifyOfChangeEditable(Waypoint*))); |
|
offlineMode = true; |
|
// Widget is inactive until shown |
|
defaultGuidedAlt = -1; |
|
loadSettings(false); |
|
|
|
//handy for debugging: |
|
//this->SetShowTileGridLines(true); |
|
|
|
//default appears to be Google Hybrid, and is broken currently |
|
#if defined MAP_DEFAULT_TYPE_BING |
|
this->SetMapType(MapType::BingHybrid); |
|
#elif defined MAP_DEFAULT_TYPE_GOOGLE |
|
this->SetMapType(MapType::GoogleHybrid); |
|
#else |
|
this->SetMapType(MapType::OpenStreetMap); |
|
#endif |
|
|
|
this->setContextMenuPolicy(Qt::ActionsContextMenu); |
|
|
|
// Go to options |
|
QAction *guidedaction = new QAction(this); |
|
guidedaction->setText("Go To Here (Guided Mode)"); |
|
connect(guidedaction,SIGNAL(triggered()),this,SLOT(guidedActionTriggered())); |
|
this->addAction(guidedaction); |
|
guidedaction = new QAction(this); |
|
guidedaction->setText("Go To Here Alt (Guided Mode)"); |
|
connect(guidedaction,SIGNAL(triggered()),this,SLOT(guidedAltActionTriggered())); |
|
this->addAction(guidedaction); |
|
// Point camera option |
|
QAction *cameraaction = new QAction(this); |
|
cameraaction->setText("Point Camera Here"); |
|
connect(cameraaction,SIGNAL(triggered()),this,SLOT(cameraActionTriggered())); |
|
this->addAction(cameraaction); |
|
// Set home location option |
|
QAction *sethomeaction = new QAction(this); |
|
sethomeaction->setText("Set Home Location Here"); |
|
connect(sethomeaction,SIGNAL(triggered()),this,SLOT(setHomeActionTriggered())); |
|
this->addAction(sethomeaction); |
|
} |
|
void QGCMapWidget::guidedActionTriggered() |
|
{ |
|
if (!uas) |
|
{ |
|
QMessageBox::information(0,"Error","Please connect first"); |
|
return; |
|
} |
|
if (currWPManager) |
|
{ |
|
if (defaultGuidedAlt == -1) |
|
{ |
|
if (!guidedAltActionTriggered()) |
|
{ |
|
return; |
|
} |
|
} |
|
// Create new waypoint and send it to the WPManager to send out. |
|
internals::PointLatLng pos = map->FromLocalToLatLng(contextMousePressPos.x(), contextMousePressPos.y()); |
|
qDebug() << "Guided action requested. Lat:" << pos.Lat() << "Lon:" << pos.Lng(); |
|
Waypoint wp; |
|
wp.setLatitude(pos.Lat()); |
|
wp.setLongitude(pos.Lng()); |
|
wp.setAltitude(defaultGuidedAlt); |
|
currWPManager->goToWaypoint(&wp); |
|
} |
|
} |
|
bool QGCMapWidget::guidedAltActionTriggered() |
|
{ |
|
if (!uas) |
|
{ |
|
QMessageBox::information(0,"Error","Please connect first"); |
|
return false; |
|
} |
|
bool ok = false; |
|
int tmpalt = QInputDialog::getInt(this,"Altitude","Enter default altitude (in meters) of destination point for guided mode",100,0,30000,1,&ok); |
|
if (!ok) |
|
{ |
|
//Use has chosen cancel. Do not send the waypoint |
|
return false; |
|
} |
|
defaultGuidedAlt = tmpalt; |
|
guidedActionTriggered(); |
|
return true; |
|
} |
|
void QGCMapWidget::cameraActionTriggered() |
|
{ |
|
if (!uas) |
|
{ |
|
QMessageBox::information(0,"Error","Please connect first"); |
|
return; |
|
} |
|
ArduPilotMegaMAV *newmav = qobject_cast<ArduPilotMegaMAV*>(this->uas); |
|
if (newmav) |
|
{ |
|
newmav->setMountConfigure(4,true,true,true); |
|
internals::PointLatLng pos = map->FromLocalToLatLng(contextMousePressPos.x(), contextMousePressPos.y()); |
|
newmav->setMountControl(pos.Lat(),pos.Lng(),100,true); |
|
} |
|
} |
|
|
|
/** |
|
* @brief QGCMapWidget::setHomeActionTriggered |
|
*/ |
|
bool QGCMapWidget::setHomeActionTriggered() |
|
{ |
|
if (!uas) |
|
{ |
|
QMessageBox::information(0,"Error","Please connect first"); |
|
return false; |
|
} |
|
UASManagerInterface *uasManager = UASManager::instance(); |
|
if (!uasManager) { return false; } |
|
|
|
// Enter an altitude |
|
bool ok = false; |
|
double alt = QInputDialog::getDouble(this,"Home Altitude","Enter altitude (in meters) of new home location",0.0,0.0,30000.0,2,&ok); |
|
if (!ok) return false; //Use has chosen cancel. Do not send the waypoint |
|
|
|
// Create new waypoint and send it to the WPManager to send out. |
|
internals::PointLatLng pos = map->FromLocalToLatLng(contextMousePressPos.x(), contextMousePressPos.y()); |
|
qDebug("Set home location sent. Lat: %f, Lon: %f, Alt: %f.", pos.Lat(), pos.Lng(), alt); |
|
|
|
bool success = uasManager->setHomePositionAndNotify(pos.Lat(),pos.Lng(), alt); |
|
|
|
qDebug() << ((success)? "Set new home location." : "Failed to set new home location."); |
|
|
|
return success; |
|
} |
|
|
|
void QGCMapWidget::mousePressEvent(QMouseEvent *event) |
|
{ |
|
|
|
// Store right-click event presses separate for context menu |
|
// TODO add check if click was on map, or popup box. |
|
if (event->button() == Qt::RightButton) { |
|
contextMousePressPos = event->pos(); |
|
} |
|
|
|
mapcontrol::OPMapWidget::mousePressEvent(event); |
|
} |
|
|
|
void QGCMapWidget::mouseReleaseEvent(QMouseEvent *event) |
|
{ |
|
mousePressPos = event->pos(); |
|
mapcontrol::OPMapWidget::mouseReleaseEvent(event); |
|
|
|
// If the mouse is released, we can't be dragging |
|
if (firingWaypointChange) { |
|
firingWaypointChange->setChanged(); |
|
firingWaypointChange = NULL; |
|
} |
|
} |
|
|
|
QGCMapWidget::~QGCMapWidget() |
|
{ |
|
SetShowHome(false); // doing this appears to stop the map lib crashing on exit |
|
SetShowUAV(false); // " " |
|
storeSettings(); |
|
} |
|
|
|
void QGCMapWidget::showEvent(QShowEvent* event) |
|
{ |
|
// Disable OP's standard UAV, we have more than one |
|
SetShowUAV(false); |
|
|
|
// Pass on to parent widget |
|
OPMapWidget::showEvent(event); |
|
|
|
// Connect map updates to the adapter slots |
|
connect(this, SIGNAL(WPValuesChanged(WayPointItem*)), this, SLOT(handleMapWaypointEdit(WayPointItem*))); |
|
|
|
connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(addUAS(UASInterface*)), Qt::UniqueConnection); |
|
connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(activeUASSet(UASInterface*)), Qt::UniqueConnection); |
|
connect(UASManager::instance(), SIGNAL(homePositionChanged(double,double,double)), this, SLOT(updateHomePosition(double,double,double))); |
|
|
|
foreach (UASInterface* uas, UASManager::instance()->getUASList()) |
|
{ |
|
addUAS(uas); |
|
} |
|
|
|
|
|
if (!mapInitialized) |
|
{ |
|
internals::PointLatLng pos_lat_lon = internals::PointLatLng(0, 0); |
|
|
|
SetMouseWheelZoomType(internals::MouseWheelZoomType::MousePositionWithoutCenter); // set how the mouse wheel zoom functions |
|
SetFollowMouse(true); // we want a contiuous mouse position reading |
|
|
|
SetShowHome(true); // display the HOME position on the map |
|
Home->SetSafeArea(0); // set radius (meters) |
|
Home->SetShowSafeArea(false); // show the safe area |
|
Home->SetCoord(pos_lat_lon); // set the HOME position |
|
|
|
setFrameStyle(QFrame::NoFrame); // no border frame |
|
setBackgroundBrush(QBrush(Qt::black)); // tile background |
|
|
|
if (!UASManager::instance()->getActiveUAS()) { |
|
SetCurrentPosition(pos_lat_lon); // set the map position to default |
|
} |
|
|
|
// Set home |
|
updateHomePosition(UASManager::instance()->getHomeLatitude(), UASManager::instance()->getHomeLongitude(), UASManager::instance()->getHomeAltitude()); |
|
|
|
// Set currently selected system |
|
activeUASSet(UASManager::instance()->getActiveUAS()); |
|
setFocus(); |
|
|
|
// Start timer |
|
connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateGlobalPosition())); |
|
mapInitialized = true; |
|
//QTimer::singleShot(800, this, SLOT(loadSettings())); |
|
} |
|
updateTimer.start(maxUpdateInterval*1000); |
|
// Update all UAV positions |
|
updateGlobalPosition(); |
|
} |
|
|
|
void QGCMapWidget::hideEvent(QHideEvent* event) |
|
{ |
|
updateTimer.stop(); |
|
storeSettings(); |
|
OPMapWidget::hideEvent(event); |
|
} |
|
|
|
/** |
|
* @param changePosition Load also the last position from settings and update the map position. |
|
*/ |
|
void QGCMapWidget::loadSettings(bool changePosition) |
|
{ |
|
// Atlantic Ocean near Africa, coordinate origin |
|
double lastZoom = 1; |
|
double lastLat = 0; |
|
double lastLon = 0; |
|
|
|
QSettings settings; |
|
settings.beginGroup("QGC_MAPWIDGET"); |
|
if (changePosition) |
|
{ |
|
lastLat = settings.value("LAST_LATITUDE", lastLat).toDouble(); |
|
lastLon = settings.value("LAST_LONGITUDE", lastLon).toDouble(); |
|
lastZoom = settings.value("LAST_ZOOM", lastZoom).toDouble(); |
|
} |
|
trailType = static_cast<mapcontrol::UAVTrailType::Types>(settings.value("TRAIL_TYPE", trailType).toInt()); |
|
trailInterval = settings.value("TRAIL_INTERVAL", trailInterval).toFloat(); |
|
settings.endGroup(); |
|
|
|
// SET CORRECT MENU CHECKBOXES |
|
// Set the correct trail interval |
|
if (trailType == mapcontrol::UAVTrailType::ByDistance) |
|
{ |
|
// XXX |
|
qDebug() << "WARNING: Settings loading for trail type (ByDistance) not implemented"; |
|
} |
|
else if (trailType == mapcontrol::UAVTrailType::ByTimeElapsed) |
|
{ |
|
// XXX |
|
qDebug() << "WARNING: Settings loading for trail type (ByTimeElapsed) not implemented"; |
|
} |
|
|
|
// SET TRAIL TYPE |
|
foreach (mapcontrol::UAVItem* uav, GetUAVS()) |
|
{ |
|
// Set the correct trail type |
|
uav->SetTrailType(trailType); |
|
// Set the correct trail interval |
|
if (trailType == mapcontrol::UAVTrailType::ByDistance) |
|
{ |
|
uav->SetTrailDistance(trailInterval); |
|
} |
|
else if (trailType == mapcontrol::UAVTrailType::ByTimeElapsed) |
|
{ |
|
uav->SetTrailTime(trailInterval); |
|
} |
|
} |
|
|
|
// SET INITIAL POSITION AND ZOOM |
|
internals::PointLatLng pos_lat_lon = internals::PointLatLng(lastLat, lastLon); |
|
SetCurrentPosition(pos_lat_lon); // set the map position |
|
SetZoom(lastZoom); // set map zoom level |
|
} |
|
|
|
void QGCMapWidget::storeSettings() |
|
{ |
|
QSettings settings; |
|
settings.beginGroup("QGC_MAPWIDGET"); |
|
internals::PointLatLng pos = CurrentPosition(); |
|
settings.setValue("LAST_LATITUDE", pos.Lat()); |
|
settings.setValue("LAST_LONGITUDE", pos.Lng()); |
|
settings.setValue("LAST_ZOOM", ZoomReal()); |
|
settings.setValue("TRAIL_TYPE", static_cast<int>(trailType)); |
|
settings.setValue("TRAIL_INTERVAL", trailInterval); |
|
settings.endGroup(); |
|
settings.sync(); |
|
} |
|
|
|
void QGCMapWidget::mouseDoubleClickEvent(QMouseEvent* event) |
|
{ |
|
// If a waypoint manager is available |
|
if (currWPManager) |
|
{ |
|
// Create new waypoint |
|
internals::PointLatLng pos = map->FromLocalToLatLng(event->pos().x(), event->pos().y()); |
|
Waypoint* wp = currWPManager->createWaypoint(); |
|
// wp->blockSignals(true); |
|
// wp->setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT); |
|
wp->setLatitude(pos.Lat()); |
|
wp->setLongitude(pos.Lng()); |
|
wp->setFrame((MAV_FRAME)currWPManager->getFrameRecommendation()); |
|
wp->setAltitude(currWPManager->getAltitudeRecommendation()); |
|
// wp->blockSignals(false); |
|
// currWPManager->notifyOfChangeEditable(wp); |
|
} |
|
|
|
OPMapWidget::mouseDoubleClickEvent(event); |
|
} |
|
|
|
|
|
/** |
|
* |
|
* @param uas the UAS/MAV to monitor/display with the map widget |
|
*/ |
|
void QGCMapWidget::addUAS(UASInterface* uas) |
|
{ |
|
connect(uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64))); |
|
connect(uas, SIGNAL(systemSpecsChanged(int)), this, SLOT(updateSystemSpecs(int))); |
|
if (!waypointLines.value(uas->getUASID(), NULL)) { |
|
waypointLines.insert(uas->getUASID(), new QGraphicsItemGroup(map)); |
|
} else { |
|
foreach (QGraphicsItem* item, waypointLines.value(uas->getUASID())->childItems()) |
|
{ |
|
delete item; |
|
} |
|
} |
|
} |
|
|
|
void QGCMapWidget::activeUASSet(UASInterface* uas) |
|
{ |
|
// Only execute if proper UAS is set |
|
this->uas = uas; |
|
|
|
// Disconnect old MAV manager |
|
if (currWPManager) |
|
{ |
|
// Disconnect the waypoint manager / data storage from the UI |
|
disconnect(currWPManager, SIGNAL(waypointEditableListChanged(int)), this, SLOT(updateWaypointList(int))); |
|
disconnect(currWPManager, SIGNAL(waypointEditableChanged(int, Waypoint*)), this, SLOT(updateWaypoint(int,Waypoint*))); |
|
disconnect(this, SIGNAL(waypointCreated(Waypoint*)), currWPManager, SLOT(addWaypointEditable(Waypoint*))); |
|
disconnect(this, SIGNAL(waypointChanged(Waypoint*)), currWPManager, SLOT(notifyOfChangeEditable(Waypoint*))); |
|
} |
|
|
|
// Attach the new waypoint manager if a new UAS was selected. Otherwise, indicate |
|
// that no such manager exists. |
|
if (uas) |
|
{ |
|
currWPManager = uas->getWaypointManager(); |
|
|
|
updateSelectedSystem(uas->getUASID()); |
|
followUAVID = uas->getUASID(); |
|
updateWaypointList(uas->getUASID()); |
|
|
|
// Connect the waypoint manager / data storage to the UI |
|
connect(currWPManager, SIGNAL(waypointEditableListChanged(int)), this, SLOT(updateWaypointList(int))); |
|
connect(currWPManager, SIGNAL(waypointEditableChanged(int, Waypoint*)), this, SLOT(updateWaypoint(int,Waypoint*))); |
|
connect(this, SIGNAL(waypointCreated(Waypoint*)), currWPManager, SLOT(addWaypointEditable(Waypoint*))); |
|
connect(this, SIGNAL(waypointChanged(Waypoint*)), currWPManager, SLOT(notifyOfChangeEditable(Waypoint*))); |
|
|
|
if (!mapPositionInitialized) { |
|
internals::PointLatLng pos_lat_lon = internals::PointLatLng(uas->getLatitude(), uas->getLongitude()); |
|
SetCurrentPosition(pos_lat_lon); |
|
|
|
// Zoom in |
|
SetZoom(13); |
|
|
|
mapPositionInitialized = true; |
|
} |
|
} |
|
else |
|
{ |
|
currWPManager = NULL; |
|
} |
|
} |
|
|
|
/** |
|
* 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 |
|
*/ |
|
void QGCMapWidget::updateGlobalPosition(UASInterface* uas, double lat, double lon, double alt, quint64 usec) |
|
{ |
|
Q_UNUSED(usec); |
|
|
|
// Immediate update |
|
if (maxUpdateInterval == 0) |
|
{ |
|
// Get reference to graphic UAV item |
|
mapcontrol::UAVItem* uav = GetUAV(uas->getUASID()); |
|
// Check if reference is valid, else create a new one |
|
if (uav == NULL) |
|
{ |
|
MAV2DIcon* newUAV = new MAV2DIcon(map, this, uas); |
|
newUAV->setParentItem(map); |
|
UAVS.insert(uas->getUASID(), newUAV); |
|
uav = GetUAV(uas->getUASID()); |
|
// Set the correct trail type |
|
uav->SetTrailType(trailType); |
|
// Set the correct trail interval |
|
if (trailType == mapcontrol::UAVTrailType::ByDistance) |
|
{ |
|
uav->SetTrailDistance(trailInterval); |
|
} |
|
else if (trailType == mapcontrol::UAVTrailType::ByTimeElapsed) |
|
{ |
|
uav->SetTrailTime(trailInterval); |
|
} |
|
} |
|
|
|
// Set new lat/lon position of UAV icon |
|
internals::PointLatLng pos_lat_lon = internals::PointLatLng(lat, lon); |
|
uav->SetUAVPos(pos_lat_lon, alt); |
|
// Follow status |
|
if (followUAVEnabled && uas->getUASID() == followUAVID) SetCurrentPosition(pos_lat_lon); |
|
// Convert from radians to degrees and apply |
|
uav->SetUAVHeading((uas->getYaw()/M_PI)*180.0f); |
|
} |
|
} |
|
|
|
/** |
|
* Pulls in the positions of all UAVs from the UAS manager |
|
*/ |
|
void QGCMapWidget::updateGlobalPosition() |
|
{ |
|
QList<UASInterface*> systems = UASManager::instance()->getUASList(); |
|
foreach (UASInterface* system, systems) |
|
{ |
|
// Get reference to graphic UAV item |
|
mapcontrol::UAVItem* uav = GetUAV(system->getUASID()); |
|
// Check if reference is valid, else create a new one |
|
if (uav == NULL) |
|
{ |
|
MAV2DIcon* newUAV = new MAV2DIcon(map, this, system); |
|
AddUAV(system->getUASID(), newUAV); |
|
uav = newUAV; |
|
uav->SetTrailTime(1); |
|
uav->SetTrailDistance(5); |
|
uav->SetTrailType(mapcontrol::UAVTrailType::ByTimeElapsed); |
|
} |
|
|
|
// Set new lat/lon position of UAV icon |
|
internals::PointLatLng pos_lat_lon = internals::PointLatLng(system->getLatitude(), system->getLongitude()); |
|
uav->SetUAVPos(pos_lat_lon, system->getAltitudeAMSL()); |
|
// Follow status |
|
if (followUAVEnabled && system->getUASID() == followUAVID) SetCurrentPosition(pos_lat_lon); |
|
// Convert from radians to degrees and apply |
|
uav->SetUAVHeading((system->getYaw()/M_PI)*180.0f); |
|
} |
|
} |
|
|
|
void QGCMapWidget::updateLocalPosition() |
|
{ |
|
QList<UASInterface*> systems = UASManager::instance()->getUASList(); |
|
foreach (UASInterface* system, systems) |
|
{ |
|
// Get reference to graphic UAV item |
|
mapcontrol::UAVItem* uav = GetUAV(system->getUASID()); |
|
// Check if reference is valid, else create a new one |
|
if (uav == NULL) |
|
{ |
|
MAV2DIcon* newUAV = new MAV2DIcon(map, this, system); |
|
AddUAV(system->getUASID(), newUAV); |
|
uav = newUAV; |
|
uav->SetTrailTime(1); |
|
uav->SetTrailDistance(5); |
|
uav->SetTrailType(mapcontrol::UAVTrailType::ByTimeElapsed); |
|
} |
|
|
|
// Set new lat/lon position of UAV icon |
|
internals::PointLatLng pos_lat_lon = internals::PointLatLng(system->getLatitude(), system->getLongitude()); |
|
uav->SetUAVPos(pos_lat_lon, system->getAltitudeAMSL()); |
|
// Follow status |
|
if (followUAVEnabled && system->getUASID() == followUAVID) SetCurrentPosition(pos_lat_lon); |
|
// Convert from radians to degrees and apply |
|
uav->SetUAVHeading((system->getYaw()/M_PI)*180.0f); |
|
} |
|
} |
|
|
|
void QGCMapWidget::updateLocalPositionEstimates() |
|
{ |
|
updateLocalPosition(); |
|
} |
|
|
|
|
|
void QGCMapWidget::updateSystemSpecs(int uas) |
|
{ |
|
foreach (mapcontrol::UAVItem* p, UAVS.values()) |
|
{ |
|
MAV2DIcon* icon = dynamic_cast<MAV2DIcon*>(p); |
|
if (icon && icon->getUASId() == uas) |
|
{ |
|
// Set new airframe |
|
icon->setAirframe(UASManager::instance()->getUASForId(uas)->getAirframe()); |
|
icon->drawIcon(); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Does not update the system type or configuration, only the selected status |
|
*/ |
|
void QGCMapWidget::updateSelectedSystem(int uas) |
|
{ |
|
foreach (mapcontrol::UAVItem* p, UAVS.values()) |
|
{ |
|
MAV2DIcon* icon = dynamic_cast<MAV2DIcon*>(p); |
|
if (icon) |
|
{ |
|
// Set as selected if ids match |
|
icon->setSelectedUAS((icon->getUASId() == uas)); |
|
} |
|
} |
|
} |
|
|
|
|
|
// MAP NAVIGATION |
|
void QGCMapWidget::showGoToDialog() |
|
{ |
|
bool ok; |
|
QString text = QInputDialog::getText(this, tr("Please enter coordinates"), |
|
tr("Coordinates (Lat,Lon):"), QLineEdit::Normal, |
|
QString("%1,%2").arg(CurrentPosition().Lat(), 0, 'g', 6).arg(CurrentPosition().Lng(), 0, 'g', 6), &ok); |
|
if (ok && !text.isEmpty()) |
|
{ |
|
QStringList split = text.split(","); |
|
if (split.length() == 2) |
|
{ |
|
bool convert; |
|
double latitude = split.first().toDouble(&convert); |
|
ok &= convert; |
|
double longitude = split.last().toDouble(&convert); |
|
ok &= convert; |
|
|
|
if (ok) |
|
{ |
|
internals::PointLatLng pos_lat_lon = internals::PointLatLng(latitude, longitude); |
|
SetCurrentPosition(pos_lat_lon); // set the map position |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
void QGCMapWidget::updateHomePosition(double latitude, double longitude, double altitude) |
|
{ |
|
qDebug() << "HOME SET TO: " << latitude << longitude << altitude; |
|
Home->SetCoord(internals::PointLatLng(latitude, longitude)); |
|
Home->SetAltitude(altitude); |
|
homeAltitude = altitude; |
|
SetShowHome(true); // display the HOME position on the map |
|
Home->RefreshPos(); |
|
} |
|
|
|
void QGCMapWidget::goHome() |
|
{ |
|
SetCurrentPosition(Home->Coord()); |
|
SetZoom(17); |
|
} |
|
|
|
/** |
|
* Limits the update rate on the specified interval. Set to zero (0) to run at maximum |
|
* telemetry speed. Recommended rate is 2 s. |
|
*/ |
|
void QGCMapWidget::setUpdateRateLimit(float seconds) |
|
{ |
|
maxUpdateInterval = seconds; |
|
updateTimer.start(maxUpdateInterval*1000); |
|
} |
|
|
|
void QGCMapWidget::cacheVisibleRegion() |
|
{ |
|
internals::RectLatLng rect = map->SelectedArea(); |
|
|
|
if (rect.IsEmpty()) |
|
{ |
|
QMessageBox msgBox(this); |
|
msgBox.setIcon(QMessageBox::Information); |
|
msgBox.setText("Cannot cache tiles for offline use"); |
|
msgBox.setInformativeText("Please select an area first by holding down SHIFT or ALT and selecting the area with the left mouse button."); |
|
msgBox.setStandardButtons(QMessageBox::Ok); |
|
msgBox.setDefaultButton(QMessageBox::Ok); |
|
msgBox.exec(); |
|
} |
|
else |
|
{ |
|
RipMap(); |
|
// Set empty area = unselect area |
|
map->SetSelectedArea(internals::RectLatLng()); |
|
} |
|
} |
|
|
|
|
|
// WAYPOINT MAP INTERACTION FUNCTIONS |
|
|
|
void QGCMapWidget::handleMapWaypointEdit(mapcontrol::WayPointItem* waypoint) |
|
{ |
|
// Block circle updates |
|
Waypoint* wp = iconsToWaypoints.value(waypoint, NULL); |
|
|
|
// Delete UI element if wp doesn't exist |
|
if (!wp) |
|
WPDelete(waypoint); |
|
|
|
// Update WP values |
|
internals::PointLatLng pos = waypoint->Coord(); |
|
|
|
// Block waypoint signals |
|
wp->blockSignals(true); |
|
wp->setLatitude(pos.Lat()); |
|
wp->setLongitude(pos.Lng()); |
|
wp->blockSignals(false); |
|
|
|
|
|
// internals::PointLatLng coord = waypoint->Coord(); |
|
// QString coord_str = " " + QString::number(coord.Lat(), 'f', 6) + " " + QString::number(coord.Lng(), 'f', 6); |
|
// qDebug() << "MAP WP COORD (MAP):" << coord_str << __FILE__ << __LINE__; |
|
// QString wp_str = QString::number(wp->getLatitude(), 'f', 6) + " " + QString::number(wp->getLongitude(), 'f', 6); |
|
// qDebug() << "MAP WP COORD (WP):" << wp_str << __FILE__ << __LINE__; |
|
|
|
// Protect from vicious double update cycle |
|
if (firingWaypointChange == wp) { |
|
return; |
|
} |
|
// Not in cycle, block now from entering it |
|
firingWaypointChange = wp; |
|
|
|
emit waypointChanged(wp); |
|
} |
|
|
|
// WAYPOINT UPDATE FUNCTIONS |
|
|
|
/** |
|
* This function is called if a a single waypoint is updated and |
|
* also if the whole list changes. |
|
*/ |
|
void QGCMapWidget::updateWaypoint(int uas, Waypoint* wp) |
|
{ |
|
//qDebug() << __FILE__ << __LINE__ << "UPDATING WP FUNCTION CALLED"; |
|
// Source of the event was in this widget, do nothing |
|
if (firingWaypointChange == wp) { |
|
return; |
|
} |
|
// Currently only accept waypoint updates from the UAS in focus |
|
// this has to be changed to accept read-only updates from other systems as well. |
|
UASInterface* uasInstance = UASManager::instance()->getUASForId(uas); |
|
if (currWPManager) |
|
{ |
|
// Only accept waypoints in global coordinate frame |
|
if (((wp->getFrame() == MAV_FRAME_GLOBAL) || (wp->getFrame() == MAV_FRAME_GLOBAL_RELATIVE_ALT)) && wp->isNavigationType()) |
|
{ |
|
// We're good, this is a global waypoint |
|
|
|
// Get the index of this waypoint |
|
// note the call to getGlobalFrameAndNavTypeIndexOf() |
|
// as we're only handling global waypoints |
|
int wpindex = currWPManager->getGlobalFrameAndNavTypeIndexOf(wp); |
|
// If not found, return (this should never happen, but helps safety) |
|
if (wpindex < 0) return; |
|
// Mark this wp as currently edited |
|
firingWaypointChange = wp; |
|
|
|
qDebug() << "UPDATING WAYPOINT" << wpindex << "IN 2D MAP"; |
|
|
|
// Check if wp exists yet in map |
|
if (!waypointsToIcons.contains(wp)) |
|
{ |
|
// Create icon for new WP |
|
QColor wpColor(Qt::red); |
|
if (uasInstance) wpColor = uasInstance->getColor(); |
|
Waypoint2DIcon* icon = new Waypoint2DIcon(map, this, wp, wpColor, wpindex); |
|
ConnectWP(icon); |
|
icon->setParentItem(map); |
|
// Update maps to allow inverse data association |
|
waypointsToIcons.insert(wp, icon); |
|
iconsToWaypoints.insert(icon, wp); |
|
|
|
// Add line element if this is NOT the first waypoint |
|
if (wpindex > 0) |
|
{ |
|
// Get predecessor of this WP |
|
QList<Waypoint* > wps = currWPManager->getGlobalFrameAndNavTypeWaypointList(); |
|
Waypoint* wp1 = wps.at(wpindex-1); |
|
mapcontrol::WayPointItem* prevIcon = waypointsToIcons.value(wp1, NULL); |
|
// If we got a valid graphics item, continue |
|
if (prevIcon) |
|
{ |
|
mapcontrol::WaypointLineItem* line = new mapcontrol::WaypointLineItem(prevIcon, icon, wpColor, map); |
|
line->setParentItem(map); |
|
QGraphicsItemGroup* group = waypointLines.value(uas, NULL); |
|
if (group) |
|
{ |
|
group->addToGroup(line); |
|
group->setParentItem(map); |
|
} |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// Waypoint exists, block it's signals and update it |
|
mapcontrol::WayPointItem* icon = waypointsToIcons.value(wp); |
|
// Make sure we don't die on a null pointer |
|
// this should never happen, just a precaution |
|
if (!icon) return; |
|
// Block outgoing signals to prevent an infinite signal loop |
|
// should not happen, just a precaution |
|
this->blockSignals(true); |
|
// Update the WP |
|
Waypoint2DIcon* wpicon = dynamic_cast<Waypoint2DIcon*>(icon); |
|
if (wpicon) |
|
{ |
|
// Let icon read out values directly from waypoint |
|
icon->SetNumber(wpindex); |
|
wpicon->updateWaypoint(); |
|
} |
|
else |
|
{ |
|
// Use safe standard interfaces for non Waypoint-class based wps |
|
icon->SetCoord(internals::PointLatLng(wp->getLatitude(), wp->getLongitude())); |
|
icon->SetAltitude(wp->getAltitude()); |
|
icon->SetHeading(wp->getYaw()); |
|
icon->SetNumber(wpindex); |
|
} |
|
// Re-enable signals again |
|
this->blockSignals(false); |
|
} |
|
|
|
firingWaypointChange = NULL; |
|
|
|
} |
|
else |
|
{ |
|
// Check if the index of this waypoint is larger than the global |
|
// waypoint list. This implies that the coordinate frame of this |
|
// waypoint was changed and the list containing only global |
|
// waypoints was shortened. Thus update the whole list |
|
if (waypointsToIcons.count() > currWPManager->getGlobalFrameAndNavTypeCount()) |
|
{ |
|
updateWaypointList(uas); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Update the whole list of waypoints. This is e.g. necessary if the list order changed. |
|
* The UAS manager will emit the appropriate signal whenever updating the list |
|
* is necessary. |
|
*/ |
|
void QGCMapWidget::updateWaypointList(int uas) |
|
{ |
|
qDebug() << "UPDATE WP LIST IN 2D MAP CALLED FOR UAS" << uas; |
|
// Currently only accept waypoint updates from the UAS in focus |
|
// this has to be changed to accept read-only updates from other systems as well. |
|
UASInterface* uasInstance = UASManager::instance()->getUASForId(uas); |
|
if (currWPManager) |
|
{ |
|
// ORDER MATTERS HERE! |
|
// TWO LOOPS ARE NEEDED - INFINITY LOOP ELSE |
|
|
|
qDebug() << "DELETING NOW OLD WPS"; |
|
|
|
// Delete connecting waypoint lines |
|
QGraphicsItemGroup* group = waypointLines.value(uas, NULL); |
|
if (group) |
|
{ |
|
foreach (QGraphicsItem* item, group->childItems()) |
|
{ |
|
delete item; |
|
} |
|
} |
|
|
|
// Delete first all old waypoints |
|
// this is suboptimal (quadratic, but wps should stay in the sub-100 range anyway) |
|
QList<Waypoint* > wps = currWPManager->getGlobalFrameAndNavTypeWaypointList(); |
|
foreach (Waypoint* wp, waypointsToIcons.keys()) |
|
{ |
|
if (!wps.contains(wp)) |
|
{ |
|
// Get icon to work on |
|
mapcontrol::WayPointItem* icon = waypointsToIcons.value(wp); |
|
waypointsToIcons.remove(wp); |
|
iconsToWaypoints.remove(icon); |
|
WPDelete(icon); |
|
} |
|
} |
|
|
|
// Update all existing waypoints |
|
foreach (Waypoint* wp, waypointsToIcons.keys()) |
|
{ |
|
// Update remaining waypoints |
|
updateWaypoint(uas, wp); |
|
} |
|
|
|
// Update all potentially new waypoints |
|
foreach (Waypoint* wp, wps) |
|
{ |
|
qDebug() << "UPDATING NEW WP" << wp->getId(); |
|
// Update / add only if new |
|
if (!waypointsToIcons.contains(wp)) updateWaypoint(uas, wp); |
|
} |
|
|
|
// Add line element if this is NOT the first waypoint |
|
mapcontrol::WayPointItem* prevIcon = NULL; |
|
foreach (Waypoint* wp, wps) |
|
{ |
|
mapcontrol::WayPointItem* currIcon = waypointsToIcons.value(wp, NULL); |
|
// Do not work on first waypoint, but only increment counter |
|
// do not continue if icon is invalid |
|
if (prevIcon && currIcon) |
|
{ |
|
// If we got a valid graphics item, continue |
|
QColor wpColor(Qt::red); |
|
if (uasInstance) wpColor = uasInstance->getColor(); |
|
mapcontrol::WaypointLineItem* line = new mapcontrol::WaypointLineItem(prevIcon, currIcon, wpColor, map); |
|
line->setParentItem(map); |
|
QGraphicsItemGroup* group = waypointLines.value(uas, NULL); |
|
if (group) |
|
{ |
|
group->addToGroup(line); |
|
group->setParentItem(map); |
|
} |
|
} |
|
prevIcon = currIcon; |
|
} |
|
} |
|
} |
|
|
|
|
|
//// ADAPTER / HELPER FUNCTIONS |
|
//float QGCMapWidget::metersToPixels(double meters) |
|
//{ |
|
// return meters/map->Projection()->GetGroundResolution(map->ZoomTotal(),coord.Lat()); |
|
//} |
|
|
|
//double QGCMapWidget::headingP1P2(internals::PointLatLng p1, internals::PointLatLng p2) |
|
//{ |
|
// double lat1 = p1.Lat() * deg_to_rad; |
|
// double lon1 = p2.Lng() * deg_to_rad; |
|
|
|
// double lat2 = p2.Lat() * deg_to_rad; |
|
// double lon2 = p2.Lng() * deg_to_rad; |
|
|
|
// double delta_lon = lon2 - lon1; |
|
|
|
// double y = sin(delta_lon) * cos(lat2); |
|
// double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_lon); |
|
// double heading = atan2(y, x) * rad_to_deg; |
|
|
|
// heading += 360; |
|
// while (heading < 0) bear += 360; |
|
// while (heading >= 360) bear -= 360; |
|
|
|
// return heading; |
|
//} |
|
|
|
//internals::PointLatLng QGCMapWidget::targetLatLon(internals::PointLatLng source, double heading, double dist) |
|
//{ |
|
// double lat1 = source.Lat() * deg_to_rad; |
|
// double lon1 = source.Lng() * deg_to_rad; |
|
|
|
// heading *= deg_to_rad; |
|
|
|
// double ad = dist / earth_mean_radius; |
|
|
|
// double lat2 = asin(sin(lat1) * cos(ad) + cos(lat1) * sin(ad) * cos(heading)); |
|
// double lon2 = lon1 + atan2(sin(bear) * sin(ad) * cos(lat1), cos(ad) - sin(lat1) * sin(lat2)); |
|
|
|
// return internals::PointLatLng(lat2 * rad_to_deg, lon2 * rad_to_deg); |
|
//}
|
|
|