Пример #1
0
GeoDataDocument *ShpRunner::parseFile(const QString &fileName, DocumentRole role, QString &error)
{
    QFileInfo fileinfo( fileName );
    if (fileinfo.suffix().compare(QLatin1String("shp"), Qt::CaseInsensitive) != 0) {
        error = QStringLiteral("File %1 does not have a shp suffix").arg(fileName);
        mDebug() << error;
        return nullptr;
    }

    SHPHandle handle = SHPOpen( fileName.toStdString().c_str(), "rb" );
    if ( !handle ) {
        error = QStringLiteral("Failed to read %1").arg(fileName);
        mDebug() << error;
        return nullptr;
    }
    int entities;
    int shapeType;
    SHPGetInfo( handle, &entities, &shapeType, NULL, NULL );
    mDebug() << " SHP info " << entities << " Entities "
             << shapeType << " Shape Type ";

    DBFHandle dbfhandle;
    dbfhandle = DBFOpen( fileName.toStdString().c_str(), "rb");
    int nameField = DBFGetFieldIndex( dbfhandle, "Name" );
    int noteField = DBFGetFieldIndex( dbfhandle, "Note" );
    int mapColorField = DBFGetFieldIndex( dbfhandle, "mapcolor13" );

    GeoDataDocument *document = new GeoDataDocument;
    document->setDocumentRole( role );

    if ( mapColorField != -1 ) {
        GeoDataSchema schema;
        schema.setId(QStringLiteral("default"));
        GeoDataSimpleField simpleField;
        simpleField.setName(QStringLiteral("mapcolor13"));
        simpleField.setType( GeoDataSimpleField::Double );
        schema.addSimpleField( simpleField );
        document->addSchema( schema );
    }

    for ( int i=0; i< entities; ++i ) {
        GeoDataPlacemark  *placemark = 0;
        placemark = new GeoDataPlacemark;
        document->append( placemark );

        SHPObject *shape = SHPReadObject( handle, i );
        if (nameField != -1) {
            const char* info = DBFReadStringAttribute( dbfhandle, i, nameField );
            // TODO: defaults to utf-8 encoding, but could be also something else, optionally noted in a .cpg file
            placemark->setName( info );
            mDebug() << "name " << placemark->name();
        }
        if (noteField != -1) {
            const char* note = DBFReadStringAttribute( dbfhandle, i, noteField );
            // TODO: defaults to utf-8 encoding, see comment for name
            placemark->setDescription( note );
            mDebug() << "desc " << placemark->description();
        }

        double mapColor = DBFReadDoubleAttribute( dbfhandle, i, mapColorField );
        if ( mapColor ) {
            GeoDataStyle::Ptr style(new GeoDataStyle);
            if ( mapColor >= 0 && mapColor <=255 ) {
                quint8 colorIndex = quint8( mapColor );
                style->polyStyle().setColorIndex( colorIndex );
            }
            else {
                quint8 colorIndex = 0;     // mapColor is undefined in this case
                style->polyStyle().setColorIndex( colorIndex );
            }
            placemark->setStyle( style );
        }

        switch ( shapeType ) {
            case SHPT_POINT: {
                GeoDataPoint *point = new GeoDataPoint( *shape->padfX, *shape->padfY, 0, GeoDataCoordinates::Degree );
                placemark->setGeometry( point );
                mDebug() << "point " << placemark->name();
                break;
            }

            case SHPT_MULTIPOINT: {
                GeoDataMultiGeometry *geom = new GeoDataMultiGeometry;
                for( int j=0; j<shape->nVertices; ++j ) {
                    geom->append( new GeoDataPoint( GeoDataCoordinates(
                                  shape->padfX[j], shape->padfY[j],
                                  0, GeoDataCoordinates::Degree ) ) );
                }
                placemark->setGeometry( geom );
                mDebug() << "multipoint " << placemark->name();
                break;
            }

            case SHPT_ARC: {
                if ( shape->nParts != 1 ) {
                    GeoDataMultiGeometry *geom = new GeoDataMultiGeometry;
                    for( int j=0; j<shape->nParts; ++j ) {
                        GeoDataLineString *line = new GeoDataLineString;
                        int itEnd = (j + 1 < shape->nParts) ? shape->panPartStart[j+1] : shape->nVertices;
                        for( int k=shape->panPartStart[j]; k<itEnd; ++k ) {
                            line->append( GeoDataCoordinates(
                                          shape->padfX[k], shape->padfY[k],
                                          0, GeoDataCoordinates::Degree ) );
                        }
                        geom->append( line );
                    }
                    placemark->setGeometry( geom );
                    mDebug() << "arc " << placemark->name() << " " << shape->nParts;

                } else {
                    GeoDataLineString *line = new GeoDataLineString;
                    for( int j=0; j<shape->nVertices; ++j ) {
                        line->append( GeoDataCoordinates(
                                      shape->padfX[j], shape->padfY[j],
                                      0, GeoDataCoordinates::Degree ) );
                    }
                    placemark->setGeometry( line );
                    mDebug() << "arc " << placemark->name() << " " << shape->nParts;
                }
                break;
            }

            case SHPT_POLYGON: {
                if ( shape->nParts != 1 ) {
                    bool isRingClockwise = false;
                    GeoDataMultiGeometry *multigeom = new GeoDataMultiGeometry;
                    GeoDataPolygon *poly = 0;
                    int polygonCount = 0;
                    for( int j=0; j<shape->nParts; ++j ) {
                        GeoDataLinearRing ring;
                        int itStart = shape->panPartStart[j];
                        int itEnd = (j + 1 < shape->nParts) ? shape->panPartStart[j+1] : shape->nVertices;
                        for( int k = itStart; k<itEnd; ++k ) {
                            ring.append( GeoDataCoordinates(
                                         shape->padfX[k], shape->padfY[k],
                                         0, GeoDataCoordinates::Degree ) );
                        }
                        isRingClockwise = ring.isClockwise();
                        if ( j == 0 || isRingClockwise ) {
                            poly = new GeoDataPolygon;
                            ++polygonCount;
                            poly->setOuterBoundary( ring );
                            if ( polygonCount > 1 ) {
                                multigeom->append( poly );
                            }
                        }
                        else {
                            poly->appendInnerBoundary( ring );
                        }
                    }
                    if ( polygonCount > 1 ) {
                        placemark->setGeometry( multigeom );
                    }
                    else {
                        placemark->setGeometry( poly );
                        delete multigeom;
                        multigeom = 0;
                    }
                    mDebug() << "donut " << placemark->name() << " " << shape->nParts;

                } else {
                    GeoDataPolygon *poly = new GeoDataPolygon;
                    GeoDataLinearRing ring;
                    for( int j=0; j<shape->nVertices; ++j ) {
                        ring.append( GeoDataCoordinates(
                                         shape->padfX[j], shape->padfY[j],
                                         0, GeoDataCoordinates::Degree ) );
                    }
                    poly->setOuterBoundary( ring );
                    placemark->setGeometry( poly );
                    mDebug() << "poly " << placemark->name() << " " << shape->nParts;
                }
                break;
            }
        }
    }

    SHPClose( handle );

    DBFClose( dbfhandle );

    if ( document->size() ) {
        document->setFileName( fileName );
        return document;
    } else {
        delete document;
        return nullptr;
    }
}
Пример #2
0
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;
}
Пример #3
0
bool JsonParser::read( QIODevice* device )
{
    // Assert previous document got released.
    delete m_document;
    m_document = new GeoDataDocument;
    Q_ASSERT( m_document );

    // Fixes for test parsing
    device->seek(21); // Strip off 'onKothicDataRespone('
    QString temp = QString::fromUtf8( device->readAll() );
    int midIndex = temp.size();
    int rightIndex = midIndex;
    for ( int i=0; i<4; ++i ) {
        rightIndex = midIndex;
        midIndex = temp.lastIndexOf( ',', midIndex-1 );
        if ( i==1 ) {
            QString name = temp.mid( midIndex-1 );
            name.remove( name.size()-2,2 );
            m_document->setName( "Kothic " + name );
        }
    }

    QString stream = temp.mid(0, midIndex);
    stream.prepend('(');
    stream.append("})");
    bool hasGranularity = false;
    int const granularity = temp.mid(midIndex+15, rightIndex-midIndex-16).toInt( &hasGranularity );
    if (!hasGranularity) {
        mDebug() << "Cannot parse json file (failed to parse granularity) " << temp;
        return false;
    }

    /** THIS IS A TEST PARSER FOR KOTHIK's JSON FORMAT **/

    m_data = m_engine.evaluate( stream );
    if (m_engine.hasUncaughtException()) {
        mDebug() << "Cannot parse json file: " << m_engine.uncaughtException().toString();
        return false;
    }

    // Start parsing
    GeoDataPlacemark *placemark;
    GeoDataFeature::GeoDataVisualCategory category;

    // Bounding box coordinates
    float east;
    float south;
    float west;
    float north;

    // Global data (even if it is at the end of the json response
    // it is possible to read it now)

    if ( m_data.property( "bbox" ).isArray() ){
        QStringList coors = m_data.property( "bbox" ).toString().split( QLatin1Char( ',' ) );

        // Load the bounding box coordinates
        west  = coors.at(0).toFloat();
        east  = coors.at(2).toFloat();
        south = coors.at(1).toFloat();
        north = coors.at(3).toFloat();
    }
    else{
        mDebug() << "Cannot parse bbox";
        return false;
    }

    //  All downloaded placemarks will be features, so we should iterate
    //  on features
    QScriptValue const features = m_data.property( "features" );
    if (features.isArray()){
        QScriptValueIterator iterator( features );

        // Add items to the list
        while ( iterator.hasNext() ) {
            iterator.next();

            GeoDataGeometry * geom;
            placemark = new GeoDataPlacemark();

            QString const typeProperty = iterator.value().property( "type" ).toString();
            if ( typeProperty == "Polygon" ){
                geom = new GeoDataPolygon( RespectLatitudeCircle | Tessellate );
            } else if ( typeProperty == "LineString" ){
                    geom = new GeoDataLineString( RespectLatitudeCircle | Tessellate );
            } else if ( typeProperty == "Point" ){
                geom = new GeoDataPoint();
            } else
                    geom = 0;

            QScriptValueIterator it (iterator.value().property( "properties" ));

            bool propertiesCorrect = false;

            // Parsing properties
            while ( it.hasNext() && geom != 0 ) {
                it.next();

                if ( it.name() == "name" ){
                    placemark->setName( it.value().toString() );
                }else if ( !propertiesCorrect ){
                    category = GeoDataFeature::OsmVisualCategory( it.name() + '=' + it.value().toString() );

                    if (category != 0){
                        placemark->setVisualCategory( category );
                        propertiesCorrect = true;
                    }
                }
            }

            // Parsing coordinates

            QScriptValue const coordinatesProperty = iterator.value().property( "coordinates" );
            if ( coordinatesProperty.isArray() ){

                QScriptValueIterator it ( coordinatesProperty );

                while ( it.hasNext() ) {
                    it.next();

                    QStringList coors = it.value().toString().split( QLatin1Char( ',' ) );
                    for (int x = 0; x < coors.size()-1 && coors.size()>1 ;){

                        float auxX = ( coors.at(x++).toFloat() / granularity)*(east-west)   + west;
                        float auxY = ( coors.at(x++).toFloat() / granularity)*(north-south) + south;


                        QString const typeProperty = iterator.value().property( "type" ).toString();
                            if (typeProperty == "Polygon"){

                                GeoDataLinearRing ring = ((GeoDataPolygon*)geom)->outerBoundary();
                                ring.append( GeoDataCoordinates(auxX, auxY,0, GeoDataCoordinates::Degree ) );

                                // FIXME appending to the ring could be done more efficiently

                                ((GeoDataPolygon*)geom)->setOuterBoundary(ring);
                            }
                            else
                                if (typeProperty == "LineString"){
                                    ((GeoDataLineString*) geom)->append( GeoDataCoordinates(auxX, auxY,0, GeoDataCoordinates::Degree ) );
                           } else if (typeProperty == "Point"){
                                    ((GeoDataPoint*) geom)->setCoordinates( GeoDataCoordinates(auxX,auxY,0, GeoDataCoordinates::Degree ) );
                           }

                    }
                }
            }

            if ( propertiesCorrect && geom != 0 ){
                placemark->setGeometry( geom );
                placemark->setVisible( true );
                m_document->append( placemark );
            }
        }
    }
    return true;
}