diff --git a/src/lib/marble/geodata/data/GeoDataLineString.cpp b/src/lib/marble/geodata/data/GeoDataLineString.cpp index e18e54692..c70808a41 100644 --- a/src/lib/marble/geodata/data/GeoDataLineString.cpp +++ b/src/lib/marble/geodata/data/GeoDataLineString.cpp @@ -1,874 +1,885 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2008 Torsten Rahn // Copyright 2009 Patrick Spendrin // #include "GeoDataLineString.h" #include "GeoDataLineString_p.h" #include "GeoDataLinearRing.h" #include "MarbleMath.h" #include "Quaternion.h" #include "MarbleDebug.h" #include namespace Marble { GeoDataLineString::GeoDataLineString( TessellationFlags f ) : GeoDataGeometry( new GeoDataLineStringPrivate( f ) ) { // mDebug() << "1) GeoDataLineString created:" << p(); } GeoDataLineString::GeoDataLineString( GeoDataLineStringPrivate* priv ) : GeoDataGeometry( priv ) { // mDebug() << "2) GeoDataLineString created:" << p(); } GeoDataLineString::GeoDataLineString( const GeoDataGeometry & other ) : GeoDataGeometry( other ) { // mDebug() << "3) GeoDataLineString created:" << p(); } GeoDataLineString::~GeoDataLineString() { #ifdef DEBUG_GEODATA mDebug() << "delete Linestring"; #endif } GeoDataLineStringPrivate* GeoDataLineString::p() { return static_cast(d); } const GeoDataLineStringPrivate* GeoDataLineString::p() const { return static_cast(d); } void GeoDataLineStringPrivate::interpolateDateLine( const GeoDataCoordinates & previousCoords, const GeoDataCoordinates & currentCoords, GeoDataCoordinates & previousAtDateLine, GeoDataCoordinates & currentAtDateLine, TessellationFlags f ) const { GeoDataCoordinates dateLineCoords; // mDebug() << Q_FUNC_INFO; if ( f.testFlag( RespectLatitudeCircle ) && previousCoords.latitude() == currentCoords.latitude() ) { dateLineCoords = currentCoords; } else { int recursionCounter = 0; dateLineCoords = findDateLine( previousCoords, currentCoords, recursionCounter ); } previousAtDateLine = dateLineCoords; currentAtDateLine = dateLineCoords; if ( previousCoords.longitude() < 0 ) { previousAtDateLine.setLongitude( -M_PI ); currentAtDateLine.setLongitude( +M_PI ); } else { previousAtDateLine.setLongitude( +M_PI ); currentAtDateLine.setLongitude( -M_PI ); } } GeoDataCoordinates GeoDataLineStringPrivate::findDateLine( const GeoDataCoordinates & previousCoords, const GeoDataCoordinates & currentCoords, int recursionCounter ) const { int currentSign = ( currentCoords.longitude() < 0.0 ) ? -1 : +1 ; int previousSign = ( previousCoords.longitude() < 0.0 ) ? -1 : +1 ; qreal longitudeDiff = fabs( previousSign * M_PI - previousCoords.longitude() ) + fabs( currentSign * M_PI - currentCoords.longitude() ); if ( longitudeDiff < 0.001 || recursionCounter == 100 ) { // mDebug() << "stopped at recursion" << recursionCounter << " and longitude difference " << longitudeDiff; return currentCoords; } ++recursionCounter; qreal lon = 0.0; qreal lat = 0.0; qreal altDiff = currentCoords.altitude() - previousCoords.altitude(); const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), currentCoords.quaternion(), 0.5 ); itpos.getSpherical( lon, lat ); qreal altitude = previousCoords.altitude() + 0.5 * altDiff; GeoDataCoordinates interpolatedCoords( lon, lat, altitude ); int interpolatedSign = ( interpolatedCoords.longitude() < 0.0 ) ? -1 : +1 ; /* mDebug() << "SRC" << previousCoords.toString(); mDebug() << "TAR" << currentCoords.toString(); mDebug() << "IPC" << interpolatedCoords.toString(); */ if ( interpolatedSign != currentSign ) { return findDateLine( interpolatedCoords, currentCoords, recursionCounter ); } return findDateLine( previousCoords, interpolatedCoords, recursionCounter ); } quint8 GeoDataLineStringPrivate::levelForResolution(qreal resolution) const { if (m_previousResolution == resolution) return m_level; m_previousResolution = resolution; if (resolution < 0.0000005) m_level = 17; else if (resolution < 0.0000010) m_level = 16; else if (resolution < 0.0000020) m_level = 15; else if (resolution < 0.0000040) m_level = 14; else if (resolution < 0.0000080) m_level = 13; else if (resolution < 0.0000160) m_level = 12; else if (resolution < 0.0000320) m_level = 11; else if (resolution < 0.0000640) m_level = 10; else if (resolution < 0.0001280) m_level = 9; else if (resolution < 0.0002560) m_level = 8; else if (resolution < 0.0005120) m_level = 7; else if (resolution < 0.0010240) m_level = 6; else if (resolution < 0.0020480) m_level = 5; else if (resolution < 0.0040960) m_level = 4; else if (resolution < 0.0081920) m_level = 3; else if (resolution < 0.0163840) m_level = 2; else m_level = 1; return m_level; } qreal GeoDataLineStringPrivate::resolutionForLevel(int level) const { switch (level) { case 0: return 0.0655360; break; case 1: return 0.0327680; break; case 2: return 0.0163840; break; case 3: return 0.0081920; break; case 4: return 0.0040960; break; case 5: return 0.0020480; break; case 6: return 0.0010240; break; case 7: return 0.0005120; break; case 8: return 0.0002560; break; case 9: return 0.0001280; break; case 10: return 0.0000640; break; case 11: return 0.0000320; break; case 12: return 0.0000160; break; case 13: return 0.0000080; break; case 14: return 0.0000040; break; case 15: return 0.0000020; break; case 16: return 0.0000010; break; default: case 17: return 0.0000005; break; } } void GeoDataLineStringPrivate::optimize (GeoDataLineString& lineString) const { QVector::iterator itCoords = lineString.begin(); QVector::const_iterator itEnd = lineString.constEnd(); if (lineString.size() < 2) return; // Calculate the least non-zero detail-level by checking the bounding box quint8 startLevel = levelForResolution( ( lineString.latLonAltBox().width() + lineString.latLonAltBox().height() ) / 2 ); quint8 currentLevel = startLevel; quint8 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); } bool GeoDataLineString::isEmpty() const { return p()->m_vector.isEmpty(); } int GeoDataLineString::size() const { return p()->m_vector.size(); } GeoDataCoordinates& GeoDataLineString::at( int pos ) { GeoDataGeometry::detach(); p()->m_dirtyRange = true; p()->m_dirtyBox = true; return p()->m_vector[ pos ]; } const GeoDataCoordinates& GeoDataLineString::at( int pos ) const { return p()->m_vector.at( pos ); } GeoDataCoordinates& GeoDataLineString::operator[]( int pos ) { GeoDataGeometry::detach(); p()->m_dirtyRange = true; p()->m_dirtyBox = true; return p()->m_vector[ pos ]; } const GeoDataCoordinates& GeoDataLineString::operator[]( int pos ) const { return p()->m_vector[ pos ]; } GeoDataCoordinates& GeoDataLineString::last() { GeoDataGeometry::detach(); p()->m_dirtyRange = true; p()->m_dirtyBox = true; return p()->m_vector.last(); } GeoDataCoordinates& GeoDataLineString::first() { GeoDataGeometry::detach(); return p()->m_vector.first(); } const GeoDataCoordinates& GeoDataLineString::last() const { return p()->m_vector.last(); } const GeoDataCoordinates& GeoDataLineString::first() const { return p()->m_vector.first(); } QVector::Iterator GeoDataLineString::begin() { GeoDataGeometry::detach(); return p()->m_vector.begin(); } QVector::ConstIterator GeoDataLineString::begin() const { return p()->m_vector.constBegin(); } QVector::Iterator GeoDataLineString::end() { GeoDataGeometry::detach(); return p()->m_vector.end(); } QVector::ConstIterator GeoDataLineString::end() const { return p()->m_vector.constEnd(); } QVector::ConstIterator GeoDataLineString::constBegin() const { return p()->m_vector.constBegin(); } QVector::ConstIterator GeoDataLineString::constEnd() const { return p()->m_vector.constEnd(); } void GeoDataLineString::insert( int index, const GeoDataCoordinates& value ) { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); delete d->m_rangeCorrected; d->m_rangeCorrected = 0; d->m_dirtyRange = true; d->m_dirtyBox = true; d->m_vector.insert( index, value ); } void GeoDataLineString::append ( const GeoDataCoordinates& value ) { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); delete d->m_rangeCorrected; d->m_rangeCorrected = 0; d->m_dirtyRange = true; d->m_dirtyBox = true; d->m_vector.append( value ); } void GeoDataLineString::append(const QVector& values) { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); delete d->m_rangeCorrected; d->m_rangeCorrected = 0; d->m_dirtyRange = true; d->m_dirtyBox = true; #if QT_VERSION >= 0x050500 d->m_vector.append(values); #else d->m_vector.reserve(d->m_vector.size() + values.size()); foreach (const GeoDataCoordinates &coordinates, values) { d->m_vector.append(coordinates); } #endif } GeoDataLineString& GeoDataLineString::operator << ( const GeoDataCoordinates& value ) { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); delete d->m_rangeCorrected; d->m_rangeCorrected = 0; d->m_dirtyRange = true; d->m_dirtyBox = true; d->m_vector.append( value ); return *this; } GeoDataLineString& GeoDataLineString::operator << ( const GeoDataLineString& value ) { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); delete d->m_rangeCorrected; d->m_rangeCorrected = 0; d->m_dirtyRange = true; d->m_dirtyBox = true; QVector::const_iterator itCoords = value.constBegin(); QVector::const_iterator itEnd = value.constEnd(); d->m_vector.reserve(d->m_vector.size() + value.size()); for( ; itCoords != itEnd; ++itCoords ) { d->m_vector.append( *itCoords ); } return *this; } bool GeoDataLineString::operator==( const GeoDataLineString &other ) const { if ( !GeoDataGeometry::equals(other) || size() != other.size() || tessellate() != other.tessellate() ) { return false; } const GeoDataLineStringPrivate* d = p(); const GeoDataLineStringPrivate* other_d = other.p(); QVector::const_iterator itCoords = d->m_vector.constBegin(); QVector::const_iterator otherItCoords = other_d->m_vector.constBegin(); QVector::const_iterator itEnd = d->m_vector.constEnd(); QVector::const_iterator otherItEnd = other_d->m_vector.constEnd(); for ( ; itCoords != itEnd && otherItCoords != otherItEnd; ++itCoords, ++otherItCoords ) { if ( *itCoords != *otherItCoords ) { return false; } } Q_ASSERT ( itCoords == itEnd && otherItCoords == otherItEnd ); return true; } bool GeoDataLineString::operator!=( const GeoDataLineString &other ) const { return !this->operator==(other); } void GeoDataLineString::clear() { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); delete d->m_rangeCorrected; d->m_rangeCorrected = 0; d->m_dirtyRange = true; d->m_dirtyBox = true; d->m_vector.clear(); } bool GeoDataLineString::isClosed() const { return false; } bool GeoDataLineString::tessellate() const { return p()->m_tessellationFlags.testFlag(Tessellate); } void GeoDataLineString::setTessellate( bool tessellate ) { GeoDataGeometry::detach(); // According to the KML reference the tesselation of line strings in Google Earth // is generally done along great circles. However for subsequent points that share // the same latitude the latitude circles are followed. Our Tesselate and RespectLatitude // Flags provide this behaviour. For true polygons the latitude circles don't get considered. if ( tessellate ) { p()->m_tessellationFlags |= Tessellate; p()->m_tessellationFlags |= RespectLatitudeCircle; } else { p()->m_tessellationFlags ^= Tessellate; p()->m_tessellationFlags ^= RespectLatitudeCircle; } } TessellationFlags GeoDataLineString::tessellationFlags() const { return p()->m_tessellationFlags; } void GeoDataLineString::setTessellationFlags( TessellationFlags f ) { p()->m_tessellationFlags = f; } +void GeoDataLineString::reverse() +{ + GeoDataGeometry::detach(); + GeoDataLineStringPrivate* d = p(); + delete d->m_rangeCorrected; + d->m_rangeCorrected = 0; + d->m_dirtyRange = true; + d->m_dirtyBox = true; + std::reverse(begin(), end()); +} + GeoDataLineString GeoDataLineString::toNormalized() const { GeoDataLineString normalizedLineString; normalizedLineString.setTessellationFlags( tessellationFlags() ); qreal lon; qreal lat; // FIXME: Think about how we can avoid unnecessary copies // if the linestring stays the same. QVector::const_iterator end = p()->m_vector.constEnd(); for( QVector::const_iterator itCoords = p()->m_vector.constBegin(); itCoords != end; ++itCoords ) { itCoords->geoCoordinates( lon, lat ); qreal alt = itCoords->altitude(); GeoDataCoordinates::normalizeLonLat( lon, lat ); GeoDataCoordinates normalizedCoords( *itCoords ); normalizedCoords.set( lon, lat, alt ); normalizedLineString << normalizedCoords; } return normalizedLineString; } GeoDataLineString GeoDataLineString::toRangeCorrected() const { if ( p()->m_dirtyRange ) { delete p()->m_rangeCorrected; if( isClosed() ) { p()->m_rangeCorrected = new GeoDataLinearRing( toPoleCorrected() ); } else { p()->m_rangeCorrected = new GeoDataLineString( toPoleCorrected() ); } p()->m_dirtyRange = false; } return *p()->m_rangeCorrected; } QVector GeoDataLineString::toDateLineCorrected() const { QVector lineStrings; p()->toDateLineCorrected( *this, lineStrings ); return lineStrings; } GeoDataLineString GeoDataLineString::toPoleCorrected() const { if( isClosed() ) { GeoDataLinearRing poleCorrected; p()->toPoleCorrected( *this, poleCorrected ); return poleCorrected; } else { GeoDataLineString poleCorrected; p()->toPoleCorrected( *this, poleCorrected ); return poleCorrected; } } void GeoDataLineStringPrivate::toPoleCorrected( const GeoDataLineString& q, GeoDataLineString& poleCorrected ) const { poleCorrected.setTessellationFlags( q.tessellationFlags() ); GeoDataCoordinates previousCoords; GeoDataCoordinates currentCoords; if ( q.isClosed() ) { if ( !( m_vector.first().isPole() ) && ( m_vector.last().isPole() ) ) { qreal firstLongitude = ( m_vector.first() ).longitude(); GeoDataCoordinates modifiedCoords( m_vector.last() ); modifiedCoords.setLongitude( firstLongitude ); poleCorrected << modifiedCoords; } } QVector::const_iterator itCoords = m_vector.constBegin(); QVector::const_iterator itEnd = m_vector.constEnd(); for( ; itCoords != itEnd; ++itCoords ) { currentCoords = *itCoords; if ( itCoords == m_vector.constBegin() ) { previousCoords = currentCoords; } if ( currentCoords.isPole() ) { if ( previousCoords.isPole() ) { continue; } else { qreal previousLongitude = previousCoords.longitude(); GeoDataCoordinates currentModifiedCoords( currentCoords ); currentModifiedCoords.setLongitude( previousLongitude ); poleCorrected << currentModifiedCoords; } } else { if ( previousCoords.isPole() ) { qreal currentLongitude = currentCoords.longitude(); GeoDataCoordinates previousModifiedCoords( previousCoords ); previousModifiedCoords.setLongitude( currentLongitude ); poleCorrected << previousModifiedCoords; poleCorrected << currentCoords; } else { // No poles at all. Nothing special to handle poleCorrected << currentCoords; } } previousCoords = currentCoords; } if ( q.isClosed() ) { if ( ( m_vector.first().isPole() ) && !( m_vector.last().isPole() ) ) { qreal lastLongitude = ( m_vector.last() ).longitude(); GeoDataCoordinates modifiedCoords( m_vector.first() ); modifiedCoords.setLongitude( lastLongitude ); poleCorrected << modifiedCoords; } } } void GeoDataLineStringPrivate::toDateLineCorrected( const GeoDataLineString & q, QVector & lineStrings ) const { const bool isClosed = q.isClosed(); const QVector::const_iterator itStartPoint = q.constBegin(); const QVector::const_iterator itEndPoint = q.constEnd(); QVector::const_iterator itPoint = itStartPoint; QVector::const_iterator itPreviousPoint = itPoint; TessellationFlags f = q.tessellationFlags(); GeoDataLineString * unfinishedLineString = 0; GeoDataLineString * dateLineCorrected = isClosed ? new GeoDataLinearRing( f ) : new GeoDataLineString( f ); qreal currentLon = 0.0; qreal previousLon = 0.0; int previousSign = 1; bool unfinished = false; for (; itPoint != itEndPoint; ++itPoint ) { currentLon = itPoint->longitude(); int currentSign = ( currentLon < 0.0 ) ? -1 : +1 ; if( itPoint == q.constBegin() ) { previousSign = currentSign; previousLon = currentLon; } // If we are crossing the date line ... if ( previousSign != currentSign && fabs(previousLon) + fabs(currentLon) > M_PI ) { unfinished = !unfinished; GeoDataCoordinates previousTemp; GeoDataCoordinates currentTemp; interpolateDateLine( *itPreviousPoint, *itPoint, previousTemp, currentTemp, q.tessellationFlags() ); *dateLineCorrected << previousTemp; if ( isClosed && unfinished ) { // If it's a linear ring and if it crossed the IDL only once then // store the current string inside the unfinishedLineString for later use ... unfinishedLineString = dateLineCorrected; // ... and start a new linear ring for now. dateLineCorrected = new GeoDataLinearRing( f ); } else { // Now it can only be a (finished) line string or a finished linear ring. // Store it in the vector if the size is not zero. if ( dateLineCorrected->size() > 0 ) { lineStrings << dateLineCorrected; } else { // Or delete it. delete dateLineCorrected; } // If it's a finished linear ring restore the "remembered" unfinished String if ( isClosed && !unfinished && unfinishedLineString ) { dateLineCorrected = unfinishedLineString; } else { // if it's a line string just create a new line string. dateLineCorrected = new GeoDataLineString( f ); } } *dateLineCorrected << currentTemp; *dateLineCorrected << *itPoint; } else { *dateLineCorrected << *itPoint; } previousSign = currentSign; previousLon = currentLon; itPreviousPoint = itPoint; } // If the line string doesn't cross the dateline an even number of times // then need to take care of the data stored in the unfinishedLineString if ( unfinished && unfinishedLineString && !unfinishedLineString->isEmpty() ) { *dateLineCorrected << *unfinishedLineString; delete unfinishedLineString; } lineStrings << dateLineCorrected; } const GeoDataLatLonAltBox& GeoDataLineString::latLonAltBox() const { // GeoDataLatLonAltBox::fromLineString is very expensive // that's why we recreate it only if the m_dirtyBox // is TRUE. // DO NOT REMOVE THIS CONSTRUCT OR MARBLE WILL BE SLOW. if ( p()->m_dirtyBox ) { p()->m_latLonAltBox = GeoDataLatLonAltBox::fromLineString( *this ); } p()->m_dirtyBox = false; return p()->m_latLonAltBox; } qreal GeoDataLineString::length( qreal planetRadius, int offset ) const { if( offset < 0 || offset >= size() ) { return 0; } qreal length = 0.0; QVector const & vector = p()->m_vector; int const start = qMax(offset+1, 1); int const end = p()->m_vector.size(); for( int i=start; i::Iterator GeoDataLineString::erase ( QVector::Iterator pos ) { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); delete d->m_rangeCorrected; d->m_rangeCorrected = 0; d->m_dirtyRange = true; d->m_dirtyBox = true; return d->m_vector.erase( pos ); } QVector::Iterator GeoDataLineString::erase ( QVector::Iterator begin, QVector::Iterator end ) { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); delete d->m_rangeCorrected; d->m_rangeCorrected = 0; d->m_dirtyRange = true; d->m_dirtyBox = true; return d->m_vector.erase( begin, end ); } void GeoDataLineString::remove ( int i ) { GeoDataGeometry::detach(); GeoDataLineStringPrivate* d = p(); d->m_dirtyRange = true; d->m_dirtyBox = true; d->m_vector.remove( i ); } GeoDataLineString GeoDataLineString::optimized () const { if( isClosed() ) { GeoDataLinearRing linearRing(*this); p()->optimize(linearRing); return linearRing; } else { GeoDataLineString lineString(*this); p()->optimize(lineString); return lineString; } } void GeoDataLineString::pack( QDataStream& stream ) const { GeoDataGeometry::pack( stream ); stream << size(); stream << (qint32)(p()->m_tessellationFlags); for( QVector::const_iterator iterator = p()->m_vector.constBegin(); iterator != p()->m_vector.constEnd(); ++iterator ) { mDebug() << "innerRing: size" << p()->m_vector.size(); GeoDataCoordinates coord = ( *iterator ); coord.pack( stream ); } } void GeoDataLineString::unpack( QDataStream& stream ) { GeoDataGeometry::detach(); GeoDataGeometry::unpack( stream ); qint32 size; qint32 tessellationFlags; stream >> size; stream >> tessellationFlags; p()->m_tessellationFlags = (TessellationFlags)(tessellationFlags); p()->m_vector.reserve(p()->m_vector.size() + size); for(qint32 i = 0; i < size; i++ ) { GeoDataCoordinates coord; coord.unpack( stream ); p()->m_vector.append( coord ); } } } diff --git a/src/lib/marble/geodata/data/GeoDataLineString.h b/src/lib/marble/geodata/data/GeoDataLineString.h index 5973c16b2..54eacf666 100644 --- a/src/lib/marble/geodata/data/GeoDataLineString.h +++ b/src/lib/marble/geodata/data/GeoDataLineString.h @@ -1,393 +1,397 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2008-2009 Torsten Rahn // Copyright 2009 Patrick Spendrin // #ifndef MARBLE_GEODATALINESTRING_H #define MARBLE_GEODATALINESTRING_H #include #include #include "MarbleGlobal.h" #include "geodata_export.h" #include "GeoDataGeometry.h" #include "GeoDataCoordinates.h" #include "GeoDataLatLonAltBox.h" namespace Marble { class GeoDataLineStringPrivate; /*! \class GeoDataLineString \brief A LineString that allows to store a contiguous set of line segments. GeoDataLineString is a tool class that implements the LineString tag/class of the Open Geospatial Consortium standard KML 2.2. GeoDataLineString extends GeoDataGeometry to store and edit LineStrings. In the QPainter API "pure" LineStrings are also referred to as "polylines". As such they are similar to the outline of a non-closed QPolygon. Whenever a LineString is painted GeoDataLineStyle should be used to assign a color and line width. A GeoDataLineString consists of several (geodetic) nodes which are each connected through line segments. The nodes are stored as GeoDataCoordinates objects. The API which provides access to the nodes is similar to the API of QVector. GeoDataLineString allows LineStrings to be tessellated in order to make them follow the terrain and the curvature of the earth. The tessellation options allow for different ways of visualization: \li Not tessellated: A LineString that connects each two nodes directly and straight in screen coordinate space. \li A tessellated line: Each line segment is bent so that the LineString follows the curvature of the earth and its terrain. A tessellated line segment connects two nodes at the shortest possible distance ("along great circles"). \li A tessellated line that follows latitude circles whenever possible: In this case Latitude circles are followed as soon as two subsequent nodes have exactly the same amount of latitude. In all other places the line segments follow great circles. Some convenience methods have been added that allow to calculate the geodesic bounding box or the length of a LineString. */ class GEODATA_EXPORT GeoDataLineString : public GeoDataGeometry { public: typedef QVector::Iterator Iterator; typedef QVector::ConstIterator ConstIterator; typedef QVector::const_iterator const_iterator; /*! \brief Creates a new LineString. */ explicit GeoDataLineString( TessellationFlags f = NoTessellation ); /*! \brief Creates a LineString from an existing geometry object. */ explicit GeoDataLineString( const GeoDataGeometry &other ); /*! \brief Destroys a LineString. */ virtual ~GeoDataLineString(); /*! \brief Returns whether a LineString is a closed polygon. \return false if the LineString is not a LinearRing. */ virtual bool isClosed() const; /*! \brief Returns whether the LineString follows the earth's surface. \return true if the LineString's line segments follow the earth's surface and terrain along great circles. */ bool tessellate() const; /*! \brief Sets the tessellation property for the LineString. If \a tessellate is true then the LineString's line segments are bent and follow the earth's surface and terrain along great circles. If \a tessellate is false then the LineString's line segments are rendered as straight lines in screen coordinate space. */ void setTessellate( bool tessellate ); /*! \brief Returns the tessellation flags for a LineString. */ TessellationFlags tessellationFlags() const; /*! \brief Sets the given tessellation flags for a LineString. */ void setTessellationFlags( TessellationFlags f ); +/*! + \brief Reverses the LineString. +*/ + void reverse(); /*! \brief Returns the smallest latLonAltBox that contains the LineString. \see GeoDataLatLonAltBox */ virtual const GeoDataLatLonAltBox& latLonAltBox() const; /** * @brief Returns the length of LineString across a sphere starting from a coordinate in LineString * This method can be used as an approximation for distances along LineStrings. * The unit used for the resulting length matches the unit of the planet * radius. * @param planetRadius radius of the sphere * @param offset position of coordinate within LineString */ virtual qreal length( qreal planetRadius, int offset = 0 ) const; /*! \brief Provides a more generic representation of the LineString. The LineString is normalized, and pole corrected. Deprecation Warning: This method will likely be removed from the public API. */ virtual GeoDataLineString toRangeCorrected() const; /*! \brief The line string with nodes that have proper longitude/latitude ranges. \return A LineString that resembles the original linestring with nodes that have longitude values between -180 and +180 deg and that feature latitude values between -90 and +90 deg. Deprecation Warning: This method will likely be removed from the public API. */ virtual GeoDataLineString toNormalized() const; /*! \brief The line string with more generic pole values. \return A LineString that resembles the original linestring. Nodes that represent one of the poles are duplicated to allow for a better visualization of flat projections. Deprecation Warning: This method will likely be removed from the public API. */ virtual GeoDataLineString toPoleCorrected() const; /*! \brief The line string corrected for date line crossing. \return A set of LineStrings that don't cross the dateline and which resemble the original linestring. Deprecation Warning: This method will likely be removed from the public API. */ virtual QVector toDateLineCorrected() const; // "Reimplementation" of QVector API /*! \brief Returns whether the LineString has no nodes at all. \return true if there are no nodes inside the line string. */ bool isEmpty() const; /*! \brief Returns the number of nodes in a LineString. */ int size() const; /*! \brief Returns a reference to the coordinates of a node at a given position. This method detaches the returned coordinate object from the line string. */ GeoDataCoordinates& at( int pos ); /*! \brief Returns a reference to the coordinates of a node at a given position. This method does not detach the returned coordinate object from the line string. */ const GeoDataCoordinates& at( int pos ) const; /*! \brief Returns a reference to the coordinates of a node at a given position. This method detaches the returned coordinate object from the line string. */ GeoDataCoordinates& operator[]( int pos ); /*! \brief Returns a reference to the coordinates of a node at a given position. This method does not detach the returned coordinate object from the line string. */ const GeoDataCoordinates& operator[]( int pos ) const; /*! \brief Returns a reference to the first node in the LineString. This method detaches the returned coordinate object from the line string. */ GeoDataCoordinates& first(); /*! \brief Returns a reference to the first node in the LineString. This method does not detach the returned coordinate object from the line string. */ const GeoDataCoordinates& first() const; /*! \brief Returns a reference to the last node in the LineString. This method detaches the returned coordinate object from the line string. */ GeoDataCoordinates& last(); /*! \brief Returns a reference to the last node in the LineString. This method does not detach the returned coordinate object from the line string. */ const GeoDataCoordinates& last() const; /*! \brief Inserts a new node at the given index. */ void insert( int index, const GeoDataCoordinates& value ); /*! \brief Appends a given geodesic position as a new node to the LineString. */ void append ( const GeoDataCoordinates& value ); /*! \brief Appends a given geodesic position as new nodes to the LineString. */ void append(const QVector& values); /*! \brief Appends a given geodesic position as a new node to the LineString. */ GeoDataLineString& operator << ( const GeoDataCoordinates& value ); /*! \brief Appends a given LineString to the end of the LineString. */ GeoDataLineString& operator << ( const GeoDataLineString& lineString ); /*! \brief Returns true/false depending on whether this and other are/are not equal. */ bool operator==( const GeoDataLineString &other ) const; bool operator!=( const GeoDataLineString &other ) const; /*! \brief Returns an iterator that points to the begin of the LineString. */ QVector::Iterator begin(); QVector::ConstIterator begin() const; /*! \brief Returns an iterator that points to the end of the LineString. */ QVector::Iterator end(); QVector::ConstIterator end() const; /*! \brief Returns a const iterator that points to the begin of the LineString. */ QVector::ConstIterator constBegin() const; /*! \brief Returns a const iterator that points to the end of the LineString. */ QVector::ConstIterator constEnd() const; /*! \brief Destroys all nodes in a LineString. */ void clear(); /*! \brief Removes the node at the given position and returns it. */ QVector::Iterator erase ( QVector::Iterator position ); /*! \brief Removes the nodes within the given range and returns them. */ QVector::Iterator erase ( QVector::Iterator begin, QVector::Iterator end ); /*! \brief Removes the node at the given position and destroys it. */ void remove ( int i ); /*! \brief Returns a linestring with detail values assigned to each node. */ GeoDataLineString optimized() const; // Serialization /*! \brief Serialize the LineString to a stream. \param stream the stream. */ virtual void pack( QDataStream& stream ) const; /*! \brief Unserialize the LineString from a stream. \param stream the stream. */ virtual void unpack( QDataStream& stream ); protected: explicit GeoDataLineString(GeoDataLineStringPrivate* priv); private: GeoDataLineStringPrivate *p(); const GeoDataLineStringPrivate *p() const; }; } Q_DECLARE_METATYPE( Marble::GeoDataLineString ) #endif diff --git a/tools/osm-simplify/BaseFilter.cpp b/tools/osm-simplify/BaseFilter.cpp index bfe6f73f3..7649c19a1 100644 --- a/tools/osm-simplify/BaseFilter.cpp +++ b/tools/osm-simplify/BaseFilter.cpp @@ -1,31 +1,41 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2016 David Kolozsvari // #include "BaseFilter.h" #include "GeoDataDocument.h" #include "GeoDataGeometry.h" #include "GeoDataObject.h" #include "GeoDataTypes.h" BaseFilter::BaseFilter(GeoDataDocument* document, const char *type) : m_document(document) { foreach (GeoDataFeature* feature, m_document->featureList()) { if(feature->nodeType() == type) { m_objects.append(feature); } } } BaseFilter::~BaseFilter() { } + +QList::const_iterator BaseFilter::objectsBegin() const +{ + return m_objects.begin(); +} + +QList::const_iterator BaseFilter::objectsEnd() const +{ + return m_objects.end(); +} diff --git a/tools/osm-simplify/BaseFilter.h b/tools/osm-simplify/BaseFilter.h index 9c361f49c..3e3ce7dc2 100644 --- a/tools/osm-simplify/BaseFilter.h +++ b/tools/osm-simplify/BaseFilter.h @@ -1,36 +1,39 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2016 David Kolozsvari // #ifndef OBJECTHANDLER_H #define OBJECTHANDLER_H #include #include #include "GeoDataObject.h" #include "GeoDataDocument.h" #include "GeoDataTypes.h" using namespace Marble; class BaseFilter { public: BaseFilter(GeoDataDocument* document, const char *type); virtual ~BaseFilter(); virtual void process() = 0; + QList::const_iterator objectsBegin() const; + QList::const_iterator objectsEnd() const; + protected: GeoDataDocument* m_document; QList m_objects; }; #endif // OBJECTHANDLER_H diff --git a/tools/osm-simplify/CMakeLists.txt b/tools/osm-simplify/CMakeLists.txt index cc43b2887..4220eb72e 100644 --- a/tools/osm-simplify/CMakeLists.txt +++ b/tools/osm-simplify/CMakeLists.txt @@ -1,35 +1,38 @@ cmake_minimum_required(VERSION 2.8.12) SET (TARGET osm-simplify) PROJECT (${TARGET}) find_package(Qt5Core REQUIRED) find_package(Qt5Widgets REQUIRED) find_package(Qt5Gui REQUIRED) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ../../src/lib/marble/osm ../../src/lib/marble/geodata/writer ../../src/lib/marble/geodata/parser ../../src/lib/marble/geodata/data ../../src/lib/marble/geodata ../../src/lib/marble/ ) set( ${TARGET}_SRC main.cpp BaseClipper.cpp BaseFilter.cpp PlacemarkFilter.cpp ShpCoastlineProcessor.cpp LineStringProcessor.cpp TinyPlanetProcessor.cpp NodeReducer.cpp + TagsFilter.cpp + WayConcatenator.cpp +WayChunk.cpp ) add_definitions( -DMAKE_MARBLE_LIB ) add_executable( ${TARGET} ${${TARGET}_SRC} ) target_link_libraries( ${TARGET} Qt5::Core marblewidget-qt5) diff --git a/tools/osm-simplify/TagsFilter.cpp b/tools/osm-simplify/TagsFilter.cpp new file mode 100644 index 000000000..5024924f0 --- /dev/null +++ b/tools/osm-simplify/TagsFilter.cpp @@ -0,0 +1,95 @@ +// +// This file is part of the Marble Virtual Globe. +// +// This program is free software licensed under the GNU LGPL. You can +// find a copy of this license in LICENSE.txt in the top directory of +// the source code. +// +// Copyright 2016 Akshat Tandon +// + +#include +#include + +#include "PlacemarkFilter.h" +#include "TagsFilter.h" +#include "GeoDataObject.h" +#include "GeoDataDocument.h" +#include "OsmPlacemarkData.h" +#include "GeoDataPlacemark.h" + +namespace Marble{ + +TagsFilter::TagsFilter(GeoDataDocument *document, const QStringList &tagsList, bool andFlag ) : PlacemarkFilter(document) +{ + int total=0, tagCount=0; + // qDebug()<<"Entered tagFilter"; + QList previousObjects(m_objects); + m_objects.clear(); + foreach (GeoDataObject *object, previousObjects) { + ++total; + GeoDataPlacemark *placemark = static_cast(object); + bool flag = andFlag; + QStringList::const_iterator itr = tagsList.begin(); + for (; itr != tagsList.end(); ++itr) { + QStringList currentTag = (*itr).split('='); + QString currentKey; + QString currentValue; + if (currentTag.size() != 2) { + qDebug()<< "Invalid tag : "<< currentTag<<" ,rejecting it"<osmData().containsTagKey(currentKey); + } else { + contains = placemark->osmData().containsTag(currentKey, currentValue); + } + if (!contains) { + if (andFlag) { + flag = false; + break; + } + } else { + if (!andFlag) { + flag = true; + break; + } + } + } + if (flag) { + ++tagCount; + // qDebug()<<"Contained tag"; + m_objects.append(object); + // qDebug()<<"ID "<osmData().id(); + } else { + m_rejectedObjects.append(object); + } + + } + // qDebug()<<"Done TagFiltering"; + // qDebug()<<"Total"<::const_iterator TagsFilter::rejectedObjectsBegin() const +{ + return m_rejectedObjects.begin(); +} + +QList::const_iterator TagsFilter::rejectedObjectsEnd() const +{ + return m_rejectedObjects.end(); +} diff --git a/tools/osm-simplify/TagsFilter.h b/tools/osm-simplify/TagsFilter.h new file mode 100644 index 000000000..9e2bf3fd2 --- /dev/null +++ b/tools/osm-simplify/TagsFilter.h @@ -0,0 +1,37 @@ +// +// This file is part of the Marble Virtual Globe. +// +// This program is free software licensed under the GNU LGPL. You can +// find a copy of this license in LICENSE.txt in the top directory of +// the source code. +// +// Copyright 2016 Akshat Tandon +// + + +#ifndef MARBLE_TAGSFILTER_H +#define MARBLE_TAGSFILTER_H + +#include "PlacemarkFilter.h" +#include +#include + + +namespace Marble{ + +class GeoDataDocument; + +class TagsFilter : public PlacemarkFilter +{ +public: + //Filters placemarks which have tags in the hash + TagsFilter(GeoDataDocument* document, const QStringList& tagsList, bool andFlag = false); + virtual void process(); + QList::const_iterator rejectedObjectsBegin() const; + QList::const_iterator rejectedObjectsEnd() const; +private: + QList m_rejectedObjects; +}; + +} +#endif diff --git a/tools/osm-simplify/WayChunk.cpp b/tools/osm-simplify/WayChunk.cpp new file mode 100644 index 000000000..dd9f77b61 --- /dev/null +++ b/tools/osm-simplify/WayChunk.cpp @@ -0,0 +1,129 @@ +// +// This file is part of the Marble Virtual Globe. +// +// This program is free software licensed under the GNU LGPL. You can +// find a copy of this license in LICENSE.txt in the top directory of +// the source code. +// +// Copyright 2016 Akshat Tandon +// + +#include +#include +#include + +#include "WayChunk.h" +#include "GeoDataCoordinates.h" +#include "GeoDataFeature.h" +#include "GeoDataPlacemark.h" +#include "GeoDataLineString.h" +#include "OsmPlacemarkData.h" + +namespace Marble +{ + +WayChunk::WayChunk(GeoDataPlacemark *placemark, qint64 first, qint64 last) +{ + m_wayList.append(placemark); + m_first = first; + m_last = last; + m_visualCategory = placemark->visualCategory(); +} + +WayChunk::~WayChunk() +{ + +} + +qint64 WayChunk::first() const +{ + return m_first; +} + +qint64 WayChunk::last() const +{ + return m_last; +} + +void WayChunk::append(GeoDataPlacemark *placemark, qint64 last) +{ + m_wayList.append(placemark); + m_last = last; + +} + +void WayChunk::prepend(GeoDataPlacemark *placemark, qint64 first) +{ + m_wayList.prepend(placemark); + m_first = first; + +} + +void WayChunk::append(WayChunk *chunk) +{ + m_wayList << chunk->m_wayList; + m_last = chunk->last(); +} + +GeoDataPlacemark* WayChunk::merge() +{ + Q_ASSERT(!m_wayList.isEmpty()); + + GeoDataPlacemark *placemark = new GeoDataPlacemark(*(m_wayList.first())); + GeoDataLineString *line = static_cast(placemark->geometry()); + QList::iterator itr = m_wayList.begin(); + QList::iterator itrEnd = m_wayList.end(); + ++itr; + for (; itr != itrEnd; ++itr) { + GeoDataLineString *currentLine = static_cast( (*itr)->geometry() ); + currentLine->remove(0); + (*line) << *currentLine; + } + //qDebug()<<"Merging placemark"; + return placemark; +} + +void WayChunk::reverse() +{ + std::reverse(m_wayList.begin(), m_wayList.end()); + QList::iterator itr = m_wayList.begin(); + for (; itr != m_wayList.end(); ++itr) { + GeoDataPlacemark *placemark = *itr; + GeoDataLineString *line = static_cast(placemark->geometry()); + line->reverse(); + } + qSwap(m_first, m_last); +} + +qint64 WayChunk::id() const +{ + return m_wayList.first()->osmData().id(); +} + +void WayChunk::printIds() const +{ + QList::const_iterator itr = m_wayList.begin(); + qDebug()<<"IDs of placemarks in chunk"; + for (; itr != m_wayList.end(); ++itr) { + qDebug()<<"Id :- "<<(*itr)->osmData().id(); + } +} + +int WayChunk::size() const +{ + return m_wayList.size(); +} + +bool WayChunk::concatPossible(GeoDataPlacemark *placemark) const +{ + GeoDataFeature::GeoDataVisualCategory category = placemark->visualCategory(); + return (category == m_visualCategory); +} + +GeoDataFeature::GeoDataVisualCategory WayChunk::visualCategory() const +{ + return m_visualCategory; +} + + +} diff --git a/tools/osm-simplify/WayChunk.h b/tools/osm-simplify/WayChunk.h new file mode 100644 index 000000000..1423c7ab4 --- /dev/null +++ b/tools/osm-simplify/WayChunk.h @@ -0,0 +1,60 @@ +// +// This file is part of the Marble Virtual Globe. +// +// This program is free software licensed under the GNU LGPL. You can +// find a copy of this license in LICENSE.txt in the top directory of +// the source code. +// +// Copyright 2016 Akshat Tandon +// + +#ifndef MARBLE_WAYCHUNK_H +#define MARBLE_WAYCHUNK_H + +#include + +#include "GeoDataFeature.h" +#include "GeoDataLineString.h" + + +namespace Marble +{ + +class GeoDataPlacemark; + +class WayChunk +{ +public: + WayChunk(GeoDataPlacemark *placemark, qint64 first, qint64 last ); + ~WayChunk(); + void append(GeoDataPlacemark *placemark, qint64 last); + void append(WayChunk *chunk); + void prepend(GeoDataPlacemark *placemark, qint64 first); + + /* + * Creates a new placemark object by concatenating all the linsetrings which exist in the WayChunk + * Caller has the responsibility of deleting the object. + */ + GeoDataPlacemark* merge(); + + qint64 first() const; + qint64 last() const; + void reverse(); + qint64 id() const; + void printIds() const; + int size() const; + bool concatPossible(GeoDataPlacemark *placemark) const; + GeoDataFeature::GeoDataVisualCategory visualCategory() const; + +private: + QList m_wayList; + qint64 m_first; + qint64 m_last; + GeoDataFeature::GeoDataVisualCategory m_visualCategory; + +}; + +} + +#endif + diff --git a/tools/osm-simplify/WayConcatenator.cpp b/tools/osm-simplify/WayConcatenator.cpp new file mode 100644 index 000000000..df1671fd5 --- /dev/null +++ b/tools/osm-simplify/WayConcatenator.cpp @@ -0,0 +1,344 @@ +// +// This file is part of the Marble Virtual Globe. +// +// This program is free software licensed under the GNU LGPL. You can +// find a copy of this license in LICENSE.txt in the top directory of +// the source code. +// +// Copyright 2016 Akshat Tandon +// + +#include +#include + +#include "GeoDataPlacemark.h" +#include "GeoDataDocument.h" +#include "GeoDataObject.h" +#include "OsmPlacemarkData.h" +#include "StyleBuilder.h" + +#include "WayConcatenator.h" +#include "WayChunk.h" +#include "TagsFilter.h" + +namespace Marble +{ + +WayConcatenator::WayConcatenator(GeoDataDocument *document, const QStringList &tagsList, bool andFlag) : TagsFilter(document, tagsList, andFlag) +{ + // qDebug()<< "Entered WayConcatenator"; +} + +WayConcatenator::~WayConcatenator() +{ + QVector::iterator itr = m_chunks.begin(); + for (; itr != m_chunks.end(); ++itr) { + delete *itr; + } +} + +void WayConcatenator::process() +{ + qint64 count = 0; + qint64 chunkCount = 0; + qint64 newCount = 0; + qint64 placemarkCount = 0; + + // qDebug()<<"** Number of TagFiletered placemarks "<< m_objects.size(); + foreach (GeoDataObject *object, m_objects) { + GeoDataPlacemark *placemark = static_cast(object); + + qDebug()<<" "; + ++placemarkCount; + // qDebug()<<"No."<geometry()->nodeType() == GeoDataTypes::GeoDataLineStringType) { + qDebug()<<"-- Placemark ID : "<osmData().id()<<" visualCategory: "<visualCategory()); + GeoDataLineString *line = static_cast(placemark->geometry()); + qint64 firstId = placemark->osmData().nodeReference(line->first()).id(); + qint64 lastId = placemark->osmData().nodeReference(line->last()).id(); + + bool containsFirst = m_hash.contains(firstId); + bool containsLast = m_hash.contains(lastId); + + if (!containsFirst && !containsLast) { + qDebug()<<"No coords matched, creating a new chunk"; + createWayChunk(placemark, firstId, lastId); + ++count; + ++chunkCount; + } else if (containsFirst && !containsLast) { + qDebug()<<"First coord matched"; + WayChunk *chunk = getWayChunk(placemark, firstId); + if (chunk != nullptr) { + // qDebug()<< "First* Chunk found, concatenating to it"; + concatFirst(placemark, chunk); + } else { + // qDebug()<<""; + qDebug()<< "First* No possible chunk found, creating a new chunk"; + qDebug()<<"FirstId"<::const_iterator itr = rejectedObjectsBegin(); + QList::const_iterator endItr = rejectedObjectsEnd(); + for (; itr != endItr; ++itr) { + GeoDataPlacemark *placemark = static_cast(*itr); + m_placemarks.append(*placemark); + } +} + +void WayConcatenator::addWayChunks() +{ + qint64 totalSize = 0; + QSet chunkSet; + // QList chunkList = m_hash.values(); + // QList::iterator cItr = chunkList.begin(); + // qDebug()<<"* Chunk list size = "<::iterator itr = m_chunks.begin(); + for (; itr != m_chunks.end(); ++itr) { + if (!chunkSet.contains(*itr)) { + chunkSet.insert(*itr); + GeoDataPlacemark* placemark = (*itr)->merge(); + if (placemark) { + m_placemarks.append(*placemark); + totalSize += (*itr)->size(); + qDebug()<<"Chunk:"; + (*itr)->printIds(); + qDebug()<<"Size of this chunk"<<(*itr)->size(); + qDebug()<<"Merged"; + qDebug()<<" "; + delete placemark; + } + } + } + qDebug()<<"*** Total number of ways merged"<clear(); + QList::iterator itr; + itr = m_placemarks.begin(); + for (; itr != m_placemarks.end(); ++itr) { + GeoDataPlacemark *placemark = new GeoDataPlacemark(*itr); + m_document->append(placemark); + } +} + +void WayConcatenator::createWayChunk(GeoDataPlacemark *placemark, qint64 firstId, qint64 lastId) +{ + WayChunk *chunk = new WayChunk(placemark, firstId, lastId); + m_hash.insert(firstId, chunk); + if (firstId != lastId) { + m_hash.insert(lastId, chunk); + } + m_chunks.append(chunk); +} + +WayChunk* WayConcatenator::getWayChunk(GeoDataPlacemark *placemark, qint64 matchId) +{ + qDebug()<<"Searching for a compatible WayChunk"; + qDebug()<<"Visual category for placemark"<visualCategory()); + + QHash::iterator matchItr = m_hash.find(matchId); + while (matchItr != m_hash.end() && matchItr.key() == matchId) { + WayChunk *chunk = matchItr.value(); + qDebug()<<" * Chunk ID: "<id()<<" Visual category for chunk"<visualCategory()); + if (chunk->concatPossible(placemark)) { + qDebug()<<"Match found"; + return chunk; + } + ++matchItr; + } + qDebug()<<"### No Chunk found, returning nullptr"; + return nullptr; +} + +void WayConcatenator::concatFirst(GeoDataPlacemark *placemark, WayChunk *chunk) +{ + qDebug()<<"First coord matched"; + qDebug()<<"Matched with: "; + chunk->printIds(); + + GeoDataLineString *line = static_cast(placemark->geometry()); + qint64 firstId = placemark->osmData().nodeReference(line->first()).id(); + qint64 lastId = placemark->osmData().nodeReference(line->last()).id(); + + if (chunk->first() != chunk->last()) { + int chunksRemoved = m_hash.remove(firstId, chunk); + Q_ASSERT(chunksRemoved == 1); + } + m_hash.insert(lastId, chunk); + + if (firstId == chunk->last()) { + //First node matches with an existing last node + qDebug()<<"Appended chunk"; + chunk->append(placemark, lastId); + } else { + //First node matches with an existing first node + //Reverse the GeoDataLineString of the placemark + line->reverse(); + chunk->prepend(placemark, lastId); + qDebug()<<"Reversed line and then prepended"; + } + +} + +void WayConcatenator::concatLast(GeoDataPlacemark *placemark, WayChunk *chunk) +{ + qDebug()<<"Last coord matched"; + qDebug()<<"Matched with: "; + chunk->printIds(); + + GeoDataLineString *line = static_cast(placemark->geometry()); + qint64 firstId = placemark->osmData().nodeReference(line->first()).id(); + qint64 lastId = placemark->osmData().nodeReference(line->last()).id(); + + if (chunk->first() != chunk->last()) { + int chunksRemoved = m_hash.remove(lastId, chunk); + Q_ASSERT(chunksRemoved == 1); + } + m_hash.insert(firstId, chunk); + + if (lastId == chunk->first()) { + qDebug()<<"Prepended chunk"; + chunk->prepend(placemark, firstId); + } else { + line->reverse(); + chunk->append(placemark, firstId); + qDebug()<<"Reversed line and then appended"; + } + +} + +void WayConcatenator::concatBoth(GeoDataPlacemark *placemark, WayChunk *chunk, WayChunk *otherChunk) +{ + + qDebug()<<" Concat possible"; + qDebug()<<"Inserting in the middle"; + qDebug()<<"Matched first coord with: "; + chunk->printIds(); + qDebug()<<"Matched last coord with"; + otherChunk->printIds(); + + GeoDataLineString *line = static_cast(placemark->geometry()); + qint64 firstId = placemark->osmData().nodeReference(line->first()).id(); + qint64 lastId = placemark->osmData().nodeReference(line->last()).id(); + + int chunksRemoved; + if (chunk->first() != chunk->last()) { + chunksRemoved = m_hash.remove(firstId, chunk); + Q_ASSERT(chunksRemoved == 1); + } + + if (firstId == chunk->first()) { + chunk->reverse(); + } + + chunk->append(placemark, lastId); + + if (lastId == otherChunk->last()) { + qDebug()<<" otherChunk reversed"; + otherChunk->reverse(); + } + chunk->append(otherChunk); + + chunksRemoved = m_hash.remove(otherChunk->first(), otherChunk); + Q_ASSERT(chunksRemoved == 1); + + if (otherChunk->first() != otherChunk->last()) { + chunksRemoved = m_hash.remove(otherChunk->last(), otherChunk); + Q_ASSERT(chunksRemoved == 1); + } + + m_hash.insert(otherChunk->last(), chunk); + + m_chunks.removeOne(otherChunk); + +} + + + +} diff --git a/tools/osm-simplify/WayConcatenator.h b/tools/osm-simplify/WayConcatenator.h new file mode 100644 index 000000000..394f1503b --- /dev/null +++ b/tools/osm-simplify/WayConcatenator.h @@ -0,0 +1,45 @@ +// +// This file is part of the Marble Virtual Globe. +// +// This program is free software licensed under the GNU LGPL. You can +// find a copy of this license in LICENSE.txt in the top directory of +// the source code. +// +// Copyright 2016 Akshat Tandon +// + +#ifndef MARBLE_WAYCONCATENATOR_H +#define MARBLE_WAYCONCATENATOR_H + +#include "PlacemarkFilter.h" +#include "TagsFilter.h" + +namespace Marble +{ +class WayChunk; + +class WayConcatenator : public TagsFilter +{ +public: + WayConcatenator(GeoDataDocument *document, const QStringList &tagsList, bool andFlag = false); + virtual void process(); + ~WayConcatenator(); +private: + QMultiHash m_hash; + QVector m_chunks; + QList m_placemarks; + void createWayChunk(GeoDataPlacemark *placemark, qint64 firstId, qint64 lastId); + WayChunk* getWayChunk(GeoDataPlacemark *placemark, qint64 matchId); + void concatFirst(GeoDataPlacemark *placemark, WayChunk *chunk); + void concatLast(GeoDataPlacemark *placemark, WayChunk *chunk); + void concatBoth(GeoDataPlacemark *placemark, WayChunk *chunk, WayChunk *otherChunk); + void addRejectedPlacemarks(); + void addWayChunks(); + void modifyDocument(); +}; + +} + +#endif + + diff --git a/tools/osm-simplify/main.cpp b/tools/osm-simplify/main.cpp index 3c34d3e58..e365f2f5f 100644 --- a/tools/osm-simplify/main.cpp +++ b/tools/osm-simplify/main.cpp @@ -1,397 +1,443 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2016 David Kolozsvari // #include "GeoWriter.h" #include "MarbleModel.h" #include "ParsingRunnerManager.h" #include #include #include #include #include #include #include #include #include "LineStringProcessor.h" #include "ShpCoastlineProcessor.h" #include "TinyPlanetProcessor.h" #include "NodeReducer.h" +#include "WayConcatenator.h" using namespace Marble; enum DebugLevel { Debug, Info, Mute }; DebugLevel debugLevel = Info; void debugOutput( QtMsgType type, const QMessageLogContext &context, const QString &msg ) { switch ( type ) { case QtDebugMsg: if ( debugLevel == Debug ) { qDebug() << "Debug: " << context.file << ":" << context.line << " " << msg; } break; case QtInfoMsg: if ( debugLevel < Mute ) { qInfo() << "Info: " << context.file << ":" << context.line << " " << msg; } break; case QtWarningMsg: if ( debugLevel < Mute ) { qDebug() << "Warning: " << context.file << ":" << context.line << " " << msg; } break; case QtCriticalMsg: if ( debugLevel < Mute ) { qDebug() << "Critical: " << context.file << ":" << context.line << " " << msg; } break; case QtFatalMsg: if ( debugLevel < Mute ) { qDebug() << "Fatal: " << context.file << ":" << context.line << " " << msg; abort(); } } } void usage() { qDebug() << "Usage: osm-simplify [options] input.osm output.osm"; qDebug() << "\t--no-streets-smaller-than %f - eliminates streets which have realsize smaller than %f"; } GeoDataDocument* mergeDocuments(GeoDataDocument* map1, GeoDataDocument* map2) { GeoDataDocument* mergedMap = new GeoDataDocument(*map1); foreach (GeoDataFeature* feature, map2->featureList()) { mergedMap->append(feature); } return mergedMap; } int main(int argc, char *argv[]) { QApplication app(argc, argv); QApplication::setApplicationName("osm-simplify"); QApplication::setApplicationVersion("0.1"); QCommandLineParser parser; parser.setApplicationDescription("A tool for Marble, which is used to reduce the details of osm maps."); parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("input", "The input .osm or .shp file."); parser.addOptions({ { {"d","debug"}, QCoreApplication::translate("main", "Debug output in the terminal.") }, { {"s","silent"}, QCoreApplication::translate("main", "Don't output to terminal.") }, { {"m","merge"}, QCoreApplication::translate("main", "Merge the main document with the file . This works together with the -c flag."), QCoreApplication::translate("main", "file_to_merge_with") }, { {"c", "cut-to-tiles"}, QCoreApplication::translate("main", "Cuts into tiles based on the zoom level passed using -z."), }, { {"n", "node-reduce"}, QCoreApplication::translate("main", "Reduces the number of nodes for a given way based on zoom level"), }, { {"z", "zoom-level"}, QCoreApplication::translate("main", "Zoom level according to which OSM information has to be processed."), QCoreApplication::translate("main", "number") }, + { + {"t", "tags-filter"}, + QCoreApplication::translate("main", "Tag key-value pairs which are to be be considered"), + QCoreApplication::translate("main", "k1=v1,k2=v2...") + }, + + { + {"and", "tags-and"}, + QCoreApplication::translate("main", "For a feature to be considered for processing it must contain all the specified using tags-filter"), + }, + + { + {"w", "concat-ways"}, + QCoreApplication::translate("main", "Concatenates the ways which are specified using tags-filter"), + }, + { {"o", "output"}, QCoreApplication::translate("main", "Generates an output .osmfile based on other flags. If the cut-to-tiles flag is set, then this needs to be a directory."), QCoreApplication::translate("main", "output_file.osm") } }); // Process the actual command line arguments given by the user parser.process(app); const QStringList args = parser.positionalArguments(); if (args.isEmpty()) { parser.showHelp(); return 0; } // input is args.at(0), output is args.at(1) QString inputFileName = args.at(0); QString mergeFileName = parser.value("merge"); bool debug = parser.isSet("debug"); bool silent = parser.isSet("silent"); unsigned int zoomLevel = parser.value("zoom-level").toInt(); qDebug()<<"Zoom level is "<name() << " done"; delete tile; } } } else if (file.suffix() == "osm" && parser.isSet("cut-to-tiles") && parser.isSet("merge")) { TinyPlanetProcessor processor(map); processor.process(); ShpCoastlineProcessor shpProcessor(mergeMap); shpProcessor.process(); unsigned int N = pow(2, zoomLevel); for(unsigned int x = 0; x < N; ++x) { for(unsigned int y = 0; y < N; ++y) { GeoDataDocument* tile1 = processor.cutToTiles(zoomLevel, x, y); GeoDataDocument* tile2 = shpProcessor.cutToTiles(zoomLevel, x, y); GeoDataDocument* tile = mergeDocuments(tile1, tile2); GeoWriter writer; writer.setDocumentType("0.6"); QFile outputFile; if(parser.isSet("output")) { outputFile.setFileName( QString("%1/%2/%3/%4.osm").arg(outputName).arg(zoomLevel).arg(x).arg(y) ); } else { outputFile.setFileName( QString("%1/%2/%3.osm").arg(zoomLevel).arg(x).arg(y) ); } QDir dir; if(parser.isSet("output")) { if(!dir.exists(outputName)) { dir.mkdir(outputName); } if(!dir.exists(QString("%1/%2").arg(outputName).arg(zoomLevel))) { dir.mkdir(QString("%1/%2").arg(outputName).arg(zoomLevel)); } if(!dir.exists(QString("%1/%2/%3").arg(outputName).arg(zoomLevel).arg(x))) { dir.mkdir(QString("%1/%2/%3").arg(outputName).arg(zoomLevel).arg(x)); } } else { if(!dir.exists(QString::number(zoomLevel))) { dir.mkdir(QString::number(zoomLevel)); } if(!dir.exists(QString("%1/%2").arg(zoomLevel).arg(x))) { dir.mkdir(QString("%1/%2").arg(zoomLevel).arg(x)); } } outputFile.open( QIODevice::WriteOnly ); if ( !writer.write( &outputFile, tile ) ) { qDebug() << "Could not write the file " << outputName; return 4; } qInfo() << tile->name() << " done"; delete tile1; delete tile2; delete tile; } } } else if (file.suffix() == "osm" && parser.isSet("cut-to-tiles")) { TinyPlanetProcessor processor(map); processor.process(); unsigned int N = pow(2, zoomLevel); for(unsigned int x = 0; x < N; ++x) { for(unsigned int y = 0; y < N; ++y) { GeoDataDocument* tile = processor.cutToTiles(zoomLevel, x, y); GeoWriter writer; writer.setDocumentType("0.6"); QFile outputFile; if(parser.isSet("output")) { outputFile.setFileName( QString("%1/%2/%3/%4.osm").arg(outputName).arg(zoomLevel).arg(x).arg(y) ); } else { outputFile.setFileName( QString("%1/%2/%3.osm").arg(zoomLevel).arg(x).arg(y) ); } QDir dir; if(parser.isSet("output")) { if(!dir.exists(outputName)) { dir.mkdir(outputName); } if(!dir.exists(QString("%1/%2").arg(outputName).arg(zoomLevel))) { dir.mkdir(QString("%1/%2").arg(outputName).arg(zoomLevel)); } if(!dir.exists(QString("%1/%2/%3").arg(outputName).arg(zoomLevel).arg(x))) { dir.mkdir(QString("%1/%2/%3").arg(outputName).arg(zoomLevel).arg(x)); } } else { if(!dir.exists(QString::number(zoomLevel))) { dir.mkdir(QString::number(zoomLevel)); } if(!dir.exists(QString("%1/%2").arg(zoomLevel).arg(x))) { dir.mkdir(QString("%1/%2").arg(zoomLevel).arg(x)); } } outputFile.open( QIODevice::WriteOnly ); if ( !writer.write( &outputFile, tile ) ) { qDebug() << "Could not write the file " << outputName; return 4; } qInfo() << tile->name() << " done"; delete tile; } } } else if(parser.isSet("node-reduce")) { qDebug()<<"Entered Node reduce"<