@ -23,11 +23,15 @@ This file is part of the QGROUNDCONTROL project
@@ -23,11 +23,15 @@ This file is part of the QGROUNDCONTROL project
# include "ComplexMissionItem.h"
# include "JsonHelper.h"
# include "MissionController.h"
# include "QGCGeo.h"
const char * ComplexMissionItem : : _jsonVersionKey = " version " ;
const char * ComplexMissionItem : : _jsonTypeKey = " type " ;
const char * ComplexMissionItem : : _jsonPolygonKey = " polygon " ;
const char * ComplexMissionItem : : _jsonIdKey = " id " ;
const char * ComplexMissionItem : : _jsonVersionKey = " version " ;
const char * ComplexMissionItem : : _jsonTypeKey = " type " ;
const char * ComplexMissionItem : : _jsonPolygonKey = " polygon " ;
const char * ComplexMissionItem : : _jsonIdKey = " id " ;
const char * ComplexMissionItem : : _jsonGridAltitudeKey = " gridAltitude " ;
const char * ComplexMissionItem : : _jsonGridAngleKey = " gridAngle " ;
const char * ComplexMissionItem : : _jsonGridSpacingKey = " gridSpacing " ;
const char * ComplexMissionItem : : _complexType = " survey " ;
@ -35,12 +39,15 @@ ComplexMissionItem::ComplexMissionItem(Vehicle* vehicle, QObject* parent)
@@ -35,12 +39,15 @@ ComplexMissionItem::ComplexMissionItem(Vehicle* vehicle, QObject* parent)
: VisualMissionItem ( vehicle , parent )
, _sequenceNumber ( 0 )
, _dirty ( false )
, _gridAltitudeFact ( 0 , " Altitude: " , FactMetaData : : valueTypeDouble )
, _gridAngleFact ( 0 , " Grid angle: " , FactMetaData : : valueTypeDouble )
, _gridSpacingFact ( 0 , " Grid spacing: " , FactMetaData : : valueTypeDouble )
{
MissionItem missionItem ;
_gridAltitudeFact . setRawValue ( 25 ) ;
_gridSpacingFact . setRawValue ( 5 ) ;
// FIXME: Bogus entries for testing
_missionItems + = new MissionItem ( this ) ;
_missionItems + = new MissionItem ( this ) ;
connect ( & _gridSpacingFact , & Fact : : valueChanged , this , & ComplexMissionItem : : _generateGrid ) ;
connect ( & _gridAngleFact , & Fact : : valueChanged , this , & ComplexMissionItem : : _generateGrid ) ;
}
ComplexMissionItem : : ComplexMissionItem ( const ComplexMissionItem & other , QObject * parent )
@ -61,9 +68,12 @@ void ComplexMissionItem::clearPolygon(void)
@@ -61,9 +68,12 @@ void ComplexMissionItem::clearPolygon(void)
// Although this code should remove the polygon from the map it doesn't. There appears
// to be a bug in MapPolygon which causes it to not be redrawn if the list is empty. So
// we work around it by using the code above to remove all bu the last point which in turn
// we work around it by using the code above to remove all but the last point which in turn
// will cause the polygon to go away.
_polygonPath . clear ( ) ;
_clearGrid ( ) ;
emit specifiesCoordinateChanged ( ) ;
}
@ -79,11 +89,18 @@ void ComplexMissionItem::addPolygonCoordinate(const QGeoCoordinate coordinate)
@@ -79,11 +89,18 @@ void ComplexMissionItem::addPolygonCoordinate(const QGeoCoordinate coordinate)
emit specifiesCoordinateChanged ( ) ;
}
_setExitCoordinate ( coordinate ) ;
_generateGrid ( ) ;
}
int ComplexMissionItem : : lastSequenceNumber ( void ) const
{
return _sequenceNumber + _missionItems . count ( ) - 1 ;
int lastSeq = _sequenceNumber ;
if ( _gridPoints . count ( ) ) {
lastSeq + = _gridPoints . count ( ) - 1 ;
}
return lastSeq ;
}
void ComplexMissionItem : : setCoordinate ( const QGeoCoordinate & coordinate )
@ -91,26 +108,26 @@ void ComplexMissionItem::setCoordinate(const QGeoCoordinate& coordinate)
@@ -91,26 +108,26 @@ void ComplexMissionItem::setCoordinate(const QGeoCoordinate& coordinate)
if ( _coordinate ! = coordinate ) {
_coordinate = coordinate ;
emit coordinateChanged ( _coordinate ) ;
_missionItems [ 0 ] - > setCoordinate ( coordinate ) ;
// FIXME: Hack
_setExitCoordinate ( coordinate ) ;
}
}
void ComplexMissionItem : : setDirty ( bool dirty )
{
if ( _dirty ! = dirty ) {
_dirty = dirty ;
emit dirtyChanged ( _dirty ) ;
}
if ( _dirty ! = dirty ) {
_dirty = dirty ;
emit dirtyChanged ( _dirty ) ;
}
}
void ComplexMissionItem : : save ( QJsonObject & saveObject ) const
{
saveObject [ _jsonVersionKey ] = 1 ;
saveObject [ _jsonTypeKey ] = _complexType ;
saveObject [ _jsonIdKey ] = sequenceNumber ( ) ;
saveObject [ _jsonVersionKey ] = 1 ;
saveObject [ _jsonTypeKey ] = _complexType ;
saveObject [ _jsonIdKey ] = sequenceNumber ( ) ;
saveObject [ _jsonGridAltitudeKey ] = _gridAltitudeFact . rawValue ( ) . toDouble ( ) ;
saveObject [ _jsonGridAngleKey ] = _gridAngleFact . rawValue ( ) . toDouble ( ) ;
saveObject [ _jsonGridSpacingKey ] = _gridSpacingFact . rawValue ( ) . toDouble ( ) ;
// Polygon shape
@ -125,42 +142,21 @@ void ComplexMissionItem::save(QJsonObject& saveObject) const
@@ -125,42 +142,21 @@ void ComplexMissionItem::save(QJsonObject& saveObject) const
}
saveObject [ _jsonPolygonKey ] = polygonArray ;
// Base mission items
QJsonArray simpleItems ;
for ( int i = 0 ; i < _missionItems . count ( ) ; i + + ) {
MissionItem * missionItem = _missionItems [ i ] ;
QJsonObject jsonObject ;
missionItem - > save ( jsonObject ) ;
simpleItems . append ( jsonObject ) ;
}
saveObject [ MissionController : : jsonSimpleItemsKey ] = simpleItems ;
}
void ComplexMissionItem : : setSequenceNumber ( int sequenceNumber )
{
if ( _sequenceNumber ! = sequenceNumber ) {
_sequenceNumber = sequenceNumber ;
// Update internal mission items to new numbering
for ( int i = 0 ; i < _missionItems . count ( ) ; i + + ) {
_missionItems [ i ] - > setSequenceNumber ( sequenceNumber + + ) ;
}
emit sequenceNumberChanged ( sequenceNumber ) ;
emit lastSequenceNumberChanged ( lastSequenceNumber ( ) ) ;
}
}
void ComplexMissionItem : : _clear ( void )
{
// Clear old settings
for ( int i = 0 ; i < _missionItems . count ( ) ; i + + ) {
_missionItems [ i ] - > deleteLater ( ) ;
}
_missionItems . clear ( ) ;
_polygonPath . clear ( ) ;
clearPolygon ( ) ;
_clearGrid ( ) ;
}
@ -170,7 +166,7 @@ bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorSt
@@ -170,7 +166,7 @@ bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorSt
// Validate requires keys
QStringList requiredKeys ;
requiredKeys < < _jsonVersionKey < < _jsonTypeKey < < _jsonIdKey < < _jsonPolygonKey < < MissionController : : jsonSimpleItems Key;
requiredKeys < < _jsonVersionKey < < _jsonTypeKey < < _jsonIdKey < < _jsonPolygonKey < < _jsonGridAltitudeKey < < _jsonGridAngleKey < < _jsonGridSpacing Key;
if ( ! JsonHelper : : validateRequiredKeys ( complexObject , requiredKeys , errorString ) ) {
_clear ( ) ;
return false ;
@ -179,8 +175,8 @@ bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorSt
@@ -179,8 +175,8 @@ bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorSt
// Validate types
QStringList keyList ;
QList < QJsonValue : : Type > typeList ;
keyList < < _jsonVersionKey < < _jsonTypeKey < < _jsonIdKey < < _jsonPolygonKey < < MissionController : : jsonSimpleItems Key;
typeList < < QJsonValue : : Double < < QJsonValue : : String < < QJsonValue : : Double < < QJsonValue : : Array < < QJsonValue : : Array ;
keyList < < _jsonVersionKey < < _jsonTypeKey < < _jsonIdKey < < _jsonPolygonKey < < _jsonGridAltitudeKey < < _jsonGridAngleKey < < _jsonGridSpacing Key;
typeList < < QJsonValue : : Double < < QJsonValue : : String < < QJsonValue : : Double < < QJsonValue : : Array < < QJsonValue : : Double < < QJsonValue : : Double < < QJsonValue : : Double ;
if ( ! JsonHelper : : validateKeyTypes ( complexObject , keyList , typeList , errorString ) ) {
_clear ( ) ;
return false ;
@ -200,6 +196,9 @@ bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorSt
@@ -200,6 +196,9 @@ bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorSt
}
setSequenceNumber ( complexObject [ _jsonIdKey ] . toInt ( ) ) ;
_gridAltitudeFact . setRawValue ( complexObject [ _jsonGridAltitudeKey ] . toDouble ( ) ) ;
_gridAngleFact . setRawValue ( complexObject [ _jsonGridAngleKey ] . toDouble ( ) ) ;
_gridSpacingFact . setRawValue ( complexObject [ _jsonGridSpacingKey ] . toDouble ( ) ) ;
// Polygon shape
QJsonArray polygonArray ( complexObject [ _jsonPolygonKey ] . toArray ( ) ) ;
@ -214,51 +213,149 @@ bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorSt
@@ -214,51 +213,149 @@ bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorSt
_polygonPath < < QVariant : : fromValue ( pointCoord ) ;
}
// Internal mission items
QJsonArray itemArray ( complexObject [ MissionController : : jsonSimpleItemsKey ] . toArray ( ) ) ;
for ( int i = 0 ; i < itemArray . count ( ) ; i + + ) {
const QJsonValue & itemValue = itemArray [ i ] ;
_generateGrid ( ) ;
if ( ! itemValue . isObject ( ) ) {
errorString = QStringLiteral ( " Mission item is not an object " ) ;
_clear ( ) ;
return false ;
}
return true ;
}
MissionItem * item = new MissionItem ( _vehicle , this ) ;
if ( item - > load ( itemValue . toObject ( ) , errorString ) ) {
_missionItems . append ( item ) ;
} else {
_clear ( ) ;
return false ;
}
void ComplexMissionItem : : _setExitCoordinate ( const QGeoCoordinate & coordinate )
{
if ( _exitCoordinate ! = coordinate ) {
_exitCoordinate = coordinate ;
emit exitCoordinateChanged ( coordinate ) ;
}
}
int itemCount = _missionItems . count ( ) ;
if ( itemCount > 0 ) {
_coordinate = _missionItems [ 0 ] - > coordinate ( ) ;
_exitCoordinate = _missionItems [ itemCount - 1 ] - > coordinate ( ) ;
bool ComplexMissionItem : : specifiesCoordinate ( void ) const
{
return _polygonPath . count ( ) > 2 ;
}
void ComplexMissionItem : : _clearGrid ( void )
{
// Bug workaround
while ( _gridPoints . count ( ) > 1 ) {
_gridPoints . takeLast ( ) ;
}
emit gridPointsChanged ( ) ;
_gridPoints . clear ( ) ;
emit gridPointsChanged ( ) ;
qDebug ( ) < < coordinate ( ) < < exitCoordinate ( ) < < _missionItems [ 0 ] - > coordinate ( ) < < _missionItems [ 1 ] - > coordinate ( ) ;
}
void ComplexMissionItem : : _generateGrid ( void )
{
if ( _polygonPath . count ( ) < 3 ) {
_clearGrid ( ) ;
return ;
}
_gridPoints . clear ( ) ;
QList < Point_t > polygonPoints ;
QList < Point_t > gridPoints ;
// Convert polygon to NED
qDebug ( ) < < " Convert polygon " ;
QGeoCoordinate tangentOrigin = _polygonPath [ 0 ] . value < QGeoCoordinate > ( ) ;
for ( int i = 0 ; i < _polygonPath . count ( ) ; i + + ) {
double x , y , z ;
convertGeoToNed ( _polygonPath [ i ] . value < QGeoCoordinate > ( ) , tangentOrigin , & x , & y , & z ) ;
polygonPoints + = Point_t ( x , y ) ;
qDebug ( ) < < _polygonPath [ i ] . value < QGeoCoordinate > ( ) < < polygonPoints . last ( ) . x < < polygonPoints . last ( ) . y ;
}
// Generate grid
_gridGenerator ( polygonPoints , gridPoints ) ;
// Convert to Geo and set altitude
for ( int i = 0 ; i < gridPoints . count ( ) ; i + + ) {
Point_t & point = gridPoints [ i ] ;
QGeoCoordinate geoCoord ;
convertNedToGeo ( point . x , point . y , 0 , tangentOrigin , & geoCoord ) ;
_gridPoints + = QVariant : : fromValue ( geoCoord ) ;
}
emit gridPointsChanged ( ) ;
emit lastSequenceNumberChanged ( lastSequenceNumber ( ) ) ;
if ( _gridPoints . count ( ) ) {
setCoordinate ( _gridPoints . first ( ) . value < QGeoCoordinate > ( ) ) ;
_setExitCoordinate ( _gridPoints . last ( ) . value < QGeoCoordinate > ( ) ) ;
}
return true ;
}
void ComplexMissionItem : : _setExitCoordinate ( const QGeoCoordinate & coordinate )
void ComplexMissionItem : : _gridGenerator ( const QList < Point_t > & polygonPoints , QList < Point_t > & gridPoints )
{
if ( _exitCoordinate ! = coordinate ) {
_exitCoordinate = coordinate ;
emit exitCoordinateChanged ( coordinate ) ;
// FIXME: Hack implementataion
int itemCount = _missionItems . count ( ) ;
if ( itemCount > 0 ) {
_missionItems [ itemCount - 1 ] - > setCoordinate ( coordinate ) ;
gridPoints . clear ( ) ;
// Convert polygon to bounding square
Point_t upperLeft = polygonPoints [ 0 ] ;
Point_t lowerRight = polygonPoints [ 0 ] ;
for ( int i = 1 ; i < polygonPoints . count ( ) ; i + + ) {
const Point_t & point = polygonPoints [ i ] ;
upperLeft . x = std : : min ( upperLeft . x , point . x ) ;
upperLeft . y = std : : max ( upperLeft . y , point . y ) ;
lowerRight . x = std : : max ( lowerRight . x , point . x ) ;
lowerRight . y = std : : min ( lowerRight . y , point . y ) ;
}
qDebug ( ) < < " bounding rect " < < upperLeft . x < < upperLeft . y < < lowerRight . x < < lowerRight . y ;
// Create simplistic set of parallel lines
QList < QPair < Point_t , Point_t > > lineList ;
double x = upperLeft . x ;
double gridSpacing = _gridSpacingFact . rawValue ( ) . toDouble ( ) ;
while ( x < lowerRight . x ) {
double yTop = upperLeft . y ;
double yBottom = lowerRight . y ;
lineList + = qMakePair ( Point_t ( x , yTop ) , Point_t ( x , yBottom ) ) ;
qDebug ( ) < < " line " < < lineList . last ( ) . first . x < < lineList . last ( ) . first . y < < lineList . last ( ) . second . x < < lineList . last ( ) . second . y ;
x + = gridSpacing ;
}
// Turn into a path
for ( int i = 0 ; i < lineList . count ( ) ; i + + ) {
const QPair < Point_t , Point_t > points = lineList [ i ] ;
if ( i & 1 ) {
gridPoints < < points . second < < points . first ;
} else {
gridPoints < < points . first < < points . second ;
}
}
}
bool ComplexMissionItem : : specifiesCoordinate ( void ) const
QmlObjectListModel * ComplexMissionItem : : getMissionItems ( void ) const
{
return _polygonPath . count ( ) > 2 ;
QmlObjectListModel * pMissionItems = new QmlObjectListModel ;
int seqNum = _sequenceNumber ;
for ( int i = 0 ; i < _gridPoints . count ( ) ; i + + ) {
QGeoCoordinate coord = _gridPoints [ i ] . value < QGeoCoordinate > ( ) ;
double altitude = _gridAltitudeFact . rawValue ( ) . toDouble ( ) ;
MissionItem * item = new MissionItem ( seqNum + + , // sequence number
MAV_CMD_NAV_WAYPOINT , // MAV_CMD
MAV_FRAME_GLOBAL_RELATIVE_ALT , // MAV_FRAME
0.0 , 0.0 , 0.0 , 0.0 , // param 1-4
coord . latitude ( ) ,
coord . longitude ( ) ,
altitude ,
true , // autoContinue
false , // isCurrentItem
pMissionItems ) ; // parent - allow delete on pMissionItems to delete everthing
pMissionItems - > append ( item ) ;
}
return pMissionItems ;
}