QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCMapPolygon.cc
Go to the documentation of this file.
1#include "QGCMapPolygon.h"
2#include "QGCGeo.h"
3#include "JsonHelper.h"
4#include "JsonParsing.h"
5#include "QGCQGeoCoordinate.h"
6#include "QGCApplication.h"
8#include "ShapeFileHelper.h"
9#include "KMLDomDocument.h"
10
11#include <QtCore/QLineF>
12#include <QMetaMethod>
13
14QGC_LOGGING_CATEGORY(QGCMapPolygonLog, "QMLControls.QGCMapPolygon")
15
17 : QObject (parent)
18 , _dirty (false)
19 , _centerDrag (false)
20 , _ignoreCenterUpdates (false)
21 , _interactive (false)
22{
23 _init();
24}
25
26QGCMapPolygon::QGCMapPolygon(const QGCMapPolygon& other, QObject* parent)
27 : QObject (parent)
28 , _dirty (false)
29 , _centerDrag (false)
30 , _ignoreCenterUpdates (false)
31 , _interactive (false)
32{
33 *this = other;
34
35 _init();
36}
37
39{
40 qgcApp()->removeCompressedSignal(QMetaMethod::fromSignal(&QGCMapPolygon::pathChanged));
41}
42
43void QGCMapPolygon::_init(void)
44{
45 connect(&_polygonModel, &QmlObjectListModel::dirtyChanged, this, &QGCMapPolygon::_polygonModelDirtyChanged);
46 connect(&_polygonModel, &QmlObjectListModel::countChanged, this, &QGCMapPolygon::_polygonModelCountChanged);
47 connect(&_polygonModel, &QmlObjectListModel::modelReset, this, [this]() {
48 emit pathChanged();
49 emit centerChanged(_center);
50 });
51
52 connect(this, &QGCMapPolygon::pathChanged, this, &QGCMapPolygon::_updateCenter);
55
56}
57
59{
60 clear();
61
62 QVariantList vertices = other.path();
63 QList<QGeoCoordinate> rgCoord;
64 for (const QVariant& vertexVar: vertices) {
65 rgCoord.append(vertexVar.value<QGeoCoordinate>());
66 }
67 appendVertices(rgCoord);
68
69 setDirty(true);
70
71 return *this;
72}
73
74void QGCMapPolygon::clear(void)
75{
76 // Bug workaround, see below
77 while (_polygonPath.count() > 1) {
78 _polygonPath.takeLast();
79 }
80 emit pathChanged();
81
82 // Although this code should remove the polygon from the map it doesn't. There appears
83 // to be a bug in QGCMapPolygon which causes it to not be redrawn if the list is empty. So
84 // we work around it by using the code above to remove all but the last point which in turn
85 // will cause the polygon to go away.
86 _polygonPath.clear();
87
88 _polygonModel.clearAndDeleteContents();
89
90 emit cleared();
91
92 setDirty(true);
93}
94
95void QGCMapPolygon::adjustVertex(int vertexIndex, const QGeoCoordinate coordinate)
96{
97 _polygonPath[vertexIndex] = QVariant::fromValue(coordinate);
98 _polygonModel.value<QGCQGeoCoordinate*>(vertexIndex)->setCoordinate(coordinate);
99 if (!_centerDrag) {
100 // When dragging center we don't signal path changed until all vertices are updated
101 if (!_deferredPathChanged) {
102 // Only update the path once per event loop, to prevent lag-spikes
103 _deferredPathChanged = true;
104 QTimer::singleShot(0, this, [this]() {
105 emit pathChanged();
106 _deferredPathChanged = false;
107 });
108 }
109 }
110 setDirty(true);
111}
112
114{
115 if (_dirty != dirty) {
116 _dirty = dirty;
117 if (!dirty) {
118 _polygonModel.setDirty(false);
119 }
120 emit dirtyChanged(dirty);
121 }
122}
123
124QGeoCoordinate QGCMapPolygon::_coordFromPointF(const QPointF& point) const
125{
126 QGeoCoordinate coord;
127
128 if (_polygonPath.count() > 0) {
129 QGeoCoordinate tangentOrigin = _polygonPath[0].value<QGeoCoordinate>();
130 QGCGeo::convertNedToGeo(-point.y(), point.x(), 0, tangentOrigin, coord);
131 }
132
133 return coord;
134}
135
136QPointF QGCMapPolygon::_pointFFromCoord(const QGeoCoordinate& coordinate) const
137{
138 if (_polygonPath.count() > 0) {
139 double y, x, down;
140 QGeoCoordinate tangentOrigin = _polygonPath[0].value<QGeoCoordinate>();
141
142 QGCGeo::convertGeoToNed(coordinate, tangentOrigin, y, x, down);
143 return QPointF(x, -y);
144 }
145
146 return QPointF();
147}
148
149QPolygonF QGCMapPolygon::_toPolygonF(void) const
150{
151 QPolygonF polygon;
152
153 if (_polygonPath.count() > 2) {
154 for (int i=0; i<_polygonPath.count(); i++) {
155 polygon.append(_pointFFromCoord(_polygonPath[i].value<QGeoCoordinate>()));
156 }
157 }
158
159 return polygon;
160}
161
162bool QGCMapPolygon::containsCoordinate(const QGeoCoordinate& coordinate) const
163{
164 if (_polygonPath.count() > 2) {
165 return _toPolygonF().containsPoint(_pointFFromCoord(coordinate), Qt::OddEvenFill);
166 } else {
167 return false;
168 }
169}
170
171void QGCMapPolygon::setPath(const QList<QGeoCoordinate>& path)
172{
173 _polygonPath.clear();
174 _polygonModel.clearAndDeleteContents();
175 for(const QGeoCoordinate& coord: path) {
176 _polygonPath.append(QVariant::fromValue(coord));
177 _polygonModel.append(new QGCQGeoCoordinate(coord, this));
178 }
179
180 setDirty(true);
181 emit pathChanged();
182}
183
184void QGCMapPolygon::setPath(const QVariantList& path)
185{
186 _polygonPath = path;
187
188 _polygonModel.clearAndDeleteContents();
189 for (int i=0; i<_polygonPath.count(); i++) {
190 _polygonModel.append(new QGCQGeoCoordinate(_polygonPath[i].value<QGeoCoordinate>(), this));
191 }
192
193 setDirty(true);
194 emit pathChanged();
195}
196
197void QGCMapPolygon::saveToJson(QJsonObject& json)
198{
199 QJsonValue jsonValue;
200
201 JsonHelper::saveGeoCoordinateArray(_polygonPath, false /* writeAltitude*/, jsonValue);
202 json.insert(jsonPolygonKey, jsonValue);
203 setDirty(false);
204}
205
206bool QGCMapPolygon::loadFromJson(const QJsonObject& json, bool required, QString& errorString)
207{
208 errorString.clear();
209 clear();
210
211 if (required) {
213 return false;
214 }
215 } else if (!json.contains(jsonPolygonKey)) {
216 return true;
217 }
218
219 if (!JsonHelper::loadGeoCoordinateArray(json[jsonPolygonKey], false /* altitudeRequired */, _polygonPath, errorString)) {
220 return false;
221 }
222
223 for (int i=0; i<_polygonPath.count(); i++) {
224 _polygonModel.append(new QGCQGeoCoordinate(_polygonPath[i].value<QGeoCoordinate>(), this));
225 }
226
227 setDirty(false);
228 emit pathChanged();
229
230 return true;
231}
232
233QList<QGeoCoordinate> QGCMapPolygon::coordinateList(void) const
234{
235 QList<QGeoCoordinate> coords;
236
237 for (int i=0; i<_polygonPath.count(); i++) {
238 coords.append(_polygonPath[i].value<QGeoCoordinate>());
239 }
240
241 return coords;
242}
243
245{
246 int nextIndex = vertexIndex + 1;
247 if (nextIndex > _polygonPath.length() - 1) {
248 nextIndex = 0;
249 }
250
251 QGeoCoordinate firstVertex = _polygonPath[vertexIndex].value<QGeoCoordinate>();
252 QGeoCoordinate nextVertex = _polygonPath[nextIndex].value<QGeoCoordinate>();
253
254 double distance = firstVertex.distanceTo(nextVertex);
255 double azimuth = firstVertex.azimuthTo(nextVertex);
256 QGeoCoordinate newVertex = firstVertex.atDistanceAndAzimuth(distance / 2, azimuth);
257
258 if (nextIndex == 0) {
259 appendVertex(newVertex);
260 } else {
261 _polygonModel.insert(nextIndex, new QGCQGeoCoordinate(newVertex, this));
262 _polygonPath.insert(nextIndex, QVariant::fromValue(newVertex));
263 emit pathChanged();
264 if (0 <= _selectedVertexIndex && vertexIndex < _selectedVertexIndex) {
265 selectVertex(_selectedVertexIndex+1);
266 }
267 }
268}
269
270void QGCMapPolygon::appendVertex(const QGeoCoordinate& coordinate)
271{
272 _polygonPath.append(QVariant::fromValue(coordinate));
273 _polygonModel.append(new QGCQGeoCoordinate(coordinate, this));
274 if (!_deferredPathChanged) {
275 // Only update the path once per event loop, to prevent lag-spikes
276 _deferredPathChanged = true;
277 QTimer::singleShot(0, this, [this]() {
278 emit pathChanged();
279 _deferredPathChanged = false;
280 });
281 }
282}
283
284void QGCMapPolygon::appendVertices(const QList<QGeoCoordinate>& coordinates)
285{
286 QList<QObject*> objects;
287
288 beginReset();
289 for (const QGeoCoordinate& coordinate: coordinates) {
290 objects.append(new QGCQGeoCoordinate(coordinate, this));
291 _polygonPath.append(QVariant::fromValue(coordinate));
292 }
293 _polygonModel.append(objects);
294 endReset();
295
296 emit pathChanged();
297}
298
299void QGCMapPolygon::appendVertices(const QVariantList& varCoords)
300{
301 QList<QGeoCoordinate> rgCoords;
302 for (const QVariant& varCoord: varCoords) {
303 rgCoords.append(varCoord.value<QGeoCoordinate>());
304 }
305 appendVertices(rgCoords);
306}
307
308void QGCMapPolygon::_polygonModelDirtyChanged(bool dirty)
309{
310 if (dirty) {
311 setDirty(true);
312 }
313}
314
315void QGCMapPolygon::removeVertex(int vertexIndex)
316{
317 if (vertexIndex < 0 || vertexIndex >= _polygonPath.length()) {
318 qCWarning(QGCMapPolygonLog) << "Call to removePolygonCoordinate with bad vertexIndex:count" << vertexIndex << _polygonPath.length();
319 return;
320 }
321
322 if (_polygonPath.length() <= 3) {
323 // Don't allow the user to trash the polygon
324 return;
325 }
326
327 QObject* coordObj = _polygonModel.removeAt(vertexIndex);
328 coordObj->deleteLater();
329 if(vertexIndex == _selectedVertexIndex) {
330 selectVertex(-1);
331 } else if (vertexIndex < _selectedVertexIndex) {
332 selectVertex(_selectedVertexIndex - 1);
333 } // else do nothing - keep current selected vertex
334
335 _polygonPath.removeAt(vertexIndex);
336 emit pathChanged();
337}
338
339void QGCMapPolygon::_polygonModelCountChanged(int count)
340{
341 emit countChanged(count);
342}
343
344void QGCMapPolygon::_updateCenter(void)
345{
346 if (!_ignoreCenterUpdates) {
347 QGeoCoordinate center;
348
349 if (_polygonPath.count() > 2) {
350 QPointF centroid(0, 0);
351 QPolygonF polygonF = _toPolygonF();
352 for (int i=0; i<polygonF.count(); i++) {
353 centroid += polygonF[i];
354 }
355 center = _coordFromPointF(QPointF(centroid.x() / polygonF.count(), centroid.y() / polygonF.count()));
356 }
357 if (_center != center) {
358 _center = center;
359 emit centerChanged(center);
360 }
361 }
362}
363
364void QGCMapPolygon::setCenter(QGeoCoordinate newCenter)
365{
366 if (newCenter != _center) {
367 _ignoreCenterUpdates = true;
368
369 // Adjust polygon vertices to new center
370 double distance = _center.distanceTo(newCenter);
371 double azimuth = _center.azimuthTo(newCenter);
372
373 for (int i=0; i<count(); i++) {
374 QGeoCoordinate oldVertex = _polygonPath[i].value<QGeoCoordinate>();
375 QGeoCoordinate newVertex = oldVertex.atDistanceAndAzimuth(distance, azimuth);
376 adjustVertex(i, newVertex);
377 }
378
379 if (_centerDrag) {
380 // When center dragging, signals from adjustVertext are not sent. So we need to signal here when all adjusting is complete.
381 if (!_deferredPathChanged) {
382 // Only update the path once per event loop, to prevent lag-spikes
383 _deferredPathChanged = true;
384 QTimer::singleShot(0, this, [this]() {
385 emit pathChanged();
386 _deferredPathChanged = false;
387 });
388 }
389 }
390
391 _ignoreCenterUpdates = false;
392
393 _center = newCenter;
394 if (!_deferredPathChanged) {
395 // Only update the center once per event loop, to prevent lag-spikes
396 _deferredPathChanged = true;
397 QTimer::singleShot(0, this, [this, newCenter]() {
398 emit centerChanged(newCenter);
399 _deferredPathChanged = false;
400 });
401 }
402 }
403}
404
405void QGCMapPolygon::setCenterDrag(bool centerDrag)
406{
407 if (centerDrag != _centerDrag) {
408 _centerDrag = centerDrag;
410 }
411}
412
413void QGCMapPolygon::setInteractive(bool interactive)
414{
415 if (_interactive != interactive) {
416 _interactive = interactive;
418 }
419}
420
421QGeoCoordinate QGCMapPolygon::vertexCoordinate(int vertex) const
422{
423 if (vertex >= 0 && vertex < _polygonPath.count()) {
424 return _polygonPath[vertex].value<QGeoCoordinate>();
425 } else {
426 qCWarning(QGCMapPolygonLog) << "QGCMapPolygon::vertexCoordinate bad vertex requested:count" << vertex << _polygonPath.count();
427 return QGeoCoordinate();
428 }
429}
430
431QList<QPointF> QGCMapPolygon::nedPolygon(void) const
432{
433 QList<QPointF> nedPolygon;
434
435 if (count() > 0) {
436 QGeoCoordinate tangentOrigin = vertexCoordinate(0);
437
438 for (int i=0; i<_polygonModel.count(); i++) {
439 double y, x, down;
440 QGeoCoordinate vertex = vertexCoordinate(i);
441 if (i == 0) {
442 // This avoids a nan calculation that comes out of convertGeoToNed
443 x = y = 0;
444 } else {
445 QGCGeo::convertGeoToNed(vertex, tangentOrigin, y, x, down);
446 }
447 nedPolygon += QPointF(x, y);
448 }
449 }
450
451 return nedPolygon;
452}
453
454
455void QGCMapPolygon::offset(double distance)
456{
457 QList<QGeoCoordinate> rgNewPolygon;
458
459 // I'm sure there is some beautiful famous algorithm to do this, but here is a brute force method
460
461 if (count() > 2) {
462 // Convert the polygon to NED
463 QList<QPointF> rgNedVertices = nedPolygon();
464
465 // Walk the edges, offsetting by the specified distance
466 QList<QLineF> rgOffsetEdges;
467 for (int i=0; i<rgNedVertices.count(); i++) {
468 int lastIndex = i == rgNedVertices.count() - 1 ? 0 : i + 1;
469 QLineF offsetEdge;
470 QLineF originalEdge(rgNedVertices[i], rgNedVertices[lastIndex]);
471
472 QLineF workerLine = originalEdge;
473 workerLine.setLength(distance);
474 workerLine.setAngle(workerLine.angle() - 90.0);
475 offsetEdge.setP1(workerLine.p2());
476
477 workerLine.setPoints(originalEdge.p2(), originalEdge.p1());
478 workerLine.setLength(distance);
479 workerLine.setAngle(workerLine.angle() + 90.0);
480 offsetEdge.setP2(workerLine.p2());
481
482 rgOffsetEdges.append(offsetEdge);
483 }
484
485 // Intersect the offset edges to generate new vertices
486 QPointF newVertex;
487 QGeoCoordinate tangentOrigin = vertexCoordinate(0);
488 for (int i=0; i<rgOffsetEdges.count(); i++) {
489 int prevIndex = i == 0 ? rgOffsetEdges.count() - 1 : i - 1;
490 auto intersect = rgOffsetEdges[prevIndex].intersects(rgOffsetEdges[i], &newVertex);
491 if (intersect == QLineF::NoIntersection) {
492 // FIXME: Better error handling?
493 qCWarning(QGCMapPolygonLog, "Intersection failed");
494 return;
495 }
496 QGeoCoordinate coord;
497 QGCGeo::convertNedToGeo(newVertex.y(), newVertex.x(), 0, tangentOrigin, coord);
498 rgNewPolygon.append(coord);
499 }
500 }
501
502 // Update internals
503 beginReset();
504 clear();
505 appendVertices(rgNewPolygon);
506 endReset();
507}
508
509bool QGCMapPolygon::loadKMLOrSHPFile(const QString& file)
510{
511 QString errorString;
512 QList<QList<QGeoCoordinate>> polygons;
513 if (!ShapeFileHelper::loadPolygonsFromFile(file, polygons, errorString)) {
514 qgcApp()->showAppMessage(errorString);
515 return false;
516 }
517 if (polygons.isEmpty()) {
518 qgcApp()->showAppMessage(tr("No polygons found in file"));
519 return false;
520 }
521 const QList<QGeoCoordinate>& rgCoords = polygons.first();
522
523 beginReset();
524 clear();
525 appendVertices(rgCoords);
526 endReset();
527
528 return true;
529}
530
531double QGCMapPolygon::area(void) const
532{
533 // https://www.mathopenref.com/coordpolygonarea2.html
534
535 if (_polygonPath.count() < 3) {
536 return 0;
537 }
538
539 double coveredArea = 0.0;
540 QList<QPointF> nedVertices = nedPolygon();
541 for (int i=0; i<nedVertices.count(); i++) {
542 if (i != 0) {
543 coveredArea += nedVertices[i - 1].x() * nedVertices[i].y() - nedVertices[i].x() * nedVertices[i -1].y();
544 } else {
545 coveredArea += nedVertices.last().x() * nedVertices[i].y() - nedVertices[i].x() * nedVertices.last().y();
546 }
547 }
548 return 0.5 * fabs(coveredArea);
549}
550
552{
553 if (_polygonPath.count() <= 2) {
554 return;
555 }
556
557 double sum = 0;
558 for (int i=0; i<_polygonPath.count(); i++) {
559 QGeoCoordinate coord1 = _polygonPath[i].value<QGeoCoordinate>();
560 QGeoCoordinate coord2 = (i == _polygonPath.count() - 1) ? _polygonPath[0].value<QGeoCoordinate>() : _polygonPath[i+1].value<QGeoCoordinate>();
561
562 sum += (coord2.longitude() - coord1.longitude()) * (coord2.latitude() + coord1.latitude());
563 }
564
565 if (sum < 0.0) {
566 // Winding is counter-clockwise and needs reversal
567
568 QList<QGeoCoordinate> rgReversed;
569 for (const QVariant& varCoord: _polygonPath) {
570 rgReversed.prepend(varCoord.value<QGeoCoordinate>());
571 }
572
573 beginReset();
574 clear();
575 appendVertices(rgReversed);
576 endReset();
577 }
578}
579
581{
582 _polygonModel.beginResetModel();
583}
584
586{
587 _polygonModel.endResetModel();
588}
589
591{
592#if 0
593 <Polygon id="ID">
594 <!-- specific to Polygon -->
595 <extrude>0</extrude> <!-- boolean -->
596 <tessellate>0</tessellate> <!-- boolean -->
597 <altitudeMode>clampToGround</altitudeMode>
598 <!-- kml:altitudeModeEnum: clampToGround, relativeToGround, or absolute -->
599 <!-- or, substitute gx:altitudeMode: clampToSeaFloor, relativeToSeaFloor -->
600 <outerBoundaryIs>
601 <LinearRing>
602 <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
603 </LinearRing>
604 </outerBoundaryIs>
605 <innerBoundaryIs>
606 <LinearRing>
607 <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
608 </LinearRing>
609 </innerBoundaryIs>
610 </Polygon>
611#endif
612
613 QDomElement polygonElement = domDocument.createElement("Polygon");
614
615 domDocument.addTextElement(polygonElement, "altitudeMode", "clampToGround");
616
617 QDomElement outerBoundaryIsElement = domDocument.createElement("outerBoundaryIs");
618 QDomElement linearRingElement = domDocument.createElement("LinearRing");
619
620 outerBoundaryIsElement.appendChild(linearRingElement);
621 polygonElement.appendChild(outerBoundaryIsElement);
622
623 QString coordString;
624 for (const QVariant& varCoord : _polygonPath) {
625 coordString += QStringLiteral("%1\n").arg(domDocument.kmlCoordString(varCoord.value<QGeoCoordinate>()));
626 }
627 coordString += QStringLiteral("%1\n").arg(domDocument.kmlCoordString(_polygonPath.first().value<QGeoCoordinate>()));
628 domDocument.addTextElement(linearRingElement, "coordinates", coordString);
629
630 return polygonElement;
631}
632
633void QGCMapPolygon::setTraceMode(bool traceMode)
634{
635 if (traceMode != _traceMode) {
636 _traceMode = traceMode;
638 }
639}
640
641void QGCMapPolygon::setShowAltColor(bool showAltColor){
642 if (showAltColor != _showAltColor) {
643 _showAltColor = showAltColor;
645 }
646}
647
649{
650 if(index == _selectedVertexIndex) return; // do nothing
651
652 if(-1 <= index && index < count()) {
653 _selectedVertexIndex = index;
654 } else {
655 qCWarning(QGCMapPolygonLog) << QString("QGCMapPolygon: Selected vertex index (%1) is out of bounds! "
656 "Polygon vertices indexes range is [%2..%3].").arg(index).arg(0).arg(count()-1);
657 _selectedVertexIndex = -1; // deselect vertex
658 }
659
660 emit selectedVertexChanged(_selectedVertexIndex);
661}
#define qgcApp()
QString errorString
Geographic coordinate conversion utilities using GeographicLib.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Used to convert a Plan to a KML document.
static QString kmlCoordString(const QGeoCoordinate &coord)
void addTextElement(QDomElement &parentElement, const QString &name, const QString &value)
void endResetModel()
Depth-counted endResetModel — only the outermost call has effect.
void beginResetModel()
Depth-counted beginResetModel — only the outermost call has effect.
void dirtyChanged(bool dirty)
int count READ count NOTIFY countChanged(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) bool dirty() const
QModelIndex index(int row, int column=0, const QModelIndex &parent=QModelIndex()) const override
void cleared(void)
void interactiveChanged(bool interactive)
~QGCMapPolygon() override
void dirtyChanged(bool dirty)
void selectVertex(int index)
bool loadKMLOrSHPFile(const QString &file)
bool isEmptyChanged(void)
void adjustVertex(int vertexIndex, const QGeoCoordinate coordinate)
double area(void) const
Returns the area of the polygon in meters squared.
void setCenter(QGeoCoordinate newCenter)
void splitPolygonSegment(int vertexIndex)
Splits the segment comprised of vertextIndex -> vertexIndex + 1.
bool traceMode(void) const
QGeoCoordinate center(void) const
void setTraceMode(bool traceMode)
QList< QGeoCoordinate > coordinateList(void) const
Returns the path in a list of QGeoCoordinate's format.
void offset(double distance)
Offsets the current polygon edges by the specified distance in meters.
void setDirty(bool dirty)
bool centerDrag(void) const
void selectedVertexChanged(int index)
void traceModeChanged(bool traceMode)
bool showAltColor(void) const
bool dirty(void) const
bool containsCoordinate(const QGeoCoordinate &coordinate) const
Returns true if the specified coordinate is within the polygon.
QGeoCoordinate vertexCoordinate(int vertex) const
Returns the QGeoCoordinate for the vertex specified.
void removeVertex(int vertexIndex)
void setPath(const QList< QGeoCoordinate > &path)
QVariantList path(void) const
void showAltColorChanged(bool showAltColor)
const QGCMapPolygon & operator=(const QGCMapPolygon &other)
void verifyClockwiseWinding(void)
Adjust polygon winding order to be clockwise (if needed)
void setInteractive(bool interactive)
void beginReset(void)
bool interactive(void) const
void saveToJson(QJsonObject &json)
QList< QPointF > nedPolygon(void) const
Convert polygon to NED and return (D is ignored)
void endReset(void)
QDomElement kmlPolygonElement(KMLDomDocument &domDocument)
static constexpr const char * jsonPolygonKey
void centerChanged(QGeoCoordinate center)
bool isValidChanged(void)
void setShowAltColor(bool showAltColor)
void appendVertex(const QGeoCoordinate &coordinate)
void pathChanged(void)
int count(void) const
bool loadFromJson(const QJsonObject &json, bool required, QString &errorString)
void appendVertices(const QVariantList &varCoords)
int count READ count NOTIFY countChanged(QVariantList path READ path NOTIFY pathChanged) 1(double area READ area NOTIFY pathChanged) 1(QmlObjectListModel *pathModel READ qmlPathModel CONSTANT) 1(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) 1(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) 1(bool centerDrag READ centerDrag WRITE setCenterDrag NOTIFY centerDragChanged) 1(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged) 1(bool isValid READ isValid NOTIFY isValidChanged) 1(bool empty READ empty NOTIFY isEmptyChanged) 1(bool traceMode READ traceMode WRITE setTraceMode NOTIFY traceModeChanged) 1(bool showAltColor READ showAltColor WRITE setShowAltColor NOTIFY showAltColorChanged) 1(int selectedVertex READ selectedVertex WRITE selectVertex NOTIFY selectedVertexChanged) 1 void clear(void)
void setCenterDrag(bool centerDrag)
QGCMapPolygon(QObject *parent=nullptr)
void centerDragChanged(bool centerDrag)
This is a QGeoCoordinate within a QObject such that it can be used on a QmlObjectListModel.
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
T value(int index) const
void setDirty(bool dirty) override final
QObject * removeAt(int index)
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
void insert(int index, QObject *object)
void saveGeoCoordinateArray(const QVariantList &rgVarPoints, bool writeAltitude, QJsonValue &jsonValue)
Saves a list of QGeoCoordinates to a json array.
bool loadGeoCoordinateArray(const QJsonValue &jsonValue, bool altitudeRequired, QVariantList &rgVarPoints, QString &errorString)
returned error string if load failure
bool validateRequiredKeys(const QJsonObject &jsonObject, const QStringList &keys, QString &errorString)
Validates that all listed keys are present in the object.
void convertGeoToNed(const QGeoCoordinate &coord, const QGeoCoordinate &origin, double &x, double &y, double &z)
Definition QGCGeo.cc:34
void convertNedToGeo(double x, double y, double z, const QGeoCoordinate &origin, QGeoCoordinate &coord)
Definition QGCGeo.cc:56