void GeoDataLineStringPrivate::optimize (GeoDataLineString& lineString) const
{

    QVector<GeoDataCoordinates>::iterator itCoords = lineString.begin();
    QVector<GeoDataCoordinates>::const_iterator itEnd = lineString.constEnd();

    if (lineString.size() < 2) return;

    // Calculate the least non-zero detail-level by checking the bounding box
    int startLevel = levelForResolution( ( lineString.latLonAltBox().width() + lineString.latLonAltBox().height() ) / 2 );

    int currentLevel = startLevel;
    int maxLevel = startLevel;
    GeoDataCoordinates currentCoords;
    lineString.first().setDetail(startLevel);

    // Iterate through the linestring to assign different detail levels to the nodes.
    // In general the first and last node should have the start level assigned as
    // a detail level.
    // Starting from the first node the algorithm picks those nodes which
    // have a distance from each other that is just above the resolution that is
    // associated with the start level (which we use as a "current level").
    // Each of those nodes get the current level assigned as the detail level.
    // After iterating through the linestring we increment the current level value
    // and starting again with the first node we assign detail values in a similar way
    // to the remaining nodes which have no final detail level assigned yet.
    // We do as many iterations through the lineString as needed and bump up the
    // current level until all nodes have a non-zero detail level assigned.

    while ( currentLevel  < 16 && currentLevel <= maxLevel + 1 ) {
        itCoords = lineString.begin();

        currentCoords = *itCoords;
        ++itCoords;

        for( ; itCoords != itEnd; ++itCoords) {
            if (itCoords->detail() != 0 && itCoords->detail() < currentLevel) continue;

            if ( currentLevel == startLevel && (itCoords->longitude() == -M_PI || itCoords->longitude() == M_PI
                || itCoords->latitude() < -89 * DEG2RAD || itCoords->latitude() > 89 * DEG2RAD)) {
                itCoords->setDetail(startLevel);
                currentCoords = *itCoords;
                maxLevel = currentLevel;
                continue;
            }
            if (distanceSphere( currentCoords, *itCoords ) < resolutionForLevel(currentLevel + 1)) {
                itCoords->setDetail(currentLevel + 1);
            }
            else {
                itCoords->setDetail(currentLevel);
                currentCoords = *itCoords;
                maxLevel = currentLevel;
            }
        }
        ++currentLevel;
    }
    lineString.last().setDetail(startLevel);
}
Example #2
0
void MonavMap::parseBoundingBox( const QFileInfo &file )
{
    GeoDataLineString points;
    bool tooLarge = false;
    QFile input( file.absoluteFilePath() );
    if ( input.open( QFile::ReadOnly ) ) {
        GeoDataParser parser( GeoData_KML );
        if ( !parser.read( &input ) ) {
            mDebug() << "Could not parse file: " << parser.errorString();
            return;
        }

        GeoDocument *doc = parser.releaseDocument();
        input.close();
        GeoDataDocument *document = dynamic_cast<GeoDataDocument*>( doc );
        QVector<GeoDataPlacemark*> placemarks = document->placemarkList();
        if ( placemarks.size() == 1 ) {
            GeoDataPlacemark* placemark = placemarks.first();
            m_name = placemark->name();
            m_version = placemark->extendedData().value( "version" ).value().toString();
            m_date = placemark->extendedData().value( "date" ).value().toString();
            m_transport = placemark->extendedData().value( "transport" ).value().toString();
            m_payload = placemark->extendedData().value( "payload" ).value().toString();
            GeoDataMultiGeometry* geometry = dynamic_cast<GeoDataMultiGeometry*>( placemark->geometry() );
            if ( geometry->size() > 1500 ) {
                tooLarge = true;
            }
            for ( int i = 0; geometry && i < geometry->size(); ++i ) {
                GeoDataLinearRing* poly = dynamic_cast<GeoDataLinearRing*>( geometry->child( i ) );
                if ( poly ) {
                    for ( int j = 0; j < poly->size(); ++j ) {
                        points << poly->at( j );
                    }
                    m_tiles.push_back( *poly );
                }

                if ( poly->size() > 1500 ) {
                    tooLarge = true;
                }
            }
        } else {
            mDebug() << "File " << file.absoluteFilePath() << " does not contain one placemark, but " << placemarks.size();
        }

        delete doc;
    }
    m_boundingBox = points.latLonAltBox();

    if ( tooLarge ) {
        // The bounding box polygon is rather complicated, therefore not allowing a quick check
        // and also occupying memory. Discard the polygon and only store the rectangular bounding
        // box. Only happens for non-simplified bounding box polygons.
        mDebug() << "Discarding too large bounding box poylgon for " << file.absoluteFilePath() << ". Please check for a map update.";
        m_tiles.clear();
    }
}
bool CylindricalProjection::screenCoordinates( const GeoDataLineString &lineString,
                                                  const ViewportParams *viewport,
                                                  QVector<QPolygonF *> &polygons ) const
{

    Q_D( const CylindricalProjection );
    // Compare bounding box size of the line string with the angularResolution
    // Immediately return if the latLonAltBox is smaller.
    if ( !viewport->resolves( lineString.latLonAltBox() ) ) {
    //    mDebug() << "Object too small to be resolved";
        return false;
    }

    QVector<QPolygonF *> subPolygons;
    d->lineStringToPolygon( lineString, viewport, subPolygons );

    polygons << subPolygons;
    return polygons.isEmpty();
}
bool CylindricalProjectionPrivate::lineStringToPolygon( const GeoDataLineString &lineString,
                                              const ViewportParams *viewport,
                                              QVector<QPolygonF *> &polygons ) const
{
    const TessellationFlags f = lineString.tessellationFlags();

    qreal x = 0;
    qreal y = 0;

    qreal previousX = -1.0;
    qreal previousY = -1.0;

    int mirrorCount = 0;
    qreal distance = repeatDistance( viewport );

    polygons.append( new QPolygonF );

    GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
    GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();

    GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
    GeoDataLineString::ConstIterator itEnd = lineString.constEnd();

    bool processingLastNode = false;

    // We use a while loop to be able to cover linestrings as well as linear rings:
    // Linear rings require to tessellate the path from the last node to the first node
    // which isn't really convenient to achieve with a for loop ...

    const bool isLong = lineString.size() > 10;
    const int maximumDetail = levelForResolution(viewport->angularResolution());
    // The first node of optimized linestrings has a non-zero detail value.
    const bool hasDetail = itBegin->detail() != 0;

    while ( itCoords != itEnd )
    {
        // Optimization for line strings with a big amount of nodes
        bool skipNode = (hasDetail ? itCoords->detail() > maximumDetail
                : itCoords != itBegin && isLong && !processingLastNode &&
                !viewport->resolves( *itPreviousCoords, *itCoords ) );

        if ( !skipNode ) {


            Q_Q( const CylindricalProjection );

            q->screenCoordinates( *itCoords, viewport, x, y );

            // Initializing variables that store the values of the previous iteration
            if ( !processingLastNode && itCoords == itBegin ) {
                itPreviousCoords = itCoords;
                previousX = x;
                previousY = y;
            }

            // This if-clause contains the section that tessellates the line
            // segments of a linestring. If you are about to learn how the code of
            // this class works you can safely ignore this section for a start.

            if ( lineString.tessellate() ) {

                mirrorCount = tessellateLineSegment( *itPreviousCoords, previousX, previousY,
                                           *itCoords, x, y,
                                           polygons, viewport,
                                           f, mirrorCount, distance );
            }

            else {
                // special case for polys which cross dateline but have no Tesselation Flag
                // the expected rendering is a screen coordinates straight line between
                // points, but in projections with repeatX things are not smooth
                mirrorCount = crossDateLine( *itPreviousCoords, *itCoords, x, y, polygons, mirrorCount, distance );
            }

            itPreviousCoords = itCoords;
            previousX = x;
            previousY = y;
        }

        // Here we modify the condition to be able to process the
        // first node after the last node in a LinearRing.

        if ( processingLastNode ) {
            break;
        }
        ++itCoords;

        if ( itCoords == itEnd  && lineString.isClosed() ) {
            itCoords = itBegin;
            processingLastNode = true;
        }
    }

    GeoDataLatLonAltBox box = lineString.latLonAltBox();

    // Closing e.g. in the Antarctica case.
    // This code makes the assumption that
    // - the first node is located at 180 E
    // - and the last node is located at 180 W
    // TODO: add a similar pattern in the crossDateLine() code.
    /*
    if( lineString.isClosed() && box.width() == 2*M_PI ) {
        QPolygonF *poly = polygons.last();
        if( box.containsPole( NorthPole ) ) {
            qreal topMargin = 0.0;
            qreal dummy = 0.0;
            q_ptr->screenCoordinates(0.0, q_ptr->maxLat(), viewport, topMargin, dummy );
            poly->push_back( QPointF( poly->last().x(), topMargin ) );
            poly->push_back( QPointF( poly->first().x(), topMargin ) );
        } else {
            qreal bottomMargin = 0.0;
            qreal dummy = 0.0;
            q_ptr->screenCoordinates(0.0, q_ptr->minLat(), viewport, bottomMargin, dummy );
            poly->push_back( QPointF( poly->last().x(), bottomMargin ) );
            poly->push_back( QPointF( poly->first().x(), bottomMargin ) );
        }
    } */

    repeatPolygons( viewport, polygons );

    return polygons.isEmpty();
}