예제 #1
0
파일: JsonParser.cpp 프로젝트: KDE/marble
bool JsonParser::read( QIODevice* device )
{
    // Assert previous document got released.
    delete m_document;
    m_document = new GeoDataDocument;
    Q_ASSERT( m_document );

    // Read file data
    QJsonParseError error;
    const QJsonDocument jsonDoc = QJsonDocument::fromJson(device->readAll(), &error);

    if (jsonDoc.isNull()) {
        qDebug() << "Error parsing GeoJSON : " << error.errorString();
        return false;
    }

    // Start parsing
    const QJsonValue featuresValue = jsonDoc.object().value(QStringLiteral("features"));

    // In GeoJSON format, geometries are stored in features, so we iterate on features
    if (featuresValue.isArray()) {
        const QJsonArray featureArray = featuresValue.toArray();

        // Parse each feature
        for (int featureIndex = 0; featureIndex < featureArray.size(); ++featureIndex) {
            const QJsonObject featureObject = featureArray[featureIndex].toObject();

            // Check if the feature contains a geometry
            const QJsonValue geometryValue = featureObject.value(QStringLiteral("geometry"));
            if (geometryValue.isObject()) {
                const QJsonObject geometryObject = geometryValue.toObject();

                // Variables for creating the geometry
                QList<GeoDataGeometry*> geometryList;
                QList<GeoDataPlacemark*> placemarkList;

                // Create the different geometry types
                const QString geometryType = geometryObject.value(QStringLiteral("type")).toString().toUpper();

                if (geometryType == QLatin1String("POLYGON")) {
                    // Check first that there are coordinates
                    const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
                    if (coordinatesValue.isArray()) {
                        const QJsonArray coordinateArray = coordinatesValue.toArray();

                        GeoDataPolygon * geom = new GeoDataPolygon( RespectLatitudeCircle | Tessellate );

                        // Coordinates first array will be the outer boundary, if there are more
                        // positions those will be inner holes
                        for (int ringIndex = 0 ; ringIndex < coordinateArray.size(); ++ringIndex) {
                            const QJsonArray ringArray = coordinateArray[ringIndex].toArray();

                            GeoDataLinearRing linearRing;

                            for (int coordinatePairIndex = 0; coordinatePairIndex < ringArray.size(); ++coordinatePairIndex) {
                                const QJsonArray coordinatePairArray = ringArray[coordinatePairIndex].toArray();

                                const qreal longitude = coordinatePairArray.at(0).toDouble();
                                const qreal latitude = coordinatePairArray.at(1).toDouble();

                                linearRing.append( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
                            }

                            // Outer ring
                            if (ringIndex == 0) {
                                geom->setOuterBoundary( linearRing );
                            }
                            // Inner holes
                            else {
                                geom->appendInnerBoundary( linearRing );
                            }
                        }
                        geometryList.append( geom );
                    }

                } else if (geometryType == QLatin1String("MULTIPOLYGON")) {
                    // Check first that there are coordinates
                    const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
                    if (coordinatesValue.isArray()) {
                        const QJsonArray coordinateArray = coordinatesValue.toArray();

                        for (int polygonIndex = 0; polygonIndex < coordinateArray.size(); ++polygonIndex) {
                            const QJsonArray polygonArray = coordinateArray[polygonIndex].toArray();

                            GeoDataPolygon * geom = new GeoDataPolygon( RespectLatitudeCircle | Tessellate );

                            // Coordinates first array will be the outer boundary, if there are more
                            // positions those will be inner holes
                            for (int ringIndex = 0 ; ringIndex < polygonArray.size(); ++ringIndex) {
                                const QJsonArray ringArray = polygonArray[ringIndex].toArray();

                                GeoDataLinearRing linearRing;

                                for (int coordinatePairIndex = 0; coordinatePairIndex < ringArray.size(); ++coordinatePairIndex) {
                                    const QJsonArray coordinatePairArray = ringArray[coordinatePairIndex].toArray();

                                    const qreal longitude = coordinatePairArray.at(0).toDouble();
                                    const qreal latitude = coordinatePairArray.at(1).toDouble();

                                    linearRing.append( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
                                }

                                // Outer ring
                                if (ringIndex == 0) {
                                    geom->setOuterBoundary( linearRing );
                                }
                                // Inner holes
                                else {
                                    geom->appendInnerBoundary( linearRing );
                                }
                            }
                            geometryList.append( geom );
                        }
                    }

                } else if (geometryType == QLatin1String("LINESTRING")) {

                    // Check first that there are coordinates
                    const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
                    if (coordinatesValue.isArray()) {
                        const QJsonArray coordinateArray = coordinatesValue.toArray();

                        GeoDataLineString * geom = new GeoDataLineString( RespectLatitudeCircle | Tessellate );

                        for (int coordinatePairIndex = 0; coordinatePairIndex < coordinateArray.size(); ++coordinatePairIndex) {
                            const QJsonArray coordinatePairArray = coordinateArray[coordinatePairIndex].toArray();

                            const qreal longitude = coordinatePairArray.at(0).toDouble();
                            const qreal latitude = coordinatePairArray.at(1).toDouble();

                            geom->append( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
                        }
                        geometryList.append( geom );
                    }

                } else if (geometryType == QLatin1String("MULTILINESTRING")) {

                    // Check first that there are coordinates
                    const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
                    if (coordinatesValue.isArray()) {
                        const QJsonArray coordinateArray = coordinatesValue.toArray();

                        for (int lineStringIndex = 0; lineStringIndex < coordinateArray.size(); ++lineStringIndex) {
                            const QJsonArray lineStringArray = coordinateArray[lineStringIndex].toArray();

                            GeoDataLineString * geom = new GeoDataLineString( RespectLatitudeCircle | Tessellate );

                            for (int coordinatePairIndex = 0; coordinatePairIndex < lineStringArray.size(); ++coordinatePairIndex) {
                                const QJsonArray coordinatePairArray = lineStringArray[coordinatePairIndex].toArray();

                                const qreal longitude = coordinatePairArray.at(0).toDouble();
                                const qreal latitude = coordinatePairArray.at(1).toDouble();

                                geom->append( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
                            }
                            geometryList.append( geom );
                        }
                    }

                } else if (geometryType == QLatin1String("POINT")) {

                    // Check first that there are coordinates
                    const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
                    if (coordinatesValue.isArray()) {
                        const QJsonArray coordinatePairArray = coordinatesValue.toArray();

                        GeoDataPoint * geom = new GeoDataPoint();

                        const qreal longitude = coordinatePairArray.at(0).toDouble();
                        const qreal latitude = coordinatePairArray.at(1).toDouble();

                        geom->setCoordinates( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );

                        geometryList.append( geom );
                    }
                } else if (geometryType == QLatin1String("MULTIPOINT")) {

                    // Check first that there are coordinates
                    const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
                    if (coordinatesValue.isArray()) {
                        const QJsonArray coordinateArray = coordinatesValue.toArray();

                        for (int pointIndex = 0; pointIndex < coordinateArray.size(); ++pointIndex) {
                            const QJsonArray coordinatePairArray = coordinateArray[pointIndex].toArray();

                            GeoDataPoint * geom = new GeoDataPoint();

                            const qreal longitude = coordinatePairArray.at(0).toDouble();
                            const qreal latitude = coordinatePairArray.at(1).toDouble();

                            geom->setCoordinates( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );

                            geometryList.append( geom );
                        }
                    }
                }


                // Parse the features properties
                const QJsonValue propertiesValue = featureObject.value(QStringLiteral("properties"));
                if (!geometryList.isEmpty() && propertiesValue.isObject()) {
                    const QJsonObject propertiesObject = propertiesValue.toObject();

                    // First create a placemark for each geometry, there could be multi geometries
                    // that are translated into more than one geometry/placemark
                    for ( int numberGeometries = 0 ; numberGeometries < geometryList.length() ; numberGeometries++ ) {
                        GeoDataPlacemark * placemark = new GeoDataPlacemark();
                        placemarkList.append( placemark );
                    }

                    OsmPlacemarkData osmData;

                    QJsonObject::ConstIterator it = propertiesObject.begin();
                    const QJsonObject::ConstIterator end = propertiesObject.end();
                    for ( ; it != end; ++it) {
                        if (it.value().isObject() || it.value().isArray()) {
                            qDebug() << "Skipping property, values of type arrays and objects not supported:" << it.key();
                            continue;
                        }

                        // pass value through QVariant to also get bool & numbers
                        osmData.addTag(it.key(), it.value().toVariant().toString());
                    }

                    // If the property read, is the features name
                    const auto tagIter = osmData.findTag(QStringLiteral("name"));
                    if (tagIter != osmData.tagsEnd()) {
                        const QString& name = tagIter.value();
                        for (int pl = 0 ; pl < placemarkList.length(); ++pl) {
                            placemarkList.at(pl)->setName(name);
                        }
                    }

                    const GeoDataPlacemark::GeoDataVisualCategory category = StyleBuilder::determineVisualCategory(osmData);
                    if (category != GeoDataPlacemark::None) {
                        // Add the visual category to all the placemarks
                        for (int pl = 0 ; pl < placemarkList.length(); ++pl) {
                            placemarkList.at(pl)->setVisualCategory(category);
                            placemarkList.at(pl)->setOsmData(osmData);
                        }
                    }
                }

                // Add the geometry to the document
                if ( geometryList.length() == placemarkList.length() ) {

                    while( placemarkList.length() > 0 ) {

                        GeoDataPlacemark * placemark = placemarkList.last();
                        placemarkList.pop_back();

                        GeoDataGeometry * geom = geometryList.last();
                        geometryList.pop_back();

                        placemark->setGeometry( geom );
                        placemark->setVisible( true );
                        m_document->append( placemark );
                    }
                }

                // If geometries or placemarks missing inside the lists, delete them
                qDeleteAll( geometryList.begin(), geometryList.end() );
                geometryList.clear();
                qDeleteAll( placemarkList.begin(), placemarkList.end() );
                placemarkList.clear();
            }
        }
    }
    return true;
}
예제 #2
0
void OsmNominatimRunner::handleResult( QNetworkReply* reply )
{   
    QDomDocument xml;
    if (!xml.setContent(reply->readAll())) {
        qWarning() << "Cannot parse osm nominatim result";
        qWarning() << reply->error();
        returnNoResults();
        return;
    }

    QVector<GeoDataPlacemark*> placemarks;
    QDomElement root = xml.documentElement();
    QDomNodeList places = root.elementsByTagName(QStringLiteral("place"));
    for (int i=0; i<places.size(); ++i) {
        QDomNode place = places.at(i);
        QDomNamedNodeMap attributes = place.attributes();
        QString lon = attributes.namedItem(QStringLiteral("lon")).nodeValue();
        QString lat = attributes.namedItem(QStringLiteral("lat")).nodeValue();
        QString desc = attributes.namedItem(QStringLiteral("display_name")).nodeValue();
        QString key = attributes.namedItem(QStringLiteral("class")).nodeValue();
        QString value = attributes.namedItem(QStringLiteral("type")).nodeValue();

        OsmPlacemarkData data;

        GeoDataExtendedData placemarkData = extractChildren(place);
        placemarkData.addValue(GeoDataData(QStringLiteral("class"), key));
        placemarkData.addValue(GeoDataData(QStringLiteral("type"), value));

        QString name = place.firstChildElement(value).text();
        QString road = place.firstChildElement(QStringLiteral("road")).text();
        placemarkData.addValue(GeoDataData(QStringLiteral("name"), name));

        QString city = place.firstChildElement(QStringLiteral("city")).text();
        if( city.isEmpty() ) {
            city = place.firstChildElement(QStringLiteral("town")).text();
            if( city.isEmpty() ) {
                city = place.firstChildElement(QStringLiteral("village")).text();
            } if( city.isEmpty() ) {
                city = place.firstChildElement(QStringLiteral("hamlet")).text();
            }
        }

        QString administrative = place.firstChildElement(QStringLiteral("county")).text();
        if( administrative.isEmpty() ) {
            administrative = place.firstChildElement(QStringLiteral("region")).text();
            if( administrative.isEmpty() ) {
                administrative = place.firstChildElement(QStringLiteral("state")).text();
                data.addTag(QStringLiteral("addr:state"), administrative);
            } else {
                data.addTag(QStringLiteral("district"), administrative);
            }
        }

        QString country = place.firstChildElement(QStringLiteral("country")).text();

        QString description;
        for (int i=0; i<place.childNodes().size(); ++i) {
            QDomElement item = place.childNodes().at(i).toElement();
            description += item.nodeName() + QLatin1Char(':') + item.text() + QLatin1Char('\n');
        }
        description += QLatin1String("Category: ") + key + QLatin1Char('/') + value;

        if (!lon.isEmpty() && !lat.isEmpty() && !desc.isEmpty()) {
            QString placemarkName;
            GeoDataPlacemark* placemark = new GeoDataPlacemark;
            // try to provide 2 fields
            if (!name.isEmpty()) {
                placemarkName = name;
            }
            if (!road.isEmpty() && road != placemarkName ) {
                if( !placemarkName.isEmpty() ) {
                    placemarkName += QLatin1String(", ");
                }
                placemarkName += road;
                data.addTag(QStringLiteral("addr:street"), road);
            }
            if (!city.isEmpty() && !placemarkName.contains(QLatin1Char(',')) && city != placemarkName) {
                if( !placemarkName.isEmpty() ) {
                    placemarkName += QLatin1String(", ");
                }
                placemarkName += city;
                data.addTag(QStringLiteral("addr:city"), city);
            }
            if (!administrative.isEmpty() && !placemarkName.contains(QLatin1Char(',')) && administrative != placemarkName) {
                if( !placemarkName.isEmpty() ) {
                    placemarkName += QLatin1String(", ");
                }
                placemarkName += administrative;
            }
            if (!country.isEmpty() && !placemarkName.contains(QLatin1Char(',')) && country != placemarkName) {
                if( !placemarkName.isEmpty() ) {
                    placemarkName += QLatin1String(", ");
                }
                placemarkName += country;
                data.addTag(QStringLiteral("addr:country"), country);
            }
            if (placemarkName.isEmpty()) {
                placemarkName = desc;
            }
            placemark->setName( placemarkName );
            placemark->setDescription(description);
            placemark->setAddress(desc);
            placemark->setCoordinate( lon.toDouble(), lat.toDouble(), 0, GeoDataCoordinates::Degree );
            const auto category = StyleBuilder::determineVisualCategory(data);
            placemark->setVisualCategory( category );
            placemark->setExtendedData(placemarkData);
            placemark->setOsmData(data);
            placemarks << placemark;
        }
    }
    
    emit searchFinished( placemarks );
}