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); }
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(); }