10#include <QtCore/QJsonArray>
16 , _entryPointLocation (EntryPointDefaultOrder)
18 , _corridorWidthFact (settingsGroup, _metaDataMap[corridorWidthName])
20 _editorQml =
"qrc:/qml/QGroundControl/PlanView/CorridorScanEditor.qml";
23 if (_cameraCalc.isManualCamera() || !_cameraCalc.valueSetIsDistance()->rawValue().toBool()) {
24 _cameraCalc.distanceToSurface()->setRawValue(
SettingsManager::instance()->appSettings()->defaultMissionItemAltitude()->rawValue());
33 connect(&_corridorWidthFact, &
Fact::valueChanged,
this, &CorridorScanComplexItem::_rebuildCorridorPolygon);
41 _updateSpecifiesCoordinate();
43 if (!kmlOrShpFile.isEmpty()) {
44 _corridorPolyline.loadKMLOrSHPFile(kmlOrShpFile);
45 _corridorPolyline.setDirty(
false);
52 QJsonObject saveObject;
54 _saveCommon(saveObject);
55 planItems.append(saveObject);
60 QJsonObject saveObject;
62 _saveCommon(saveObject);
66void CorridorScanComplexItem::_saveCommon(QJsonObject& saveObject)
74 saveObject[_jsonEntryPointKey] =
static_cast<int>(_entryPointLocation);
84 if (!_loadWorker(presetObject, 0,
errorString,
true )) {
90bool CorridorScanComplexItem::_loadWorker(
const QJsonObject& complexObject,
int sequenceNumber, QString&
errorString,
bool forPresets)
94 QList<JsonParsing::KeyValidateInfo> keyInfoList = {
99 { _jsonEntryPointKey, QJsonValue::Double,
true },
110 errorString = tr(
"%1 does not support loading this complex mission item type: %2:%3").arg(
qgcApp()->applicationName()).arg(itemType).arg(complexType);
138 _entryPointLocation =
static_cast<EntryPointLocation>(complexObject[_jsonEntryPointKey].toInt());
145 _recalcCameraShots();
158 return _corridorPolyline.
count() > 1;
162void CorridorScanComplexItem::_updateSpecifiesCoordinate(
void)
165 if (newSpecifiesCoordinate != _specifiesCoordinate) {
166 _specifiesCoordinate = newSpecifiesCoordinate;
179 const QList<QGeoCoordinate> vertices = _corridorPolyline.
coordinateList();
181 QList<QGeoCoordinate> translatedVertices;
182 translatedVertices.reserve(vertices.count());
183 for (
const QGeoCoordinate& vertex: vertices) {
184 translatedVertices.append(vertex.atDistanceAndAzimuth(distanceMeters, azimuthDegrees));
187 _corridorPolyline.
setPath(translatedVertices);
190int CorridorScanComplexItem::_calcTransectCount(
void)
const
192 double fullWidth = _corridorWidthFact.
rawValue().toDouble();
193 if (fullWidth <= 0.0) {
196 const double spacing = _calcTransectSpacing();
197 return spacing > 0.0 ? qMin(qCeil(fullWidth / spacing),
maxTransectCount) : 1;
200void CorridorScanComplexItem::_polylineDirtyChanged(
bool dirty)
209 int modeAsInt =
static_cast<int>(_entryPointLocation);
211 if (_calcTransectCount() < 2) {
227void CorridorScanComplexItem::_rebuildCorridorPolygon(
void)
229 if (_corridorPolyline.
count() < 2) {
234 double halfWidth = _corridorWidthFact.
rawValue().toDouble() / 2.0;
236 QList<QGeoCoordinate> firstSideVertices = _corridorPolyline.
offsetPolyline(halfWidth);
237 QList<QGeoCoordinate> secondSideVertices = _corridorPolyline.
offsetPolyline(-halfWidth);
241 QList<QGeoCoordinate> rgCoord;
242 for (
const QGeoCoordinate& vertex: firstSideVertices) {
243 rgCoord.append(vertex);
245 for (
int i=secondSideVertices.count() - 1; i >= 0; i--) {
246 rgCoord.append(secondSideVertices[i]);
251void CorridorScanComplexItem::_rebuildTransectsPhase1(
void)
264 double transectSpacing = _calcTransectSpacing();
265 double fullWidth = _corridorWidthFact.
rawValue().toDouble();
266 double halfWidth = fullWidth / 2.0;
267 int transectCount = _calcTransectCount();
268 double normalizedTransectPosition = transectSpacing / 2.0;
270 if (_corridorPolyline.
count() >= 2) {
273 for (
int i=0; i<transectCount; i++) {
275 double offsetDistance;
276 if (transectCount == 1) {
281 offsetDistance = halfWidth - normalizedTransectPosition;
285 QList<TransectStyleComplexItem::CoordInfo_t> transect;
286 QList<QGeoCoordinate> transectCoords = _corridorPolyline.
offsetPolyline(offsetDistance);
287 for (
int j=1; j<transectCoords.count() - 1; j++) {
289 transect.append(coordInfo);
292 transect.prepend(coordInfo);
294 transect.append(coordInfo);
298 QGeoCoordinate turnaroundCoord;
301 double azimuth = transectCoords[0].azimuthTo(transectCoords[1]);
303 turnaroundCoord.setAltitude(qQNaN());
305 transect.prepend(turnaroundCoordInfo);
307 azimuth = transectCoords.last().azimuthTo(transectCoords[transectCoords.count() - 2]);
309 turnaroundCoord.setAltitude(qQNaN());
311 transect.append(coordInfo);
315 qDebug() <<
"transect debug";
322 normalizedTransectPosition += transectSpacing;
331 bool reverseTransects =
false;
332 bool reverseVertices =
false;
333 switch (_entryPointLocation) {
335 reverseTransects =
false;
336 reverseVertices =
false;
339 reverseTransects =
true;
340 reverseVertices =
false;
343 reverseTransects =
false;
344 reverseVertices =
true;
347 reverseTransects =
true;
348 reverseVertices =
true;
351 if (reverseTransects) {
352 QList<QList<TransectStyleComplexItem::CoordInfo_t>> reversedTransects;
353 for (
const QList<TransectStyleComplexItem::CoordInfo_t>& transect:
_transects) {
354 reversedTransects.prepend(transect);
358 if (reverseVertices) {
360 QList<TransectStyleComplexItem::CoordInfo_t> reversedVertices;
362 reversedVertices.prepend(vertex);
369 reverseVertices =
false;
372 QList<TransectStyleComplexItem::CoordInfo_t> transectVertices =
_transects[i];
373 if (reverseVertices) {
374 reverseVertices =
false;
375 QList<TransectStyleComplexItem::CoordInfo_t> reversedVertices;
376 for (
int j=transectVertices.count()-1; j>=0; j--) {
377 reversedVertices.append(transectVertices[j]);
387 transectVertices = reversedVertices;
389 reverseVertices =
true;
396void CorridorScanComplexItem::_recalcCameraShots(
void)
406 _cameraShots = singleTransectImageCount * _calcTransectCount();
422double CorridorScanComplexItem::_calcTransectSpacing(
void)
const
425 if (transectSpacing <= 0) {
433 qCWarning(CorridorScanComplexItemLog) <<
"Corridor width" <<
corridorWidth <<
"is invalid, skipping transect count cap";
434 return transectSpacing;
441 return transectSpacing;
444void CorridorScanComplexItem::_updateWizardMode(
void)
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Fact * adjustedFootprintSide(void)
Fact * adjustedFootprintFrontal(void)
void _savePresetJson(const QString &name, QJsonObject &presetObject)
static constexpr const char * jsonComplexItemTypeKey
This mission item attribute specifies the type of the complex item.
QJsonObject _loadPresetJson(const QString &name)
bool load(const QJsonObject &complexObject, int sequenceNumber, QString &errorString) final
void savePreset(const QString &name)
void setCoordinate(const QGeoCoordinate &coordinate) final
double timeBetweenShots(void) final
Fact * corridorWidth(void)
void save(QJsonArray &planItems) final
static constexpr const char * corridorWidthName
bool specifiesCoordinate(void) const final
void loadPreset(const QString &name)
Q_INVOKABLE void rotateEntryPoint(void)
@ EntryPointStartOppositeEndSameSide
@ EntryPointStartOppositeEndOppositeSide
@ EntryPointStartSameEndOppositeSide
static constexpr const char * jsonComplexItemTypeValue
ReadyForSaveState readyForSaveState(void) const final
void setRawValue(const QVariant &value)
QVariant rawValue() const
void valueChanged(const QVariant &value)
This signal is only meant for use by the QT property system. It should not be connected to by client ...
Master controller for mission, fence, rally.
Q_INVOKABLE void clear(void)
Q_INVOKABLE void appendVertices(const QVariantList &varCoords)
QList< QGeoCoordinate > offsetPolyline(double distance)
void dirtyChanged(bool dirty)
static constexpr const char * jsonPolylineKey
void traceModeChanged(bool traceMode)
void setPath(const QList< QGeoCoordinate > &path)
void saveToJson(QJsonObject &json)
QList< QGeoCoordinate > coordinateList(void) const
Returns the path in a list of QGeoCoordinate's format.
void isValidChanged(void)
bool loadFromJson(const QJsonObject &json, bool required, QString &errorString)
double length(void) const
Returns the length of the polyline in meters.
void countChanged(int count)
bool traceMode(void) const
Provides access to group of settings.
static SettingsManager * instance()
QObject * _loadedMissionItemsParent
Parent for all items in _loadedMissionItems for simpler delete.
QList< QList< CoordInfo_t > > _transects
QList< MissionItem * > _loadedMissionItems
Mission items loaded from plan file.
void _recalcComplexDistance(void)
void setSequenceNumber(int sequenceNumber) final
QGeoCoordinate coordinate(void) const final
bool _hasTurnaround(void) const
int sequenceNumber(void) const final
static constexpr int maxTransectCount
Maximum number of transects allowed; spacing is raised to enforce this limit.
ReadyForSaveState readyForSaveState(void) const override
bool _load(const QJsonObject &complexObject, bool forPresets, QString &errorString)
QGCMapPolygon _surveyAreaPolygon
Fact * turnAroundDistance(void)
SettingsFact _cameraTriggerInTurnAroundFact
double triggerDistance(void) const
void cameraShotsChanged(void)
void _save(QJsonObject &saveObject)
void _rebuildTransects(void)
bool dirty(void) const final
SettingsFact _turnAroundDistanceFact
void setDirty(bool dirty) final
QGeoCoordinate _entryCoordinate
@ CoordTypeTurnaround
Turnaround extension waypoint.
@ CoordTypeSurveyExit
Waypoint at exit edge of survey polygon.
@ CoordTypeInterior
Interior waypoint for flight path only (example: interior corridor point)
@ CoordTypeSurveyEntry
Waypoint at entry edge of survey polygon.
static constexpr const char * jsonTypeComplexItemValue
Item type is Complex Item.
void setWizardMode(bool wizardMode)
void specifiesCoordinateChanged(void)
static constexpr const char * jsonTypeKey
Json file attribute which specifies the item type.
double azimuth(void) const
bool validateKeys(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)
Validates that all required keys are present and that listed keys have the expected type.
constexpr const char * jsonVersionKey
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.