QGCMapPolygon.cc 9.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "QGCMapPolygon.h"
#include "QGCGeo.h"
12
#include "JsonHelper.h"
13
#include "QGCQGeoCoordinate.h"
14 15 16

#include <QGeoRectangle>
#include <QDebug>
17 18
#include <QJsonArray>

19
const char* QGCMapPolygon::jsonPolygonKey = "polygon";
20

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
QGCMapPolygon::QGCMapPolygon(QObject* parent)
    : QObject               (parent)
    , _dirty                (false)
    , _centerDrag           (false)
    , _ignoreCenterUpdates  (false)
    , _interactive          (false)
{
    _init();
}

QGCMapPolygon::QGCMapPolygon(const QGCMapPolygon& other, QObject* parent)
    : QObject               (parent)
    , _dirty                (false)
    , _centerDrag           (false)
    , _ignoreCenterUpdates  (false)
    , _interactive          (false)
{
    *this = other;

    _init();
}

void QGCMapPolygon::_init(void)
44
{
45 46
    connect(&_polygonModel, &QmlObjectListModel::dirtyChanged, this, &QGCMapPolygon::_polygonModelDirtyChanged);
    connect(&_polygonModel, &QmlObjectListModel::countChanged, this, &QGCMapPolygon::_polygonModelCountChanged);
47
    connect(&_polygonModel, &QmlObjectListModel::countChanged, this, &QGCMapPolygon::_updateCenter);
48 49
}

50 51 52 53 54 55 56 57 58 59 60 61 62 63
const QGCMapPolygon& QGCMapPolygon::operator=(const QGCMapPolygon& other)
{
    clear();

    QVariantList vertices = other.path();
    for (int i=0; i<vertices.count(); i++) {
        appendVertex(vertices[i].value<QGeoCoordinate>());
    }

    setDirty(true);

    return *this;
}

64 65 66 67 68 69 70 71 72 73 74 75 76 77
void QGCMapPolygon::clear(void)
{
    // Bug workaround, see below
    while (_polygonPath.count() > 1) {
        _polygonPath.takeLast();
    }
    emit pathChanged();

    // Although this code should remove the polygon from the map it doesn't. There appears
    // to be a bug in QGCMapPolygon 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 but the last point which in turn
    // will cause the polygon to go away.
    _polygonPath.clear();

78 79
    _polygonModel.clearAndDeleteContents();

80 81
    emit cleared();

82 83 84
    setDirty(true);
}

85
void QGCMapPolygon::adjustVertex(int vertexIndex, const QGeoCoordinate coordinate)
86 87
{
    _polygonPath[vertexIndex] = QVariant::fromValue(coordinate);
DonLakeFlyer's avatar
DonLakeFlyer committed
88 89 90 91
    if (!_centerDrag) {
        // When dragging center we don't signal path changed until add vertices are updated
        emit pathChanged();
    }
92 93 94

    _polygonModel.value<QGCQGeoCoordinate*>(vertexIndex)->setCoordinate(coordinate);

95 96 97 98
    if (!_ignoreCenterUpdates) {
        _updateCenter();
    }

99 100 101 102 103 104 105
    setDirty(true);
}

void QGCMapPolygon::setDirty(bool dirty)
{
    if (_dirty != dirty) {
        _dirty = dirty;
106 107 108
        if (!dirty) {
            _polygonModel.setDirty(false);
        }
109 110 111 112
        emit dirtyChanged(dirty);
    }
}

113
QGeoCoordinate QGCMapPolygon::_coordFromPointF(const QPointF& point) const
114
{
115 116 117 118 119 120
    QGeoCoordinate coord;

    if (_polygonPath.count() > 0) {
        QGeoCoordinate tangentOrigin = _polygonPath[0].value<QGeoCoordinate>();
        convertNedToGeo(-point.y(), point.x(), 0, tangentOrigin, &coord);
    }
121

122 123
    return coord;
}
124

