KMLHelper.cc 5.29 KB
Newer Older
1 2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

10
#include "KMLHelper.h"
11 12

#include <QFile>
13
#include <QVariant>
14

15
const char* KMLHelper::_errorPrefix = QT_TR_NOOP("KML file load failed. %1");
16

17
QDomDocument KMLHelper::_loadFile(const QString& kmlFile, QString& errorString)
18 19 20 21 22 23
{
    QFile file(kmlFile);

    errorString.clear();

    if (!file.exists()) {
24
        errorString = QString(_errorPrefix).arg(tr("File not found: %1").arg(kmlFile));
25 26 27 28
        return QDomDocument();
    }

    if (!file.open(QIODevice::ReadOnly)) {
29
        errorString = QString(_errorPrefix).arg(tr("Unable to open file: %1 error: $%2").arg(kmlFile).arg(file.errorString()));
30 31 32 33 34 35 36
        return QDomDocument();
    }

    QDomDocument doc;
    QString errorMessage;
    int errorLine;
    if (!doc.setContent(&file, &errorMessage, &errorLine)) {
37
        errorString = QString(_errorPrefix).arg(tr("Unable to parse KML file: %1 error: %2 line: %3").arg(kmlFile).arg(errorMessage).arg(errorLine));
38 39 40 41 42 43
        return QDomDocument();
    }

    return doc;
}

44
ShapeFileHelper::ShapeType KMLHelper::determineShapeType(const QString& kmlFile, QString& errorString)
45
{
46
    QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
47
    if (!errorString.isEmpty()) {
48
        return ShapeFileHelper::Error;
49 50 51 52
    }

    QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
    if (rgNodes.count()) {
53
        return ShapeFileHelper::Polygon;
54 55 56 57
    }

    rgNodes = domDocument.elementsByTagName("LineString");
    if (rgNodes.count()) {
58
        return ShapeFileHelper::Polyline;
59 60
    }

61
    errorString = QString(_errorPrefix).arg(tr("No supported type found in KML file."));
62
    return ShapeFileHelper::Error;
63 64
}

65
bool KMLHelper::loadPolygonFromFile(const QString& kmlFile, QList<QGeoCoordinate>& vertices, QString& errorString)
66 67 68 69
{
    errorString.clear();
    vertices.clear();

70
    QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
71 72 73 74 75 76
    if (!errorString.isEmpty()) {
        return false;
    }

    QDomNodeList rgNodes = domDocument.elementsByTagName("Polygon");
    if (rgNodes.count() == 0) {
77
        errorString = QString(_errorPrefix).arg(tr("Unable to find Polygon node in KML"));
78 79 80 81 82
        return false;
    }

    QDomNode coordinatesNode = rgNodes.item(0).namedItem("outerBoundaryIs").namedItem("LinearRing").namedItem("coordinates");
    if (coordinatesNode.isNull()) {
83
        errorString = QString(_errorPrefix).arg(tr("Internal error: Unable to find coordinates node in KML"));
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
        return false;
    }

    QString coordinatesString = coordinatesNode.toElement().text().simplified();
    QStringList rgCoordinateStrings = coordinatesString.split(" ");

    QList<QGeoCoordinate> rgCoords;
    for (int i=0; i<rgCoordinateStrings.count()-1; i++) {
        QString coordinateString = rgCoordinateStrings[i];

        QStringList rgValueStrings = coordinateString.split(",");

        QGeoCoordinate coord;
        coord.setLongitude(rgValueStrings[0].toDouble());
        coord.setLatitude(rgValueStrings[1].toDouble());

        rgCoords.append(coord);
    }

103
    // Determine winding, reverse if needed. QGC wants clockwise winding
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    double sum = 0;
    for (int i=0; i<rgCoords.count(); i++) {
        QGeoCoordinate coord1 = rgCoords[i];
        QGeoCoordinate coord2 = (i == rgCoords.count() - 1) ? rgCoords[0] : rgCoords[i+1];

        sum += (coord2.longitude() - coord1.longitude()) * (coord2.latitude() + coord1.latitude());
    }
    bool reverse = sum < 0.0;
    if (reverse) {
        QList<QGeoCoordinate> rgReversed;

        for (int i=0; i<rgCoords.count(); i++) {
            rgReversed.prepend(rgCoords[i]);
        }
        rgCoords = rgReversed;
    }

    vertices = rgCoords;

    return true;
}

126
bool KMLHelper::loadPolylineFromFile(const QString& kmlFile, QList<QGeoCoordinate>& coords, QString& errorString)
127 128 129 130
{
    errorString.clear();
    coords.clear();

131
    QDomDocument domDocument = KMLHelper::_loadFile(kmlFile, errorString);
132 133 134 135 136 137
    if (!errorString.isEmpty()) {
        return false;
    }

    QDomNodeList rgNodes = domDocument.elementsByTagName("LineString");
    if (rgNodes.count() == 0) {
138
        errorString = QString(_errorPrefix).arg(tr("Unable to find LineString node in KML"));
139 140 141 142 143
        return false;
    }

    QDomNode coordinatesNode = rgNodes.item(0).namedItem("coordinates");
    if (coordinatesNode.isNull()) {
144
        errorString = QString(_errorPrefix).arg(tr("Internal error: Unable to find coordinates node in KML"));
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
        return false;
    }

    QString coordinatesString = coordinatesNode.toElement().text().simplified();
    QStringList rgCoordinateStrings = coordinatesString.split(" ");

    QList<QGeoCoordinate> rgCoords;
    for (int i=0; i<rgCoordinateStrings.count()-1; i++) {
        QString coordinateString = rgCoordinateStrings[i];

        QStringList rgValueStrings = coordinateString.split(",");

        QGeoCoordinate coord;
        coord.setLongitude(rgValueStrings[0].toDouble());
        coord.setLatitude(rgValueStrings[1].toDouble());

        rgCoords.append(coord);
    }

    coords = rgCoords;

    return true;
}