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);
38 if (!kmlOrShpFile.isEmpty()) {
39 _corridorPolyline.loadKMLOrSHPFile(kmlOrShpFile);
40 _corridorPolyline.setDirty(
false);
47 QJsonObject saveObject;
49 _saveCommon(saveObject);
50 planItems.append(saveObject);
55 QJsonObject saveObject;
57 _saveCommon(saveObject);
61void CorridorScanComplexItem::_saveCommon(QJsonObject& saveObject)
69 saveObject[_jsonEntryPointKey] =
static_cast<int>(_entryPointLocation);
79 if (!_loadWorker(presetObject, 0,
errorString,
true )) {
85bool CorridorScanComplexItem::_loadWorker(
const QJsonObject& complexObject,
int sequenceNumber, QString&
errorString,
bool forPresets)
89 QList<JsonParsing::KeyValidateInfo> keyInfoList = {
94 { _jsonEntryPointKey, QJsonValue::Double,
true },
105 errorString = tr(
"%1 does not support loading this complex mission item type: %2:%3").arg(
qgcApp()->applicationName()).arg(itemType).arg(complexType);
133 _entryPointLocation =
static_cast<EntryPointLocation>(complexObject[_jsonEntryPointKey].toInt());
140 _recalcCameraShots();
153 return _corridorPolyline.
count() > 1;
164 const QList<QGeoCoordinate> vertices = _corridorPolyline.
coordinateList();
166 QList<QGeoCoordinate> translatedVertices;
167 translatedVertices.reserve(vertices.count());
168 for (
const QGeoCoordinate& vertex: vertices) {
169 translatedVertices.append(vertex.atDistanceAndAzimuth(distanceMeters, azimuthDegrees));
172 _corridorPolyline.
setPath(translatedVertices);
175int CorridorScanComplexItem::_calcTransectCount(
void)
const
177 double fullWidth = _corridorWidthFact.
rawValue().toDouble();
178 if (fullWidth <= 0.0) {
181 const double spacing = _calcTransectSpacing();
182 return spacing > 0.0 ? qMin(qCeil(fullWidth / spacing),
maxTransectCount) : 1;
185void CorridorScanComplexItem::_polylineDirtyChanged(
bool dirty)
194 int modeAsInt =
static_cast<int>(_entryPointLocation);
196 if (_calcTransectCount() < 2) {
212void CorridorScanComplexItem::_rebuildCorridorPolygon(
void)
214 if (_corridorPolyline.
count() < 2) {
219 double halfWidth = _corridorWidthFact.
rawValue().toDouble() / 2.0;
221 QList<QGeoCoordinate> firstSideVertices = _corridorPolyline.
offsetPolyline(halfWidth);
222 QList<QGeoCoordinate> secondSideVertices = _corridorPolyline.
offsetPolyline(-halfWidth);
226 QList<QGeoCoordinate> rgCoord;
227 for (
const QGeoCoordinate& vertex: firstSideVertices) {
228 rgCoord.append(vertex);
230 for (
int i=secondSideVertices.count() - 1; i >= 0; i--) {
231 rgCoord.append(secondSideVertices[i]);
236void CorridorScanComplexItem::_rebuildTransectsPhase1(
void)
249 double transectSpacing = _calcTransectSpacing();
250 double fullWidth = _corridorWidthFact.
rawValue().toDouble();
251 double halfWidth = fullWidth / 2.0;
252 int transectCount = _calcTransectCount();
253 double normalizedTransectPosition = transectSpacing / 2.0;
255 if (_corridorPolyline.
count() >= 2) {
258 for (
int i=0; i<transectCount; i++) {
260 double offsetDistance;
261 if (transectCount == 1) {
266 offsetDistance = halfWidth - normalizedTransectPosition;
270 QList<TransectStyleComplexItem::CoordInfo_t> transect;
271 QList<QGeoCoordinate> transectCoords = _corridorPolyline.
offsetPolyline(offsetDistance);
272 for (
int j=1; j<transectCoords.count() - 1; j++) {
274 transect.append(coordInfo);
277 transect.prepend(coordInfo);
279 transect.append(coordInfo);
283 QGeoCoordinate turnaroundCoord;
286 double azimuth = transectCoords[0].azimuthTo(transectCoords[1]);
288 turnaroundCoord.setAltitude(qQNaN());
290 transect.prepend(turnaroundCoordInfo);
292 azimuth = transectCoords.last().azimuthTo(transectCoords[transectCoords.count() - 2]);
294 turnaroundCoord.setAltitude(qQNaN());
296 transect.append(coordInfo);
300 qDebug() <<
"transect debug";
307 normalizedTransectPosition += transectSpacing;
316 bool reverseTransects =
false;
317 bool reverseVertices =
false;
318 switch (_entryPointLocation) {
320 reverseTransects =
false;
321 reverseVertices =
false;
324 reverseTransects =
true;
325 reverseVertices =
false;
328 reverseTransects =
false;
329 reverseVertices =
true;
332 reverseTransects =
true;
333 reverseVertices =
true;
336 if (reverseTransects) {
337 QList<QList<TransectStyleComplexItem::CoordInfo_t>> reversedTransects;
338 for (
const QList<TransectStyleComplexItem::CoordInfo_t>& transect:
_transects) {
339 reversedTransects.prepend(transect);
343 if (reverseVertices) {
345 QList<TransectStyleComplexItem::CoordInfo_t> reversedVertices;
347 reversedVertices.prepend(vertex);
354 reverseVertices =
false;
357 QList<TransectStyleComplexItem::CoordInfo_t> transectVertices =
_transects[i];
358 if (reverseVertices) {
359 reverseVertices =
false;
360 QList<TransectStyleComplexItem::CoordInfo_t> reversedVertices;
361 for (
int j=transectVertices.count()-1; j>=0; j--) {
362 reversedVertices.append(transectVertices[j]);
372 transectVertices = reversedVertices;
374 reverseVertices =
true;
381void CorridorScanComplexItem::_recalcCameraShots(
void)
391 _cameraShots = singleTransectImageCount * _calcTransectCount();
407double CorridorScanComplexItem::_calcTransectSpacing(
void)
const
410 if (transectSpacing <= 0) {
418 qCWarning(CorridorScanComplexItemLog) <<
"Corridor width" <<
corridorWidth <<
"is invalid, skipping transect count cap";
419 return transectSpacing;
426 return transectSpacing;
429void 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
Value after translation.
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.
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)
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.