125 126 127
QPointF QGCMapPolygon::_pointFFromCoord(const QGeoCoordinate& coordinate) const
{
    if (_polygonPath.count() > 0) {
128
        double y, x, down;
129
        QGeoCoordinate tangentOrigin = _polygonPath[0].value<QGeoCoordinate>();
130

131 132
        convertGeoToNed(coordinate, tangentOrigin, &y, &x, &down);
        return QPointF(x, -y);
133 134
    }

135 136
    return QPointF();
}
137

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
QPolygonF QGCMapPolygon::_toPolygonF(void) const
{
    QPolygonF polygon;

    if (_polygonPath.count() > 2) {
        for (int i=0; i<_polygonPath.count(); i++) {
            polygon.append(_pointFFromCoord(_polygonPath[i].value<QGeoCoordinate>()));
        }
    }

    return polygon;
}

bool QGCMapPolygon::containsCoordinate(const QGeoCoordinate& coordinate) const
{
    if (_polygonPath.count() > 2) {
        return _toPolygonF().containsPoint(_pointFFromCoord(coordinate), Qt::OddEvenFill);
    } else {
        return false;
    }
}

160 161 162
void QGCMapPolygon::setPath(const QList<QGeoCoordinate>& path)
{
    _polygonPath.clear();
163
    _polygonModel.clearAndDeleteContents();
164
    foreach(const QGeoCoordinate& coord, path) {
165
        _polygonPath.append(QVariant::fromValue(coord));
166
        _polygonModel.append(new QGCQGeoCoordinate(coord, this));
167
    }
168

169 170 171 172 173 174 175
    setDirty(true);
    emit pathChanged();
}

void QGCMapPolygon::setPath(const QVariantList& path)
{
    _polygonPath = path;
176 177 178

    _polygonModel.clearAndDeleteContents();
    for (int i=0; i<_polygonPath.count(); i++) {
179
        _polygonModel.append(new QGCQGeoCoordinate(_polygonPath[i].value<QGeoCoordinate>(), this));
180 181
    }

182 183 184
    setDirty(true);
    emit pathChanged();
}
185 186 187

void QGCMapPolygon::saveToJson(QJsonObject& json)
{
188
    QJsonValue jsonValue;
189

190
    JsonHelper::saveGeoCoordinateArray(_polygonPath, false /* writeAltitude*/, jsonValue);
191
    json.insert(jsonPolygonKey, jsonValue);
192 193 194 195 196 197 198 199 200
    setDirty(false);
}

bool QGCMapPolygon::loadFromJson(const QJsonObject& json, bool required, QString& errorString)
{
    errorString.clear();
    clear();

    if (required) {
201
        if (!JsonHelper::validateRequiredKeys(json, QStringList(jsonPolygonKey), errorString)) {
202 203
            return false;
        }
204
    } else if (!json.contains(jsonPolygonKey)) {
205 206 207
        return true;
    }

208
    if (!JsonHelper::loadGeoCoordinateArray(json[jsonPolygonKey], false /* altitudeRequired */, _polygonPath, errorString)) {
209 210
        return false;
    }
211 212

    for (int i=0; i<_polygonPath.count(); i++) {
213
        _polygonModel.append(new QGCQGeoCoordinate(_polygonPath[i].value<QGeoCoordinate>(), this));
214 215
    }

216
    setDirty(false);
217
    emit pathChanged();
218 219 220 221

    return true;
}

222 223 224 225
QList<QGeoCoordinate> QGCMapPolygon::coordinateList(void) const
{
    QList<QGeoCoordinate> coords;

226 227
    for (int i=0; i<_polygonPath.count(); i++) {
        coords.append(_polygonPath[i].value<QGeoCoordinate>());
228 229 230 231
    }

    return coords;
}
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258

