diff --git a/tools/osm-simplify/BaseClipper.cpp b/tools/osm-simplify/BaseClipper.cpp index c8c2a1661..54f241c84 100644 --- a/tools/osm-simplify/BaseClipper.cpp +++ b/tools/osm-simplify/BaseClipper.cpp @@ -1,851 +1,848 @@ // // 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 2006-2009 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2016 David Kolozsvari // #include "BaseClipper.h" #include "MarbleMath.h" #include #include BaseClipper::BaseClipper() : m_left(0.0), m_right(0.0), m_top(0.0), m_bottom(0.0), m_currentSector(4), m_previousSector(4), m_currentPoint(QPointF()), m_previousPoint(QPointF()) { } QPolygonF BaseClipper::lineString2Qpolygon(const GeoDataLineString& lineString) { QPolygonF polygon; foreach (const GeoDataCoordinates& coord, lineString) { // Need to flip the Y axis(latitude) QPointF point(coord.longitude(), -coord.latitude()); polygon.append(point); } return polygon; } QPolygonF BaseClipper::linearRing2Qpolygon(const GeoDataLinearRing& linearRing) { QPolygonF polygon; foreach (const GeoDataCoordinates& coord, linearRing) { // Need to flip the Y axis(latitude) QPointF point(coord.longitude(), -coord.latitude()); polygon.append(point); } return polygon; } GeoDataLineString BaseClipper::qPolygon2lineString(const QPolygonF& polygon) { GeoDataLineString lineString; foreach (const QPointF& point, polygon) { // Flipping back the Y axis GeoDataCoordinates coord(point.x(), -point.y()); lineString.append(coord); } return lineString; } GeoDataLinearRing BaseClipper::qPolygon2linearRing(const QPolygonF& polygon) { GeoDataLinearRing linearRing; foreach (const QPointF& point, polygon) { // Flipping back the Y axis GeoDataCoordinates coord(point.x(), -point.y()); linearRing.append(coord); } return linearRing; } qreal BaseClipper::tileX2lon( unsigned int x, unsigned int maxTileX ) { return ( (2*M_PI * x) / maxTileX - M_PI ); } qreal BaseClipper::tileY2lat( unsigned int y, unsigned int maxTileY ) { return gd( M_PI - (2*M_PI * y) / maxTileY ) * (90.0 / 85.0511); } void BaseClipper::initClipRect (const GeoDataLatLonBox &clippingBox) { m_left = clippingBox.west(); m_right = clippingBox.east(); // Had to flip the 'Y' axis, because the origo of the coordinate system in which the // geographics coordinates are is based in the bottom left corner, while the // screencoordinatas on which this clipper operated are in an upper left corner based origo. m_top = -clippingBox.north(); m_bottom = -clippingBox.south(); } qreal BaseClipper::_m( const QPointF & start, const QPointF & end ) { qreal divisor = end.x() - start.x(); - // Had to add mre zeros, because what is acceptable in screen coordinates + // Had to add more zeros, because what is acceptable in screen coordinates // could be meters on 10 meters in geographic coordinates. if ( std::fabs( divisor ) < 0.00000000000000001 ) { divisor = 0.00000000000000001 * (divisor < 0 ? -1 : 1); } return ( end.y() - start.y() ) - / divisor; + / divisor; } QPointF BaseClipper::clipTop( qreal m, const QPointF & point ) const { return QPointF( ( m_top - point.y() ) / m + point.x(), m_top ); } QPointF BaseClipper::clipLeft( qreal m, const QPointF & point ) const { return QPointF( m_left, ( m_left - point.x() ) * m + point.y() ); } QPointF BaseClipper::clipBottom( qreal m, const QPointF & point ) const { return QPointF( ( m_bottom - point.y() ) / m + point.x(), m_bottom ); } QPointF BaseClipper::clipRight( qreal m, const QPointF & point ) const { return QPointF( m_right, ( m_right - point.x() ) * m + point.y() ); } int BaseClipper::sector( const QPointF & point ) const { // If we think of the image borders as (infinitely long) parallel // lines then the plane is divided into 9 sectors. Each of these // sections is identified by a unique keynumber (currentSector): // // 0 | 1 | 2 // --+---+-- // 3 | 4 | 5 <- sector number "4" represents the onscreen sector / viewport // --+---+-- // 6 | 7 | 8 // // Figure out the section of the current point. int xSector = 1; if ( point.x() < m_left ) xSector = 0; else if ( point.x() > m_right ) xSector = 2; int ySector = 3; if ( point.y() < m_top ) ySector = 0; else if ( point.y() > m_bottom ) ySector = 6; // By adding xSector and ySector we get a // sector number of the values shown in the ASCII-art graph above. return ySector + xSector; } void BaseClipper::clipPolyObject ( const QPolygonF & polygon, QVector & clippedPolyObjects, bool isClosed ) { // Only create a new polyObject as soon as we know for sure that // the current point is on the screen. QPolygonF clippedPolyObject = QPolygonF(); const QVector::const_iterator itStartPoint = polygon.constBegin(); const QVector::const_iterator itEndPoint = polygon.constEnd(); QVector::const_iterator itPoint = itStartPoint; // 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 ... bool processingLastNode = false; while ( itPoint != itEndPoint ) { m_currentPoint = (*itPoint); // qDebug() << "m_currentPoint.x()" << m_currentPoint.x() << "m_currentPOint.y()" << m_currentPoint.y(); // Figure out the sector of the current point. m_currentSector = sector( m_currentPoint ); // Initialize the variables related to the previous point. if ( itPoint == itStartPoint && processingLastNode == false ) { if ( isClosed ) { m_previousPoint = polygon.last(); // Figure out the sector of the previous point. m_previousSector = sector( m_previousPoint ); } else { m_previousSector = m_currentSector; } } // If the current point reaches a new sector, take care of clipping. if ( m_currentSector != m_previousSector ) { if ( m_currentSector == 4 || m_previousSector == 4 ) { // In this case the current or the previous point is visible on the // screen but not both. Hence we only need to clip once and require // only one interpolation for both cases. clipOnce( clippedPolyObject, clippedPolyObjects, isClosed ); } else { // This case mostly deals with lines that reach from one // sector that is located off screen to another one that // is located off screen. In this situation the line // can get clipped once, twice, or not at all. + clipMultiple( clippedPolyObject, clippedPolyObjects, isClosed ); } m_previousSector = m_currentSector; } // If the current point is onscreen, just add it to our final polygon. if ( m_currentSector == 4 ) { clippedPolyObject << m_currentPoint; -#ifdef MARBLE_DEBUG - ++(m_debugNodeCount); -#endif } m_previousPoint = m_currentPoint; // Now let's handle the case where we have a (closed) polygon and where the // last point of the polyline is outside the viewport and the start point // is inside the viewport. This needs special treatment if ( processingLastNode ) { break; } ++itPoint; if ( itPoint == itEndPoint && isClosed ) { itPoint = itStartPoint; processingLastNode = true; } } // Only add the pointer if there's node data available. if ( !clippedPolyObject.isEmpty() ) { clippedPolyObjects << clippedPolyObject; } } void BaseClipper::clipMultiple( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, bool isClosed ) { Q_UNUSED( clippedPolyObjects ) Q_UNUSED( isClosed ) // Take care of adding nodes in the image corners if the iterator // traverses off screen sections. qreal m = _m( m_previousPoint, m_currentPoint ); switch ( m_currentSector ) { case 0: if ( m_previousSector == 5 ) { QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointRight.y() > m_top ) { clippedPolyObject << pointRight; } else { clippedPolyObject << QPointF( m_right, m_top ); } if ( pointTop.x() >= m_left && pointTop.x() < m_right ) clippedPolyObject << pointTop; if ( pointLeft.y() > m_top ) clippedPolyObject << pointLeft; } else if ( m_previousSector == 7 ) { QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointBottom.x() > m_left ) { clippedPolyObject << pointBottom; } else { clippedPolyObject << QPointF( m_left, m_bottom ); } if ( pointLeft.y() >= m_top && pointLeft.y() < m_bottom ) clippedPolyObject << pointLeft; if ( pointTop.x() > m_left ) clippedPolyObject << pointTop; } else if ( m_previousSector == 8 ) { QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointBottom.x() > m_left && pointBottom.x() < m_right ) clippedPolyObject << pointBottom; if ( pointRight.y() > m_top && pointRight.y() < m_bottom ) clippedPolyObject << pointRight; if ( pointTop.x() > m_left && pointTop.x() < m_right ) clippedPolyObject << pointTop; if ( pointLeft.y() > m_top && pointLeft.y() < m_bottom ) clippedPolyObject << pointLeft; if ( pointBottom.x() <= m_left && pointLeft.y() >= m_bottom ) clippedPolyObject << QPointF( m_left, m_bottom ); if ( pointTop.x() >= m_right && pointRight.y() <= m_top ) clippedPolyObject << QPointF( m_right, m_top ); } clippedPolyObject << QPointF( m_left, m_top ); break; case 1: if ( m_previousSector == 3 ) { QPointF pointLeft = clipLeft( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); if ( pointLeft.y() > m_top ) { clippedPolyObject << pointLeft; } else { clippedPolyObject << QPointF( m_left, m_top ); } if ( pointTop.x() > m_left ) clippedPolyObject << pointTop; } else if ( m_previousSector == 5 ) { QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); if ( pointRight.y() > m_top ) { clippedPolyObject << pointRight; } else { clippedPolyObject << QPointF( m_right, m_top ); } if ( pointTop.x() < m_right ) clippedPolyObject << pointTop; } else if ( m_previousSector == 6 ) { QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); if ( pointBottom.x() > m_left ) clippedPolyObject << pointBottom; if ( pointLeft.y() > m_top && pointLeft.y() <= m_bottom ) clippedPolyObject << pointLeft; if ( pointTop.x() > m_left ) { clippedPolyObject << pointTop; } else { clippedPolyObject << QPointF( m_left, m_top ); } } else if ( m_previousSector == 7 ) { clippedPolyObject << clipBottom( m, m_previousPoint ); clippedPolyObject << clipTop( m, m_currentPoint ); } else if ( m_previousSector == 8 ) { QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); if ( pointBottom.x() < m_right ) clippedPolyObject << pointBottom; if ( pointRight.y() > m_top && pointRight.y() <= m_bottom ) clippedPolyObject << pointRight; if ( pointTop.x() < m_right ) { clippedPolyObject << pointTop; } else { clippedPolyObject << QPointF( m_right, m_top ); } } break; case 2: if ( m_previousSector == 3 ) { QPointF pointLeft = clipLeft( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); QPointF pointRight = clipRight( m, m_currentPoint ); if ( pointLeft.y() > m_top ) { clippedPolyObject << pointLeft; } else { clippedPolyObject << QPointF( m_left, m_top ); } if ( pointTop.x() > m_left && pointTop.x() <= m_right ) clippedPolyObject << pointTop; if ( pointRight.y() > m_top ) clippedPolyObject << pointRight; } else if ( m_previousSector == 7 ) { QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); QPointF pointRight = clipRight( m, m_currentPoint ); if ( pointBottom.x() < m_right ) { clippedPolyObject << pointBottom; } else { clippedPolyObject << QPointF( m_right, m_bottom ); } if ( pointRight.y() >= m_top && pointRight.y() < m_bottom ) clippedPolyObject << pointRight; if ( pointTop.x() < m_right ) clippedPolyObject << pointTop; } else if ( m_previousSector == 6 ) { QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); QPointF pointTop = clipTop( m, m_currentPoint ); QPointF pointRight = clipRight( m, m_previousPoint ); if ( pointBottom.x() > m_left && pointBottom.x() < m_right ) clippedPolyObject << pointBottom; if ( pointLeft.y() > m_top && pointLeft.y() < m_bottom ) clippedPolyObject << pointLeft; if ( pointTop.x() > m_left && pointTop.x() < m_right ) clippedPolyObject << pointTop; if ( pointRight.y() > m_top && pointRight.y() < m_bottom ) clippedPolyObject << pointRight; if ( pointBottom.x() >= m_right && pointRight.y() >= m_bottom ) clippedPolyObject << QPointF( m_right, m_bottom ); if ( pointTop.x() <= m_left && pointLeft.y() <= m_top ) clippedPolyObject << QPointF( m_left, m_top ); } clippedPolyObject << QPointF( m_right, m_top ); break; case 3: if ( m_previousSector == 7 ) { QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointBottom.x() > m_left ) clippedPolyObject << pointBottom; if ( pointLeft.y() < m_bottom ) { clippedPolyObject << pointLeft; } else { clippedPolyObject << QPointF( m_left, m_bottom ); } } else if ( m_previousSector == 1 ) { QPointF pointTop = clipTop( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointTop.x() > m_left ) clippedPolyObject << pointTop; if ( pointLeft.y() > m_top ) { clippedPolyObject << pointLeft; } else { clippedPolyObject << QPointF( m_left, m_top ); } } else if ( m_previousSector == 8 ) { QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointRight.y() < m_bottom ) clippedPolyObject << pointRight; if ( pointBottom.x() > m_left && pointBottom.x() <= m_right ) clippedPolyObject << pointBottom; if ( pointLeft.y() < m_bottom ) { clippedPolyObject << pointLeft; } else { clippedPolyObject << QPointF( m_left, m_bottom ); } } else if ( m_previousSector == 5 ) { clippedPolyObject << clipRight( m, m_previousPoint ); clippedPolyObject << clipLeft( m, m_currentPoint ); } else if ( m_previousSector == 2 ) { QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointRight.y() > m_top ) clippedPolyObject << pointRight; if ( pointTop.x() > m_left && pointTop.x() <= m_right ) clippedPolyObject << pointTop; if ( pointLeft.y() > m_top ) { clippedPolyObject << pointLeft; } else { clippedPolyObject << QPointF( m_left, m_top ); } } break; case 5: if ( m_previousSector == 7 ) { QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_currentPoint ); if ( pointBottom.x() < m_right ) clippedPolyObject << pointBottom; if ( pointRight.y() < m_bottom ) { clippedPolyObject << pointRight; } else { clippedPolyObject << QPointF( m_right, m_bottom ); } } else if ( m_previousSector == 1 ) { QPointF pointTop = clipTop( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_currentPoint ); if ( pointTop.x() < m_right ) clippedPolyObject << pointTop; if ( pointRight.y() > m_top ) { clippedPolyObject << pointRight; } else { clippedPolyObject << QPointF( m_right, m_top ); } } else if ( m_previousSector == 6 ) { QPointF pointLeft = clipLeft( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_currentPoint ); if ( pointLeft.y() < m_bottom ) clippedPolyObject << pointLeft; if ( pointBottom.x() >= m_left && pointBottom.x() < m_right ) clippedPolyObject << pointBottom; if ( pointRight.y() < m_bottom ) { clippedPolyObject << pointRight; } else { clippedPolyObject << QPointF( m_right, m_bottom ); } } else if ( m_previousSector == 3 ) { clippedPolyObject << clipLeft( m, m_previousPoint ); clippedPolyObject << clipRight( m, m_currentPoint ); } else if ( m_previousSector == 0 ) { QPointF pointLeft = clipLeft( m, m_previousPoint ); QPointF pointTop = clipTop( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_currentPoint ); if ( pointLeft.y() > m_top ) clippedPolyObject << pointLeft; if ( pointTop.x() >= m_left && pointTop.x() < m_right ) clippedPolyObject << pointTop; if ( pointRight.y() > m_top ) { clippedPolyObject << pointRight; } else { clippedPolyObject << QPointF( m_right, m_top ); } } break; case 6: if ( m_previousSector == 5 ) { QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_currentPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointRight.y() < m_bottom ) { clippedPolyObject << pointRight; } else { clippedPolyObject << QPointF( m_right, m_bottom ); } if ( pointBottom.x() >= m_left && pointBottom.x() < m_right ) clippedPolyObject << pointBottom; if ( pointLeft.y() < m_bottom ) clippedPolyObject << pointLeft; } else if ( m_previousSector == 1 ) { QPointF pointTop = clipTop( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); QPointF pointBottom = clipBottom( m, m_currentPoint ); if ( pointTop.x() > m_left ) { clippedPolyObject << pointTop; } else { clippedPolyObject << QPointF( m_left, m_top ); } if ( pointLeft.y() > m_top && pointLeft.y() <= m_bottom ) clippedPolyObject << pointLeft; if ( pointBottom.x() > m_left ) clippedPolyObject << pointBottom; } else if ( m_previousSector == 2 ) { QPointF pointTop = clipTop( m, m_currentPoint ); QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); if ( pointTop.x() > m_left && pointTop.x() < m_right ) clippedPolyObject << pointTop; if ( pointRight.y() > m_top && pointRight.y() < m_bottom ) clippedPolyObject << pointRight; if ( pointBottom.x() > m_left && pointBottom.x() < m_right ) clippedPolyObject << pointBottom; if ( pointLeft.y() > m_top && pointLeft.y() < m_bottom ) clippedPolyObject << pointLeft; if ( pointBottom.x() >= m_right && pointRight.y() >= m_bottom ) clippedPolyObject << QPointF( m_right, m_bottom ); if ( pointTop.x() <= m_left && pointLeft.y() <= m_top ) clippedPolyObject << QPointF( m_left, m_top ); } clippedPolyObject << QPointF( m_left, m_bottom ); break; case 7: if ( m_previousSector == 3 ) { QPointF pointLeft = clipLeft( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_currentPoint ); if ( pointLeft.y() < m_bottom ) { clippedPolyObject << pointLeft; } else { clippedPolyObject << QPointF( m_left, m_bottom ); } if ( pointBottom.x() > m_left ) clippedPolyObject << pointBottom; } else if ( m_previousSector == 5 ) { QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_currentPoint ); if ( pointRight.y() < m_bottom ) { clippedPolyObject << pointRight; } else { clippedPolyObject << QPointF( m_right, m_bottom ); } if ( pointBottom.x() < m_right ) clippedPolyObject << pointBottom; } else if ( m_previousSector == 0 ) { QPointF pointTop = clipTop( m, m_previousPoint ); QPointF pointLeft = clipLeft( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_currentPoint ); if ( pointTop.x() > m_left ) clippedPolyObject << pointTop; if ( pointLeft.y() >= m_top && pointLeft.y() < m_bottom ) clippedPolyObject << pointLeft; if ( pointBottom.x() > m_left ) { clippedPolyObject << pointBottom; } else { clippedPolyObject << QPointF( m_left, m_bottom ); } } else if ( m_previousSector == 1 ) { clippedPolyObject << clipTop( m, m_previousPoint ); clippedPolyObject << clipBottom( m, m_currentPoint ); } else if ( m_previousSector == 2 ) { QPointF pointTop = clipTop( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_currentPoint ); if ( pointTop.x() < m_right ) clippedPolyObject << pointTop; if ( pointRight.y() >= m_top && pointRight.y() < m_bottom ) clippedPolyObject << pointRight; if ( pointBottom.x() < m_right ) { clippedPolyObject << pointBottom; } else { clippedPolyObject << QPointF( m_right, m_bottom ); } } break; case 8: if ( m_previousSector == 3 ) { QPointF pointLeft = clipLeft( m, m_previousPoint ); QPointF pointBottom = clipBottom( m, m_currentPoint ); QPointF pointRight = clipRight( m, m_currentPoint ); if ( pointLeft.y() < m_bottom ) { clippedPolyObject << pointLeft; } else { clippedPolyObject << QPointF( m_left, m_bottom ); } if ( pointBottom.x() > m_left && pointBottom.x() <= m_right ) clippedPolyObject << pointBottom; if ( pointRight.y() < m_bottom ) clippedPolyObject << pointRight; } else if ( m_previousSector == 1 ) { QPointF pointTop = clipTop( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_currentPoint ); QPointF pointBottom = clipBottom( m, m_currentPoint ); if ( pointTop.x() < m_right ) { clippedPolyObject << pointTop; } else { clippedPolyObject << QPointF( m_right, m_top ); } if ( pointRight.y() > m_top && pointRight.y() <= m_bottom ) clippedPolyObject << pointRight; if ( pointBottom.x() < m_right ) clippedPolyObject << pointBottom; } else if ( m_previousSector == 0 ) { QPointF pointTop = clipTop( m, m_currentPoint ); QPointF pointLeft = clipLeft( m, m_currentPoint ); QPointF pointBottom = clipBottom( m, m_previousPoint ); QPointF pointRight = clipRight( m, m_previousPoint ); if ( pointTop.x() > m_left && pointTop.x() < m_right ) clippedPolyObject << pointTop; if ( pointLeft.y() > m_top && pointLeft.y() < m_bottom ) clippedPolyObject << pointLeft; if ( pointBottom.x() > m_left && pointBottom.x() < m_right ) clippedPolyObject << pointBottom; if ( pointRight.y() > m_top && pointRight.y() < m_bottom ) clippedPolyObject << pointRight; if ( pointBottom.x() <= m_left && pointLeft.y() >= m_bottom ) clippedPolyObject << QPointF( m_left, m_bottom ); if ( pointTop.x() >= m_right && pointRight.y() <= m_top ) clippedPolyObject << QPointF( m_right, m_top ); } clippedPolyObject << QPointF( m_right, m_bottom ); break; default: break; } } void BaseClipper::clipOnceCorner( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, const QPointF& corner, const QPointF& point, - bool isClosed ) const + bool isClosed ) { - Q_UNUSED( clippedPolyObjects ) - Q_UNUSED( isClosed ) - if ( m_currentSector == 4) { // Appearing clippedPolyObject << corner; clippedPolyObject << point; } else { // Disappearing clippedPolyObject << point; clippedPolyObject << corner; } } void BaseClipper::clipOnceEdge( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, const QPointF& point, - bool isClosed ) const + bool isClosed ) { if ( m_currentSector == 4) { // Appearing if ( !isClosed ) { clippedPolyObject = QPolygonF(); } clippedPolyObject << point; } else { // Disappearing clippedPolyObject << point; + if ( !isClosed ) { clippedPolyObjects << clippedPolyObject; + clippedPolyObject = QPolygonF(); } } } void BaseClipper::clipOnce( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, bool isClosed ) { // Interpolate border points (linear interpolation) QPointF point; // Calculating the slope. qreal m = _m( m_previousPoint, m_currentPoint ); // Calculate in which sector the end of the line is located that is off screen int offscreenpos = ( m_currentSector == 4 ) ? m_previousSector : m_currentSector; // "Rise over run" for all possible situations . switch ( offscreenpos ) { case 0: // topleft point = clipTop( m, m_previousPoint ); if ( point.x() < m_left ) { point = clipLeft( m, point ); } clipOnceCorner( clippedPolyObject, clippedPolyObjects, QPointF( m_left, m_top ), point, isClosed ); break; case 1: // top point = clipTop( m, m_previousPoint ); clipOnceEdge( clippedPolyObject, clippedPolyObjects, point, isClosed ); break; case 2: // topright point = clipTop( m, m_previousPoint ); if ( point.x() > m_right ) { point = clipRight( m, point ); } clipOnceCorner( clippedPolyObject, clippedPolyObjects, QPointF( m_right, m_top ), point, isClosed ); break; case 3: // left point = clipLeft( m, m_previousPoint ); clipOnceEdge( clippedPolyObject, clippedPolyObjects, point, isClosed ); break; case 5: // right point = clipRight( m, m_previousPoint ); clipOnceEdge( clippedPolyObject, clippedPolyObjects, point, isClosed ); break; case 6: // bottomleft point = clipBottom( m, m_previousPoint ); if ( point.x() < m_left ) { point = clipLeft( m, point ); } clipOnceCorner( clippedPolyObject, clippedPolyObjects, QPointF( m_left, m_bottom ), point, isClosed ); break; case 7: // bottom point = clipBottom( m, m_previousPoint ); clipOnceEdge( clippedPolyObject, clippedPolyObjects, point, isClosed ); break; case 8: // bottomright point = clipBottom( m, m_previousPoint ); if ( point.x() > m_right ) { point = clipRight( m, point ); } clipOnceCorner( clippedPolyObject, clippedPolyObjects, QPointF( m_right, m_bottom ), point, isClosed ); break; default: break; } } diff --git a/tools/osm-simplify/BaseClipper.h b/tools/osm-simplify/BaseClipper.h index c5c4646e6..56dc14cac 100644 --- a/tools/osm-simplify/BaseClipper.h +++ b/tools/osm-simplify/BaseClipper.h @@ -1,85 +1,85 @@ // // 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 2006-2009 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2016 David Kolozsvari // #ifndef BASECLIPPER_H #define BASECLIPPER_H #include #include "GeoDataLinearRing.h" #include "GeoDataLatLonBox.h" using namespace Marble; class BaseClipper { public: BaseClipper(); static qreal tileX2lon( unsigned int x, unsigned int maxTileX ); static qreal tileY2lat( unsigned int y, unsigned int maxTileY ); static QPolygonF lineString2Qpolygon(const GeoDataLineString &lineString); static QPolygonF linearRing2Qpolygon(const GeoDataLinearRing &linearRing); static GeoDataLineString qPolygon2lineString(const QPolygonF& polygon); static GeoDataLinearRing qPolygon2linearRing(const QPolygonF& polygon); void initClipRect(const GeoDataLatLonBox& clippingBox); void clipPolyObject ( const QPolygonF & sourcePolygon, QVector & clippedPolyObjects, bool isClosed ); private: inline int sector( const QPointF & point ) const; inline QPointF clipTop( qreal m, const QPointF & point ) const; inline QPointF clipLeft( qreal m, const QPointF & point ) const; inline QPointF clipBottom( qreal m, const QPointF & point ) const; inline QPointF clipRight( qreal m, const QPointF & point ) const; inline void clipMultiple( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, bool isClosed ); inline void clipOnce( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, bool isClosed ); inline void clipOnceCorner( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, const QPointF& corner, const QPointF& point, - bool isClosed ) const; + bool isClosed ); inline void clipOnceEdge( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, const QPointF& point, - bool isClosed ) const; + bool isClosed ); static inline qreal _m( const QPointF & start, const QPointF & end ); qreal m_left; qreal m_right; qreal m_top; qreal m_bottom; int m_currentSector; int m_previousSector; QPointF m_currentPoint; QPointF m_previousPoint; }; #endif // BASECLIPPER_H diff --git a/tools/osm-simplify/CMakeLists.txt b/tools/osm-simplify/CMakeLists.txt index b66199ff1..cc43b2887 100644 --- a/tools/osm-simplify/CMakeLists.txt +++ b/tools/osm-simplify/CMakeLists.txt @@ -1,34 +1,35 @@ 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 ) add_definitions( -DMAKE_MARBLE_LIB ) add_executable( ${TARGET} ${${TARGET}_SRC} ) target_link_libraries( ${TARGET} Qt5::Core marblewidget-qt5) diff --git a/tools/osm-simplify/TinyPlanetProcessor.cpp b/tools/osm-simplify/TinyPlanetProcessor.cpp new file mode 100644 index 000000000..22cd8ed81 --- /dev/null +++ b/tools/osm-simplify/TinyPlanetProcessor.cpp @@ -0,0 +1,156 @@ +// +// 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 "TinyPlanetProcessor.h" + +#include "BaseClipper.h" + +#include "GeoDataPlacemark.h" +#include "OsmPlacemarkData.h" + +#include +#include + +TinyPlanetProcessor::TinyPlanetProcessor(GeoDataDocument* document) : + PlacemarkFilter(document) +{ + +} + +void TinyPlanetProcessor::process() +{ + // ? +} + +GeoDataDocument *TinyPlanetProcessor::cutToTiles(unsigned int zoomLevel, unsigned int tileX, unsigned int tileY) +{ + unsigned int N = pow(2, zoomLevel); + + GeoDataDocument* tile = new GeoDataDocument(); + QString tileName = QString("%1/%2/%3").arg(zoomLevel).arg(tileX).arg(tileY); + tile->setName(tileName); + + GeoDataLatLonBox tileBoundary; + qreal north = BaseClipper::tileY2lat(tileY, N); + qreal south = BaseClipper::tileY2lat(tileY+1, N); + qreal west = BaseClipper::tileX2lon(tileX, N); + qreal east = BaseClipper::tileX2lon(tileX+1, N); + + tileBoundary.setBoundaries(north, south, east, west); + + BaseClipper clipper; + clipper.initClipRect(tileBoundary); + + foreach (GeoDataObject* object, m_objects) { + GeoDataPlacemark* placemark = static_cast(object); + + if(tileBoundary.intersects(placemark->geometry()->latLonAltBox())) { + + if( placemark->geometry()->nodeType() == GeoDataTypes::GeoDataPolygonType || + placemark->visualCategory() == GeoDataFeature::Landmass) { + + GeoDataLinearRing* marblePolygon; + if(placemark->geometry()->nodeType() == GeoDataTypes::GeoDataPolygonType) { + marblePolygon = &static_cast(placemark->geometry())->outerBoundary(); + } else if (placemark->geometry()->nodeType() == GeoDataTypes::GeoDataLinearRingType) { + marblePolygon = static_cast(placemark->geometry()); + } + + QVector clippedPolygons; + + QPolygonF outerBoundary = BaseClipper::linearRing2Qpolygon(*marblePolygon); + + clipper.clipPolyObject(outerBoundary, clippedPolygons, true); + + foreach(const QPolygonF& polygon, clippedPolygons) { + + GeoDataLinearRing outerBoundary = BaseClipper::qPolygon2linearRing(polygon); + GeoDataPolygon* newMarblePolygon = new GeoDataPolygon(); + newMarblePolygon->setOuterBoundary(outerBoundary); + + GeoDataPlacemark* newPlacemark = new GeoDataPlacemark(); + newPlacemark->setGeometry(newMarblePolygon); + newPlacemark->setVisualCategory(GeoDataFeature::Landmass); + + OsmPlacemarkData marbleLand; + marbleLand.addTag("marble_land","landmass"); + newPlacemark->setOsmData(marbleLand); + + tile->append(newPlacemark); + } + } else if (placemark->geometry()->nodeType() == GeoDataTypes::GeoDataLineStringType) { + GeoDataLineString* marbleWay = static_cast(placemark->geometry()); + + QVector clippedPolygons; + + QPolygonF way = BaseClipper::lineString2Qpolygon(*marbleWay); + + clipper.clipPolyObject(way, clippedPolygons, false); + + foreach(const QPolygonF& polygon, clippedPolygons) { + + GeoDataLineString* newMarbleWay = new GeoDataLineString(BaseClipper::qPolygon2lineString(polygon)); + + GeoDataPlacemark* newPlacemark = new GeoDataPlacemark(); + newPlacemark->setGeometry(newMarbleWay); + newPlacemark->setVisualCategory(placemark->visualCategory()); + + OsmPlacemarkData osmData; + auto it = placemark->osmData().tagsBegin(); + auto itEnd = placemark->osmData().tagsEnd(); + while(it != itEnd) { + osmData.addTag(it.key(), it.value()); + ++it; + } + newPlacemark->setOsmData(osmData); + + tile->append(newPlacemark); + } + } else if (placemark->geometry()->nodeType() == GeoDataTypes::GeoDataLinearRingType) { + + GeoDataLinearRing* marbleClosedWay = static_cast(placemark->geometry()); + + QVector clippedPolygons; + + QPolygonF closedWay = BaseClipper::linearRing2Qpolygon(*marbleClosedWay); + + // If we cut a closed way to pieces, the results shouldn't be closed ways too + clipper.clipPolyObject(closedWay, clippedPolygons, false); + + foreach(const QPolygonF& polygon, clippedPolygons) { + + // When a linearRing is cut to pieces, the resulting geometries will be lineStrings + GeoDataLineString* newMarbleWay = new GeoDataLineString(BaseClipper::qPolygon2lineString(polygon)); + + GeoDataPlacemark* newPlacemark = new GeoDataPlacemark(); + newPlacemark->setGeometry(newMarbleWay); + newPlacemark->setVisualCategory(placemark->visualCategory()); + + OsmPlacemarkData osmData; + auto it = placemark->osmData().tagsBegin(); + auto itEnd = placemark->osmData().tagsEnd(); + while(it != itEnd) { + osmData.addTag(it.key(), it.value()); + ++it; + } + newPlacemark->setOsmData(osmData); + + tile->append(newPlacemark); + } + + } else { + tile->append(placemark); + } + } + } + + + return tile; +} diff --git a/tools/osm-simplify/TinyPlanetProcessor.h b/tools/osm-simplify/TinyPlanetProcessor.h new file mode 100644 index 000000000..862f55ec5 --- /dev/null +++ b/tools/osm-simplify/TinyPlanetProcessor.h @@ -0,0 +1,26 @@ +// +// 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 TINYPLANETPROCESSOR_H +#define TINYPLANETPROCESSOR_H + +#include "PlacemarkFilter.h" + +class TinyPlanetProcessor : public PlacemarkFilter +{ +public: + TinyPlanetProcessor(GeoDataDocument* document); + + virtual void process(); + + GeoDataDocument* cutToTiles(unsigned int zoomLevel, unsigned int tileX, unsigned int tileY); +}; + +#endif // TINYPLANETPROCESSOR_H diff --git a/tools/osm-simplify/main.cpp b/tools/osm-simplify/main.cpp index e0de41c4b..3c34d3e58 100644 --- a/tools/osm-simplify/main.cpp +++ b/tools/osm-simplify/main.cpp @@ -1,227 +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 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" 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.") }, { - {"m","mute"}, + {"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."), - //QCoreApplication::translate("main", "number") }, { {"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") }, { {"o", "output"}, - QCoreApplication::translate("main", "Generates an output .osmfile based on other flags. This won't work together with the cut-to-tiles flag."), + 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 mute = parser.isSet("mute"); + 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(!dir.exists(QString::number(zoomLevel))) { - dir.mkdir(QString::number(zoomLevel)); + 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)); + } } - 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 " << outputFileName; + 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"<