QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
CameraCalc.cc
Go to the documentation of this file.
1#include "CameraCalc.h"
2#include "JsonParsing.h"
3#include "Vehicle.h"
4#include "CameraMetaData.h"
6
7#include <QtQml/QQmlEngine>
8
9CameraCalc::CameraCalc(PlanMasterController* masterController, const QString& settingsGroup, QObject* parent)
10 : CameraSpec (settingsGroup, parent)
11 , _distanceMode (masterController->missionController()->globalAltitudeFrameDefault())
12 , _knownCameraList (masterController->controllerVehicle()->staticCameraList())
13 , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraCalc.FactMetaData.json"), this))
14 , _cameraNameFact (settingsGroup, _metaDataMap[cameraNameName])
15 , _valueSetIsDistanceFact (settingsGroup, _metaDataMap[valueSetIsDistanceName])
16 , _distanceToSurfaceFact (settingsGroup, _metaDataMap[distanceToSurfaceName])
17 , _imageDensityFact (settingsGroup, _metaDataMap[imageDensityName])
18 , _frontalOverlapFact (settingsGroup, _metaDataMap[frontalOverlapName])
19 , _sideOverlapFact (settingsGroup, _metaDataMap[sideOverlapName])
20 , _adjustedFootprintSideFact (settingsGroup, _metaDataMap[adjustedFootprintSideName])
21 , _adjustedFootprintFrontalFact (settingsGroup, _metaDataMap[adjustedFootprintFrontalName])
22{
23 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
24
25 connect(&_valueSetIsDistanceFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
26 connect(&_distanceToSurfaceFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
27 connect(&_imageDensityFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
28 connect(&_frontalOverlapFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
29 connect(&_sideOverlapFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
30 connect(&_adjustedFootprintSideFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
31 connect(&_adjustedFootprintFrontalFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
32 connect(&_cameraNameFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
33 connect(this, &CameraCalc::distanceModeChanged, this, &CameraCalc::_setDirty);
34
35 connect(&_cameraNameFact, &Fact::valueChanged, this, &CameraCalc::_cameraNameChanged);
36 connect(&_cameraNameFact, &Fact::valueChanged, this, &CameraCalc::isManualCameraChanged);
37 connect(&_cameraNameFact, &Fact::valueChanged, this, &CameraCalc::isCustomCameraChanged);
38
39 connect(&_distanceToSurfaceFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
40 connect(&_imageDensityFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
41 connect(&_frontalOverlapFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
42 connect(&_sideOverlapFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
43 connect(sensorWidth(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
44 connect(sensorHeight(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
45 connect(imageWidth(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
46 connect(imageHeight(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
47 connect(focalLength(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
48 connect(landscape(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
49
50 // Build the brand list from known cameras
51 _cameraBrandList.append(xlatManualCameraName());
52 _cameraBrandList.append(xlatCustomCameraName());
53 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
54 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
55 if (!_cameraBrandList.contains(cameraMetaData->brand)) {
56 _cameraBrandList.append(cameraMetaData->brand);
57 }
58 }
59
60 _cameraNameChanged();
61 _setBrandModelFromCanonicalName(_cameraNameFact.rawValue().toString());
62
63 setDirty(false);
64}
65
66void CameraCalc::_cameraNameChanged(void)
67{
68 if (_disableRecalc) {
69 return;
70 }
71
72 QString cameraName = _cameraNameFact.rawValue().toString();
73
74 if (isManualCamera() || isCustomCamera()) {
77 if (isManualCamera() && !valueSetIsDistance()->rawValue().toBool()) {
79 }
80 } else {
81 // Look for known camera
82 CameraMetaData* knownCameraMetaData = nullptr;
83 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
84 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
85 if (cameraName == cameraMetaData->canonicalName) {
86 knownCameraMetaData = cameraMetaData;
87 break;
88 }
89 }
90
91 if (!knownCameraMetaData) {
92 // Lookup failed. Force to custom as fallback.
93 // This will cause another camera changed signal which will recurse back into this routine
94 _cameraNameFact.setRawValue(canonicalCustomCameraName());
95 return;
96 }
97
98 _disableRecalc = true;
99
100 sensorWidth()->setRawValue (knownCameraMetaData->sensorWidth);
101 sensorHeight()->setRawValue (knownCameraMetaData->sensorHeight);
102 imageWidth()->setRawValue (knownCameraMetaData->imageWidth);
103 imageHeight()->setRawValue (knownCameraMetaData->imageHeight);
104 focalLength()->setRawValue (knownCameraMetaData->focalLength);
105 landscape()->setRawValue (knownCameraMetaData->landscape);
106 fixedOrientation()->setRawValue (knownCameraMetaData->fixedOrientation);
107 minTriggerInterval()->setRawValue (knownCameraMetaData->minTriggerInterval);
108
109 _disableRecalc = false;
110 }
111
112 _recalcTriggerDistance();
114 // Manual grids support absolute alts whereas nothing else does. Make sure we are not left in absolute
116 }
117}
118
119void CameraCalc::_recalcTriggerDistance(void)
120{
121 if (_disableRecalc || isManualCamera()) {
122 return;
123 }
124
125 _disableRecalc = true;
126
127 double focalLength = this->focalLength()->rawValue().toDouble();
128 double sensorWidth = this->sensorWidth()->rawValue().toDouble();
129 double sensorHeight = this->sensorHeight()->rawValue().toDouble();
130 double imageWidth = this->imageWidth()->rawValue().toDouble();
131 double imageHeight = this->imageHeight()->rawValue().toDouble();
132 double imageDensity = _imageDensityFact.rawValue().toDouble();
133
134 if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || imageDensity <= 0) {
135 return;
136 }
137
138 if (_valueSetIsDistanceFact.rawValue().toBool()) {
139 _imageDensityFact.setRawValue((_distanceToSurfaceFact.rawValue().toDouble() * sensorWidth * 100.0) / (imageWidth * focalLength));
140 } else {
141 _distanceToSurfaceFact.setRawValue((imageWidth * _imageDensityFact.rawValue().toDouble() * focalLength) / (sensorWidth * 100.0));
142 }
143
144 imageDensity = _imageDensityFact.rawValue().toDouble();
145
146 if (landscape()->rawValue().toBool()) {
147 _imageFootprintSide = (imageWidth * imageDensity) / 100.0;
148 _imageFootprintFrontal = (imageHeight * imageDensity) / 100.0;
149 } else {
150 _imageFootprintSide = (imageHeight * imageDensity) / 100.0;
151 _imageFootprintFrontal = (imageWidth * imageDensity) / 100.0;
152 }
153 _adjustedFootprintSideFact.setRawValue (_imageFootprintSide * ((100.0 - _sideOverlapFact.rawValue().toDouble()) / 100.0));
154 _adjustedFootprintFrontalFact.setRawValue (_imageFootprintFrontal * ((100.0 - _frontalOverlapFact.rawValue().toDouble()) / 100.0));
155
156 emit imageFootprintSideChanged (_imageFootprintSide);
157 emit imageFootprintFrontalChanged (_imageFootprintFrontal);
158
159 _disableRecalc = false;
160}
161
162void CameraCalc::save(QJsonObject& json) const
163{
165 json[adjustedFootprintSideName] = _adjustedFootprintSideFact.rawValue().toDouble();
166 json[adjustedFootprintFrontalName] = _adjustedFootprintFrontalFact.rawValue().toDouble();
167 json[distanceToSurfaceName] = _distanceToSurfaceFact.rawValue().toDouble();
168 json[distanceModeName] = _distanceMode;
169 json[cameraNameName] = _cameraNameFact.rawValue().toString();
170
171 if (!isManualCamera()) {
172 CameraSpec::save(json);
173 json[valueSetIsDistanceName] = _valueSetIsDistanceFact.rawValue().toBool();
174 json[imageDensityName] = _imageDensityFact.rawValue().toDouble();
175 json[frontalOverlapName] = _frontalOverlapFact.rawValue().toDouble();
176 json[sideOverlapName] = _sideOverlapFact.rawValue().toDouble();
177 }
178}
179
180bool CameraCalc::load(const QJsonObject& originalJson, bool deprecatedFollowTerrain, QString& errorString, bool forPresets)
181{
182 QJsonObject json = originalJson;
183
184 int version = 0;
185 if (json.contains(JsonParsing::jsonVersionKey)) {
186 version = json[JsonParsing::jsonVersionKey].toInt();
187 }
188
189 if (version == 0) {
190 // Version 0->1 differences:
191 // - JsonParsing::jsonVersionKey not stored
192 // - _jsonCameraSpecTypeKeyDeprecated is only in v0 files and stores CameraSpecType. V2 files store same info in cameraNameName.
193 // - _jsonCameraNameKey only set if CameraSpecKnown
194 int cameraSpec = json[_jsonCameraSpecTypeKeyDeprecated].toInt(CameraSpecNone);
195 if (cameraSpec == CameraSpecCustom) {
197 } else if (cameraSpec == CameraSpecNone) {
199 }
200 json.remove(_jsonCameraSpecTypeKeyDeprecated);
201 version = 1;
202 }
203 if (version == 1) {
204 // Version 1->2 differences:
205 // - _jsonDistanceToSurfaceRelativeKeyDeprecated changed to distanceMode
206 // - deprecatedFollowTerrain value was loaded from upper level callers and represents AltitudeFrameCalcAboveTerrain. AltitudeFrameTerrain was not supported yet.
207 if (deprecatedFollowTerrain) {
209 } else {
210 json[distanceModeName] = json[_jsonDistanceToSurfaceRelativeKeyDeprecated].toBool() ? QGroundControlQmlGlobal::AltitudeFrameRelative : QGroundControlQmlGlobal::AltitudeFrameAbsolute;
211 }
212 json.remove(_jsonDistanceToSurfaceRelativeKeyDeprecated);
213 version = 2;
214 }
215 if (version != 2) {
216 errorString = tr("CameraCalc section version %1 not supported").arg(version);
217 return false;
218 }
219
220 QList<JsonParsing::KeyValidateInfo> keyInfoList1 = {
221 { cameraNameName, QJsonValue::String, true },
222 { adjustedFootprintSideName, QJsonValue::Double, true },
223 { adjustedFootprintFrontalName, QJsonValue::Double, true },
224 { distanceToSurfaceName, QJsonValue::Double, true },
225 { distanceModeName, QJsonValue::Double, true },
226 };
227 if (!JsonParsing::validateKeys(json, keyInfoList1, errorString)) {
228 return false;
229 }
230
231 _disableRecalc = !forPresets;
232
233 // We have to clean up camera names. Older builds incorrectly used translated the camera names in the persisted plan file.
234 // Newer builds use a canonical english camera name in plan files.
235 QString canonicalCameraName = _validCanonicalCameraName(json[cameraNameName].toString());
236 _cameraNameFact.setRawValue(canonicalCameraName);
237
239
240 _adjustedFootprintSideFact.setRawValue (json[adjustedFootprintSideName].toDouble());
241 _adjustedFootprintFrontalFact.setRawValue (json[adjustedFootprintFrontalName].toDouble());
242 _distanceToSurfaceFact.setRawValue (json[distanceToSurfaceName].toDouble());
243
244 if (!isManualCamera()) {
245 QList<JsonParsing::KeyValidateInfo> keyInfoList2 = {
246 { valueSetIsDistanceName, QJsonValue::Bool, true },
247 { imageDensityName, QJsonValue::Double, true },
248 { frontalOverlapName, QJsonValue::Double, true },
249 { sideOverlapName, QJsonValue::Double, true },
250 };
251 if (!JsonParsing::validateKeys(json, keyInfoList2, errorString)) {
252 _disableRecalc = false;
253 return false;
254 }
255
256 _valueSetIsDistanceFact.setRawValue (json[valueSetIsDistanceName].toBool());
257 _frontalOverlapFact.setRawValue (json[frontalOverlapName].toDouble());
258 _sideOverlapFact.setRawValue (json[sideOverlapName].toDouble());
259 _imageDensityFact.setRawValue (json[imageDensityName].toDouble());
260
261 if (!CameraSpec::load(json, errorString)) {
262 _disableRecalc = false;
263 return false;
264 }
265 }
266
267 _disableRecalc = false;
268
269 _setBrandModelFromCanonicalName(canonicalCameraName);
270
271 return true;
272}
273
275{
276 // This string should NOT be translated
277 return "Custom Camera";
278}
279
281{
282 // This string should NOT be translated
283 return "Manual (no camera specs)";
284}
285
287{
288 return tr("Custom Camera");
289}
290
292{
293 return tr("Manual (no camera specs)");
294}
295
297{
298 if (altFrame != _distanceMode) {
299 _distanceMode = altFrame;
300 emit distanceModeChanged(_distanceMode);
301 }
302}
303
304void CameraCalc::_setDirty(void)
305{
306 setDirty(true);
307}
308
309void CameraCalc::setCameraBrand(const QString& cameraBrand)
310{
311 // Note that cameraBrand can also be manual or custom camera
312
313 if (cameraBrand != _cameraBrand) {
314 QString newCameraName = cameraBrand;
315
316 _cameraBrand = cameraBrand;
317 _cameraModel.clear();
318
319 if (_cameraBrand != xlatManualCameraName() && _cameraBrand != xlatCustomCameraName()) {
320 CameraMetaData* firstCameraMetaData = nullptr;
321 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
322 firstCameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
323 if (firstCameraMetaData->brand == _cameraBrand) {
324 break;
325 }
326 }
327 newCameraName = firstCameraMetaData->canonicalName;
328 _cameraModel = firstCameraMetaData->model;
329 }
330 emit cameraBrandChanged();
331 emit cameraModelChanged();
332
333 _rebuildCameraModelList();
334
335 _cameraNameFact.setRawValue(newCameraName);
336 }
337}
338
339void CameraCalc::setCameraModel(const QString& cameraModel)
340{
341 if (cameraModel != _cameraModel) {
342 _cameraModel = cameraModel;
343 emit cameraModelChanged();
344
345 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
346 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
347 if (cameraMetaData->brand == _cameraBrand && cameraMetaData->model == _cameraModel) {
348 _cameraNameFact.setRawValue(cameraMetaData->canonicalName);
349 break;
350 }
351 }
352
353 }
354}
355
356void CameraCalc::_setBrandModelFromCanonicalName(const QString& cameraName)
357{
358 _cameraBrand = cameraName;
359 _cameraModel.clear();
360 _cameraModelList.clear();
361
362 if (cameraName != canonicalManualCameraName() && cameraName != canonicalCustomCameraName()) {
363 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
364 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
365 if (cameraMetaData->canonicalName == cameraName) {
366 _cameraBrand = cameraMetaData->brand;
367 _cameraModel = cameraMetaData->model;
368 break;
369 }
370 }
371 }
372 emit cameraBrandChanged();
373 emit cameraModelChanged();
374
375 _rebuildCameraModelList();
376}
377
378void CameraCalc::_rebuildCameraModelList(void)
379{
380 _cameraModelList.clear();
381
382 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
383 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
384 if (cameraMetaData->brand == _cameraBrand) {
385 _cameraModelList.append(cameraMetaData->model);
386 }
387 }
388
390}
391
392void CameraCalc::_setCameraNameFromV3TransectLoad(const QString& cameraName)
393{
394 // We don't recalc here since the rest of the camera values are already loaded from the json
395 _disableRecalc = true;
396 QString canonicalCameraName = _validCanonicalCameraName(cameraName);
397 _cameraNameFact.setRawValue(cameraName);
398 _disableRecalc = true;
399
400 _setBrandModelFromCanonicalName(canonicalCameraName);
401}
402
403QString CameraCalc::_validCanonicalCameraName(const QString& cameraName)
404{
405 QString canonicalCameraName = cameraName;
406
407 if (canonicalCameraName != canonicalCustomCameraName() && canonicalCameraName != canonicalManualCameraName()) {
408 if (cameraName == xlatManualCameraName()) {
409 canonicalCameraName = canonicalManualCameraName();
410 } else if (cameraName == xlatCustomCameraName()) {
411 canonicalCameraName = canonicalCustomCameraName();
412 } else {
413 // Look for known camera
414 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
415 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
416 if (cameraName == cameraMetaData->canonicalName || cameraName == cameraMetaData->deprecatedTranslatedName) {
417 return cameraMetaData->canonicalName;
418 }
419 }
420
421 canonicalCameraName = canonicalCustomCameraName();
422 }
423 }
424
425 return canonicalCameraName;
426}
QString errorString
static constexpr const char * distanceModeName
Definition CameraCalc.h:86
static QString xlatManualCameraName(void)
void save(QJsonObject &json) const
void _setCameraNameFromV3TransectLoad(const QString &cameraName)
static constexpr const char * sideOverlapName
Definition CameraCalc.h:89
void cameraModelListChanged(void)
void setDistanceMode(QGroundControlQmlGlobal::AltitudeFrame altFrame)
Fact * imageDensity(void)
Definition CameraCalc.h:54
CameraCalc(PlanMasterController *masterController, const QString &settingsGroup, QObject *parent=nullptr)
Definition CameraCalc.cc:9
static constexpr const char * frontalOverlapName
Definition CameraCalc.h:88
static constexpr const char * adjustedFootprintFrontalName
Definition CameraCalc.h:90
bool isCustomCamera(void) const
Definition CameraCalc.h:69
bool isManualCamera(void) const
Definition CameraCalc.h:68
void imageFootprintSideChanged(double imageFootprintSide)
static constexpr const char * adjustedFootprintSideName
Definition CameraCalc.h:91
static constexpr const char * valueSetIsDistanceName
Definition CameraCalc.h:84
void cameraBrandChanged(void)
static constexpr const char * cameraNameName
Definition CameraCalc.h:83
void isCustomCameraChanged(void)
void distanceModeChanged(int altFrame)
bool load(const QJsonObject &json, bool deprecatedFollowTerrain, QString &errorString, bool forPresets)
QGroundControlQmlGlobal::AltitudeFrame distanceMode(void) const
Definition CameraCalc.h:72
static QString canonicalCustomCameraName(void)
void setCameraBrand(const QString &cameraBrand)
static constexpr const char * distanceToSurfaceName
Definition CameraCalc.h:85
static constexpr const char * imageDensityName
Definition CameraCalc.h:87
void isManualCameraChanged(void)
Fact * valueSetIsDistance(void)
Definition CameraCalc.h:52
void cameraModelChanged(void)
void imageFootprintFrontalChanged(double imageFootprintFrontal)
static QString xlatCustomCameraName(void)
< Size of image size frontal in meters
void setCameraModel(const QString &cameraModel)
static QString canonicalManualCameraName(void)
Set of meta data which describes a camera available on the vehicle.
const double focalLength
Focal length in millimeters.
const QString brand
Camera brand. Used for grouping.
const double imageWidth
Image size in pixels.
const QString deprecatedTranslatedName
const double imageHeight
Image size in pixels.
const bool fixedOrientation
true: camera is in fixed orientation
const QString model
Camerar model.
const double minTriggerInterval
Minimum time in seconds between each photo taken, 0 for not specified.
const QString canonicalName
Canonical name saved in plan files. Not translated.
const bool landscape
true: camera is in landscape orientation
const double sensorWidth
Sensor size in millimeters.
const double sensorHeight
Sensor size in millimeters.
SettingsFact * minTriggerInterval(void)
Definition CameraSpec.h:31
SettingsFact * imageHeight(void)
Definition CameraSpec.h:27
SettingsFact * sensorWidth(void)
Definition CameraSpec.h:24
SettingsFact * fixedOrientation(void)
Definition CameraSpec.h:30
void save(QJsonObject &json) const
Definition CameraSpec.cc:44
SettingsFact * focalLength(void)
Definition CameraSpec.h:28
void setDirty(bool dirty)
Definition CameraSpec.cc:36
SettingsFact * sensorHeight(void)
Definition CameraSpec.h:25
SettingsFact * imageWidth(void)
Definition CameraSpec.h:26
bool load(const QJsonObject &json, QString &errorString)
Definition CameraSpec.cc:56
SettingsFact * landscape(void)
Definition CameraSpec.h:29
Holds the meta data associated with a Fact.
void rawValueChanged(const QVariant &value)
void setRawValue(const QVariant &value)
Definition Fact.cc:128
QVariant rawValue() const
Value after translation.
Definition Fact.h:85
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.
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
Definition JsonParsing.h:12