void QGCMapPolygon::splitPolygonSegment(int vertexIndex)
{
    int nextIndex = vertexIndex + 1;
    if (nextIndex > _polygonPath.length() - 1) {
        nextIndex = 0;
    }

    QGeoCoordinate firstVertex = _polygonPath[vertexIndex].value<QGeoCoordinate>();
    QGeoCoordinate nextVertex = _polygonPath[nextIndex].value<QGeoCoordinate>();

    double distance = firstVertex.distanceTo(nextVertex);
    double azimuth = firstVertex.azimuthTo(nextVertex);
    QGeoCoordinate newVertex = firstVertex.atDistanceAndAzimuth(distance / 2, azimuth);

    if (nextIndex == 0) {
        appendVertex(newVertex);
    } else {
        _polygonModel.insert(nextIndex, new QGCQGeoCoordinate(newVertex, this));
        _polygonPath.insert(nextIndex, QVariant::fromValue(newVertex));
        emit pathChanged();
    }
}

void QGCMapPolygon::appendVertex(const QGeoCoordinate& coordinate)
{
    _polygonPath.append(QVariant::fromValue(coordinate));
259
    _polygonModel.append(new QGCQGeoCoordinate(coordinate, this));
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
    emit pathChanged();
}

void QGCMapPolygon::_polygonModelDirtyChanged(bool dirty)
{
    if (dirty) {
        setDirty(true);
    }
}

void QGCMapPolygon::removeVertex(int vertexIndex)
{
    if (vertexIndex < 0 && vertexIndex > _polygonPath.length() - 1) {
        qWarning() << "Call to removePolygonCoordinate with bad vertexIndex:count" << vertexIndex << _polygonPath.length();
        return;
    }

    if (_polygonPath.length() <= 3) {
        // Don't allow the user to trash the polygon
        return;
    }

    QObject* coordObj = _polygonModel.removeAt(vertexIndex);
    coordObj->deleteLater();

    _polygonPath.removeAt(vertexIndex);
    emit pathChanged();
Don Gagne's avatar
Don Gagne committed
287 288

    _updateCenter();
289 290 291 292 293 294
}

void QGCMapPolygon::_polygonModelCountChanged(int count)
{
    emit countChanged(count);
}
295 296 297 298 299 300

void QGCMapPolygon::_updateCenter(void)
{
    if (!_ignoreCenterUpdates) {
        QGeoCoordinate center;

Don Gagne's avatar
Don Gagne committed
301 302 303 304 305 306 307
        if (_polygonPath.count() > 2) {            
            QPointF centroid(0, 0);
            QPolygonF polygonF = _toPolygonF();
            for (int i=0; i<polygonF.count(); i++) {
                centroid += polygonF[i];
            }
            center = _coordFromPointF(QPointF(centroid.x() / polygonF.count(), centroid.y() / polygonF.count()));
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
        }

        _center = center;
        emit centerChanged(center);
    }
}

void QGCMapPolygon::setCenter(QGeoCoordinate newCenter)
{
    if (newCenter != _center) {
        _ignoreCenterUpdates = true;

        // Adjust polygon vertices to new center
        double distance = _center.distanceTo(newCenter);
        double azimuth = _center.azimuthTo(newCenter);

        for (int i=0; i<count(); i++) {
            QGeoCoordinate oldVertex = _polygonPath[i].value<QGeoCoordinate>();
            QGeoCoordinate newVertex = oldVertex.atDistanceAndAzimuth(distance, azimuth);
            adjustVertex(i, newVertex);
        }

DonLakeFlyer's avatar
DonLakeFlyer committed
330 331 332 333 334
        if (_centerDrag) {
            // When center dragging signals are delayed until all vertices are updated
            emit pathChanged();
        }

335
        _ignoreCenterUpdates = false;
DonLakeFlyer's avatar
DonLakeFlyer committed
336 337 338 339 340 341 342 343 344 345 346

        _center = newCenter;
        emit centerChanged(newCenter);
    }
}

void QGCMapPolygon::setCenterDrag(bool centerDrag)
{
    if (centerDrag != _centerDrag) {
        _centerDrag = centerDrag;
        emit centerDragChanged(centerDrag);
347 348
    }
}
349 350 351 352 353 354 355 356

void QGCMapPolygon::setInteractive(bool interactive)
{
    if (_interactive != interactive) {
        _interactive = interactive;
        emit interactiveChanged(interactive);
    }
}