CircularSurveyComplexItem.cc 10.7 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])
14
{
15 16
    _editorQml = "qrc:/qml/CircularSurveyItemEditor.qml";

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

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

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

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

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

QGeoCoordinate CircularSurveyComplexItem::refPoint() const
{
    return _referencePoint;
43 44
}

45 46 47 48 49 50 51 52 53 54
Fact *CircularSurveyComplexItem::deltaR()
{
    return &_deltaR;
}

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

55 56
bool CircularSurveyComplexItem::load(const QJsonObject &complexObject, int sequenceNumber, QString &errorString)
{
57
    return false;
58 59 60 61
}

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

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
}

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()
{
92 93 94 95
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
    using namespace PlanimetryCalculus;

96 97 98
    if ( _surveyAreaPolygon.count() < 3)
        return;

99
    _transects.clear();
100 101 102
    QPolygonF surveyPolygon = toQPolygonF(toCartesian2D(_surveyAreaPolygon.coordinateList(), _referencePoint));

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

105 106 107 108 109 110 111 112 113 114 115
    // 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;


    double dalpha = _deltaAlpha.rawValue().toDouble()/180*M_PI; // radiants
    double dr = _deltaR.rawValue().toDouble(); // meter
116 117 118 119 120
    double r_min = dr; // meter
    double r_max = (*std::max_element(distances.begin(), distances.end())); // meter

    QPointF origin(0, 0);
    IntersectType type;
121
    bool originInside = true;
122 123 124 125 126
    if (!contains(surveyPolygon, origin, type)) {
        QVector<double> angles;
        for (const QPointF &p : surveyPolygon) angles.append(truncateAngle(angle(p)));

        // determine r_min by successive approximation
127 128
        double r = r_min;
        while ( r < r_max) {
129 130
            Circle circle(r, origin);

131
            if (intersects(circle, surveyPolygon)) {
132 133 134 135
                r_min = r;
                break;
            }

136
            r += dr;
137
        }
138
        originInside = false;
139 140
    }

141 142 143
//    qWarning("r_min, r_max:");
//    qWarning() << r_min;
//    qWarning() << r_max;
144 145 146 147

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

148
    QList<QList<QPointF>> fullPath;
149
    for (int i = 0; i < convexPolygons.size(); i++) {
150
        const QPolygonF &polygon = convexPolygons[i];
151
        double r = r_min;
152

153 154
        QList<QList<QPointF>> currPolyPath;
        while (r < r_max) {
155
            Circle circle(r, origin);
156 157
            QList<QPointFList> intersectPoints;
            QList<IntersectType> typeList;
158 159
            QList<QPair<int, int>> neighbourList;
            if (intersects(circle, polygon, intersectPoints, neighbourList, typeList)) {
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

                // 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);
186 187
                            }
                        }
188 189
                    }
                }
190

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
                // 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;
                    }
                }
210

211 212 213
                for (int k = 0; k < entryPoints.size(); k++) {
                    double alpha1 = angle(entryPoints[k]);
                    double alpha2 = angle(exitPoints[(k+offset) % entryPoints.size()]);
214

215
                    QList<QPointF> sectorPath = circle.approximateSektor(double(dalpha), alpha1, alpha2);
216 217
                    // use shortestPath() here if necessary, could be a problem if dr >>
                    if (sectorPath.size() > 0)
218 219
                        currPolyPath.append(sectorPath);
                }
220 221
            } else if (originInside) {
                // circle fully inside polygon
222
                QList<QPointF> sectorPath = circle.approximateSektor(double(dalpha), 0, 2*M_PI);
223 224
                // use shortestPath() here if necessary, could be a problem if dr >>
                currPolyPath.append(sectorPath);
225 226
            }
            r += dr;
227 228 229
         }
        if (currPolyPath.size() > 0) {
            fullPath.append(currPolyPath);
230
        }
231
    }
232

233
    // optimize path to lawn pattern
234 235
    if (fullPath.size() == 0)
        return;
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
    QList<QPointF> currentSection = fullPath.takeFirst();
    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;
258
            }
259
        }
260 261 262 263
        currentSection = fullPath.takeAt(index);
        if (reversePath) {
            PolygonCalculus::reversePath(currentSection);
        }
264 265
    }

266 267 268

    // convert to CoordInfo_t
    for ( const QList<QPointF> &transect : optiPath) {
269 270 271 272 273 274 275 276
        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
277

278 279 280 281 282 283 284 285 286 287 288 289
}

void CircularSurveyComplexItem::_recalcComplexDistance()
{

}

void CircularSurveyComplexItem::_recalcCameraShots()
{

}

290 291 292 293
void CircularSurveyComplexItem::_updateItem()
{
    if (_dirty) {
        _rebuildTransects();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
294
        qDebug() << "CircularSurveyComplexItem::_updateItem()";
295 296 297 298 299
        setDirty(false);
    }

}

300 301 302 303 304 305 306 307 308 309 310
/*!
    \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
*/