CircularSurveyComplexItem.cc 11.2 KB
Newer Older
1 2 3
#include "CircularSurveyComplexItem.h"


Valentin Platzgummer's avatar
Valentin Platzgummer committed
4
const char* CircularSurveyComplexItem::settingsGroup =              "CircularSurvey";
5 6
const char* CircularSurveyComplexItem::deltaRName =                 "DeltaR";
const char* CircularSurveyComplexItem::deltaAlphaName =             "DeltaAlpha";
7 8

CircularSurveyComplexItem::CircularSurveyComplexItem(Vehicle *vehicle, bool flyView, const QString &kmlOrShpFile, QObject *parent)
9
    :   TransectStyleComplexItem    (vehicle, flyView, settingsGroup, parent)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
10
    ,   _referencePoint             (QGeoCoordinate(0, 0,0))
11 12 13
    ,   _metaDataMap                (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CircularSurvey.SettingsGroup.json"), this))
    ,   _deltaR                     (settingsGroup, _metaDataMap[deltaRName])
    ,   _deltaAlpha                 (settingsGroup, _metaDataMap[deltaAlphaName])
Valentin Platzgummer's avatar
Valentin Platzgummer committed
14
    ,   _autoGenerated              (false)
15
{
16 17
    _editorQml = "qrc:/qml/CircularSurveyItemEditor.qml";

Valentin Platzgummer's avatar
Valentin Platzgummer committed
18 19 20
    connect(&_deltaR,       &Fact::valueChanged, this, &CircularSurveyComplexItem::_setDirty);
    connect(&_deltaAlpha,   &Fact::valueChanged, this, &CircularSurveyComplexItem::_setDirty);
    connect(this,           &CircularSurveyComplexItem::refPointChanged, this, &CircularSurveyComplexItem::_setDirty);
21

Valentin Platzgummer's avatar
Valentin Platzgummer committed
22 23 24 25
    _deltaR.setRawValue(_deltaR.rawDefaultValue());
    _deltaAlpha.setRawValue(_deltaAlpha.rawDefaultValue());
    qDebug() << _deltaAlpha.rawDefaultValue().toDouble();
    qDebug() << _deltaAlpha.rawValue().toDouble();
26

27 28
    connect(&_updateTimer, &QTimer::timeout, this, &CircularSurveyComplexItem::_updateItem);
    _updateTimer.start(100);
29 30 31
}

void CircularSurveyComplexItem::setRefPoint(const QGeoCoordinate &refPt)
32
{
33 34
    if (refPt != _referencePoint){
        _referencePoint = refPt;
35

36
        emit refPointChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
37
        //qDebug() << _referencePoint.toString();
38 39 40
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
41 42 43 44 45 46 47 48 49
void CircularSurveyComplexItem::setAutoGenerated(bool autoGen)
{
    if (autoGen != _autoGenerated) {
        _autoGenerated = autoGen;

        emit autoGeneratedChanged();
    }
}

50 51 52
QGeoCoordinate CircularSurveyComplexItem::refPoint() const
{
    return _referencePoint;
53 54
}

55 56 57 58 59 60 61 62 63 64
Fact *CircularSurveyComplexItem::deltaR()
{
    return &_deltaR;
}

Fact *CircularSurveyComplexItem::deltaAlpha()
{
    return &_deltaAlpha;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
65 66 67 68 69
bool CircularSurveyComplexItem::autoGenerated()
{
    return _autoGenerated;
}

70 71
bool CircularSurveyComplexItem::load(const QJsonObject &complexObject, int sequenceNumber, QString &errorString)
{
72
    return false;
73 74 75 76
}

void CircularSurveyComplexItem::save(QJsonArray &planItems)
{
77

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
}

void CircularSurveyComplexItem::appendMissionItems(QList<MissionItem *> &items, QObject *missionItemParent)
{

}

void CircularSurveyComplexItem::applyNewAltitude(double newAltitude)
{

}

double CircularSurveyComplexItem::timeBetweenShots()
{
    return 1;
}

bool CircularSurveyComplexItem::readyForSave() const
{
    return false;
}

double CircularSurveyComplexItem::additionalTimeDelay() const
{
    return 0;
}

void CircularSurveyComplexItem::_rebuildTransectsPhase1()
{
107 108 109 110
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
    using namespace PlanimetryCalculus;

111 112 113
    if ( _surveyAreaPolygon.count() < 3)
        return;

114
    _transects.clear();
115 116 117
    QPolygonF surveyPolygon = toQPolygonF(toCartesian2D(_surveyAreaPolygon.coordinateList(), _referencePoint));

    QVector<double> distances;
118 119
    for (const QPointF &p : surveyPolygon) distances.append(norm(p));

120 121 122 123 124 125 126 127 128
    // check if input is valid
    if (   _deltaAlpha.rawValue() > _deltaAlpha.rawMax()
           && _deltaAlpha.rawValue() < _deltaAlpha.rawMin())
        return;
    if (   _deltaR.rawValue() > _deltaR.rawMax()
           && _deltaR.rawValue() < _deltaR.rawMin())
        return;


Valentin Platzgummer's avatar
Valentin Platzgummer committed
129
    double dalpha = _deltaAlpha.rawValue().toDouble()/180.0*M_PI; // radiants
130
    double dr = _deltaR.rawValue().toDouble(); // meter
Valentin Platzgummer's avatar
Valentin Platzgummer committed
131 132
//    double dalpha = 1.0/180.0*M_PI; // radiants
//    double dr = 10.0; // meter
133 134 135 136 137
    double r_min = dr; // meter
    double r_max = (*std::max_element(distances.begin(), distances.end())); // meter

    QPointF origin(0, 0);
    IntersectType type;
138
    bool originInside = true;
139 140 141 142 143
    if (!contains(surveyPolygon, origin, type)) {
        QVector<double> angles;
        for (const QPointF &p : surveyPolygon) angles.append(truncateAngle(angle(p)));

        // determine r_min by successive approximation
144 145
        double r = r_min;
        while ( r < r_max) {
146 147
            Circle circle(r, origin);

148
            if (intersects(circle, surveyPolygon)) {
149 150 151 152
                r_min = r;
                break;
            }

153
            r += dr;
154
        }
155
        originInside = false;
156 157
    }

158 159 160
//    qWarning("r_min, r_max:");
//    qWarning() << r_min;
//    qWarning() << r_max;
161 162 163 164

    QList<QPolygonF> convexPolygons;
    decomposeToConvex(surveyPolygon, convexPolygons);

165
    QList<QList<QPointF>> fullPath;
166
    for (int i = 0; i < convexPolygons.size(); i++) {
167
        const QPolygonF &polygon = convexPolygons[i];
168
        double r = r_min;
169

170 171
        QList<QList<QPointF>> currPolyPath;
        while (r < r_max) {
172
            Circle circle(r, origin);
173 174
            QList<QPointFList> intersectPoints;
            QList<IntersectType> typeList;
175 176
            QList<QPair<int, int>> neighbourList;
            if (intersects(circle, polygon, intersectPoints, neighbourList, typeList)) {
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202

                // intersection Points between circle and polygon, entering polygon
                // when walking in counterclockwise direction along circle
                QPointFList entryPoints;
                // intersection Points between circle and polygon, leaving polygon
                // when walking in counterclockwise direction along circle
                QPointFList exitPoints;
                // determine entryPoints and exit Points
                for (int j = 0; j < intersectPoints.size(); j++) {
                    QList<QPointF> intersects = intersectPoints[j];

                    QPointF p1 = polygon[neighbourList[j].first];
                    QPointF p2 = polygon[neighbourList[j].second];
                    QLineF intersetLine(p1, p2);
                    double lineAngle = truncateAngle(angle(intersetLine));

                    for (QPointF ipt : intersects) {
                        double circleTangentAngle = truncateAngle(angle(ipt)+M_PI_2);
                        // compare line angle and circle tangent at intersection point
                        // to determine between exit and entry point
                        if (   !qFuzzyCompare(lineAngle, circleTangentAngle)
                            && !qFuzzyCompare(lineAngle, truncateAngle(circleTangentAngle + M_PI))) {
                            if (truncateAngle(lineAngle - circleTangentAngle)  < M_PI) {
                                entryPoints.append(ipt);
                            } else {
                                exitPoints.append(ipt);
203 204
                            }
                        }
205 206
                    }
                }
207

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
                // sort
                std::sort(entryPoints.begin(), entryPoints.end(), [](QPointF p1, QPointF p2) {
                   return angle(p1) < angle(p2);
                });
                std::sort(exitPoints.begin(), exitPoints.end(), [](QPointF p1, QPointF p2) {
                   return angle(p1) < angle(p2);
                });

                // match entry and exit points
                int offset = 0;
                double minAngle = std::numeric_limits<double>::infinity();
                for (int k = 0; k < exitPoints.size(); k++) {
                    QPointF pt = exitPoints[k];
                    double alpha = truncateAngle(angle(pt) - angle(entryPoints[0]));
                    if (minAngle > alpha) {
                        minAngle = alpha;
                        offset = k;
                    }
                }
227

228 229 230
                for (int k = 0; k < entryPoints.size(); k++) {
                    double alpha1 = angle(entryPoints[k]);
                    double alpha2 = angle(exitPoints[(k+offset) % entryPoints.size()]);
231

232
                    QList<QPointF> sectorPath = circle.approximateSektor(double(dalpha), alpha1, alpha2);
233 234
                    // use shortestPath() here if necessary, could be a problem if dr >>
                    if (sectorPath.size() > 0)
235 236
                        currPolyPath.append(sectorPath);
                }
237 238
            } else if (originInside) {
                // circle fully inside polygon
239
                QList<QPointF> sectorPath = circle.approximateSektor(double(dalpha), 0, 2*M_PI);
240 241
                // use shortestPath() here if necessary, could be a problem if dr >>
                currPolyPath.append(sectorPath);
242 243
            }
            r += dr;
244 245 246
         }
        if (currPolyPath.size() > 0) {
            fullPath.append(currPolyPath);
247
        }
248
    }
249

Valentin Platzgummer's avatar
Valentin Platzgummer committed
250 251 252 253 254 255 256 257 258 259 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
    // optimize path to lawn pattern
    if (fullPath.size() == 0)
        return;
    QList<QPointF> currentSection = fullPath.takeFirst();
    if ( currentSection.isEmpty() )
        return;
    QList<QList<QPointF>> optiPath; // optimized path
    while( !fullPath.empty() ) {
        optiPath.append(currentSection);
        QPointF endVertex = currentSection.last();
        double minDist = std::numeric_limits<double>::infinity();
        int index = 0;
        bool reversePath = false;

        // iterate over all paths in fullPath and assign the one with the shortest distance to endVertex to currentSection
        for (int i = 0; i < fullPath.size(); i++) {
            auto iteratorPath = fullPath[i];
            double dist = PlanimetryCalculus::distance(endVertex, iteratorPath.first());
            if ( dist < minDist ) {
                minDist = dist;
                index = i;
            }
            dist = PlanimetryCalculus::distance(endVertex, iteratorPath.last());
            if (dist < minDist) {
                minDist = dist;
                index = i;
                reversePath = true;
            }
        }
        currentSection = fullPath.takeAt(index);
        if (reversePath) {
            PolygonCalculus::reversePath(currentSection);
        }
    }

    optiPath.append(currentSection); // append last section
286

287 288

    // convert to CoordInfo_t
Valentin Platzgummer's avatar
Valentin Platzgummer committed
289 290
    for ( const QList<QPointF> &transect : optiPath) {
//    for ( const QList<QPointF> &transect : fullPath) {
291 292 293 294 295 296 297 298
        QList<QGeoCoordinate> geoPath = toGeo(transect, _referencePoint);
        QList<CoordInfo_t> transectList;
        for ( const QGeoCoordinate &coordinate : geoPath) {
            CoordInfo_t coordinfo = {coordinate, CoordTypeInterior};
            transectList.append(coordinfo);
        }
        _transects.append(transectList);
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
299

300 301 302 303 304 305 306 307 308 309 310 311
}

void CircularSurveyComplexItem::_recalcComplexDistance()
{

}

void CircularSurveyComplexItem::_recalcCameraShots()
{

}

312 313 314 315
void CircularSurveyComplexItem::_updateItem()
{
    if (_dirty) {
        _rebuildTransects();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
316
        qDebug() << "CircularSurveyComplexItem::_updateItem()";
317 318 319 320 321
        setDirty(false);
    }

}

322 323 324 325 326 327 328 329 330 331 332
/*!
    \class CircularSurveyComplexItem
    \inmodule Wima

    \brief The \c CircularSurveyComplexItem class provides a survey mission item with circular transects around a point of interest.

    CircularSurveyComplexItem class provides a survey mission item with circular transects around a point of interest. Within the
    \c Wima module it's used to scan a defined area with constant angle (circular transects) to the base station (point of interest).

    \sa WimaArea
*/