diff --git a/src/lib/marble/AbstractFloatItem.cpp b/src/lib/marble/AbstractFloatItem.cpp index 41fc87ff4..b3003a0b5 100644 --- a/src/lib/marble/AbstractFloatItem.cpp +++ b/src/lib/marble/AbstractFloatItem.cpp @@ -1,258 +1,258 @@ // // 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 // // Self #include "AbstractFloatItem.h" // Qt #include #include #include #include #include #include // Marble #include "DialogConfigurationInterface.h" #include "GeoPainter.h" #include "MarbleDebug.h" namespace Marble { class AbstractFloatItemPrivate { public: AbstractFloatItemPrivate() : m_contextMenu( 0 ) { } ~AbstractFloatItemPrivate() { delete m_contextMenu; } static QPen s_pen; static QFont s_font; QMenu* m_contextMenu; }; QPen AbstractFloatItemPrivate::s_pen = QPen( Qt::black ); #ifdef Q_OS_MACX QFont AbstractFloatItemPrivate::s_font = QFont( QStringLiteral("Sans Serif"), 10 ); #else QFont AbstractFloatItemPrivate::s_font = QFont( QStringLiteral("Sans Serif"), 8 ); #endif AbstractFloatItem::AbstractFloatItem( const MarbleModel *marbleModel, const QPointF &point, const QSizeF &size ) : RenderPlugin( marbleModel ), FrameGraphicsItem(), d( new AbstractFloatItemPrivate() ) { setCacheMode( ItemCoordinateCache ); setFrame( RectFrame ); setPadding( 4.0 ); setContentSize( size ); setPosition( point ); } AbstractFloatItem::~AbstractFloatItem() { delete d; } QHash AbstractFloatItem::settings() const { QHash updated = RenderPlugin::settings(); updated.insert(QStringLiteral("position"), position()); return updated; } void AbstractFloatItem::setSettings(const QHash &settings) { if (settings.value(QStringLiteral("position")).type() == QVariant::String) { // work around KConfig turning QPointFs into QStrings const QStringList coordinates = settings.value(QStringLiteral("position")).toString().split(QLatin1Char(',')); setPosition( QPointF( coordinates.at( 0 ).toFloat(), coordinates.at( 1 ).toFloat() ) ); } else { setPosition(settings.value(QStringLiteral("position"), position()).toPointF()); } RenderPlugin::setSettings(settings); } RenderPlugin::RenderType AbstractFloatItem::renderType() const { return RenderPlugin::PanelRenderType; } QPen AbstractFloatItem::pen() const { return d->s_pen; } void AbstractFloatItem::setPen( const QPen &pen ) { d->s_pen = pen; update(); } QFont AbstractFloatItem::font() const { return d->s_font; } void AbstractFloatItem::setFont( const QFont &font ) { d->s_font = font; update(); } QString AbstractFloatItem::renderPolicy() const { return QStringLiteral("ALWAYS"); } QStringList AbstractFloatItem::renderPosition() const { return QStringList(QStringLiteral("FLOAT_ITEM")); } void AbstractFloatItem::setVisible( bool visible ) { // Reimplemented since AbstractFloatItem does multiple inheritance // and the (set)Visible() methods are available in both base classes! RenderPlugin::setVisible( visible ); } bool AbstractFloatItem::visible() const { // Reimplemented since AbstractFloatItem does multiple inheritance // and the (set)Visible() methods are available in both base classes! return RenderPlugin::visible(); } void AbstractFloatItem::setPositionLocked( bool lock ) { ScreenGraphicsItem::GraphicsItemFlags flags = this->flags(); if ( lock ) { flags &= ~ScreenGraphicsItem::ItemIsMovable; } else { flags |= ScreenGraphicsItem::ItemIsMovable; } setFlags( flags ); } bool AbstractFloatItem::positionLocked() const { - return ( flags() & ScreenGraphicsItem::ItemIsMovable ) ? false : true; + return ( flags() & ScreenGraphicsItem::ItemIsMovable ) == 0; } bool AbstractFloatItem::eventFilter( QObject *object, QEvent *e ) { if ( !enabled() || !visible() ) { return false; } if( e->type() == QEvent::ContextMenu ) { QWidget *widget = qobject_cast( object ); QContextMenuEvent *menuEvent = dynamic_cast ( e ); if( widget != NULL && menuEvent != NULL && contains( menuEvent->pos() ) ) { contextMenuEvent( widget, menuEvent ); return true; } return false; } else if( e->type() == QEvent::ToolTip ) { QHelpEvent *helpEvent = dynamic_cast( e ); if( helpEvent != NULL && contains( helpEvent->pos() ) ) { toolTipEvent( helpEvent ); return true; } return false; } else return ScreenGraphicsItem::eventFilter( object, e ); } void AbstractFloatItem::contextMenuEvent ( QWidget *w, QContextMenuEvent *e ) { contextMenu()->exec( w->mapToGlobal( e->pos() ) ); } void AbstractFloatItem::toolTipEvent ( QHelpEvent *e ) { Q_UNUSED( e ); } bool AbstractFloatItem::render( GeoPainter *painter, ViewportParams *viewport, const QString& renderPos, GeoSceneLayer * layer ) { Q_UNUSED(painter) Q_UNUSED(viewport) Q_UNUSED(renderPos) Q_UNUSED(layer) return true; } void AbstractFloatItem::show() { setVisible( true ); } void AbstractFloatItem::hide() { setVisible( false ); } QMenu* AbstractFloatItem::contextMenu() { if ( !d->m_contextMenu ) { d->m_contextMenu = new QMenu; QAction *lockAction = d->m_contextMenu->addAction(QIcon(QStringLiteral(":/icons/unlock.png")), tr("&Lock")); lockAction->setCheckable( true ); lockAction->setChecked( positionLocked() ); connect( lockAction, SIGNAL(triggered(bool)), this, SLOT(setPositionLocked(bool)) ); if(!(flags() & ItemIsHideable)) { QAction *hideAction = d->m_contextMenu->addAction( tr( "&Hide" ) ); connect( hideAction, SIGNAL(triggered()), this, SLOT(hide()) ); } DialogConfigurationInterface *configInterface = qobject_cast( this ); QDialog *dialog = configInterface ? configInterface->configDialog() : 0; if( dialog ) { d->m_contextMenu->addSeparator(); QAction *configAction = d->m_contextMenu->addAction(QIcon(QStringLiteral(":/icons/settings-configure.png")), tr("&Configure...")); connect( configAction, SIGNAL(triggered()), dialog, SLOT(exec()) ); } } Q_ASSERT( d->m_contextMenu ); return d->m_contextMenu; } } #include "moc_AbstractFloatItem.cpp" diff --git a/src/lib/marble/BranchFilterProxyModel.cpp b/src/lib/marble/BranchFilterProxyModel.cpp index 86c1b64b0..6c36baac4 100644 --- a/src/lib/marble/BranchFilterProxyModel.cpp +++ b/src/lib/marble/BranchFilterProxyModel.cpp @@ -1,68 +1,65 @@ // // 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 2010 Dennis Nienhüser // Copyright 2012 Thibaut Gridel #include "BranchFilterProxyModel.h" #include "MarblePlacemarkModel.h" #include "GeoDataContainer.h" #include "GeoDataTreeModel.h" namespace Marble { BranchFilterProxyModel::BranchFilterProxyModel( QObject *parent ) : QSortFilterProxyModel( parent ), m_treeModel( 0 ) { // nothing to do } /// sets the folder index for which we want to see bookmarks void BranchFilterProxyModel::setBranchIndex( GeoDataTreeModel* treeModel, const QModelIndex &index ) { Q_ASSERT( index.isValid() ); Q_ASSERT( index.model() == treeModel ); m_treeModel = treeModel; m_branchIndex = index; invalidateFilter(); } /// determines if such row should be filtered. /// Beware, all parents of our folder must be accepted as well bool BranchFilterProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const { if ( !m_treeModel || !m_branchIndex.isValid() ) { return true; } Q_ASSERT( m_treeModel == sourceModel() ); if ( sourceParent.isValid() ) { Q_ASSERT( sourceParent.model() == m_treeModel ); } QModelIndex rowIndex = sourceModel()->index( sourceRow, 0, sourceParent ); Q_ASSERT( rowIndex.isValid() ); // return true for all non folder children of m_branchIndex if( sourceParent == m_branchIndex ) { GeoDataObject* obj = qvariant_cast( rowIndex.data( MarblePlacemarkModel::ObjectPointerRole ) ); return dynamic_cast(obj); } // return true if rowIndex is a parent of m_branchIndex QModelIndex tmpIndex = m_branchIndex; while ( tmpIndex.isValid() && tmpIndex != rowIndex ) { tmpIndex = tmpIndex.parent(); } - if( tmpIndex == rowIndex ) { - return true; - } - return false; + return tmpIndex == rowIndex; } } diff --git a/src/lib/marble/ClipPainter.cpp b/src/lib/marble/ClipPainter.cpp index d6ca8eb57..22f6bfd37 100644 --- a/src/lib/marble/ClipPainter.cpp +++ b/src/lib/marble/ClipPainter.cpp @@ -1,1308 +1,1305 @@ // // 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 // #include "ClipPainter.h" #include #include "MarbleDebug.h" namespace Marble { class ClipPainterPrivate { public: explicit ClipPainterPrivate( ClipPainter * parent ); ClipPainter * q; // true if clipping is on. bool m_doClip; // The limits qreal m_left; qreal m_right; qreal m_top; qreal m_bottom; // Used in the paint process of vectors.. int m_currentSector; int m_previousSector; // int m_debugNodeCount; QPointF m_currentPoint; QPointF m_previousPoint; 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 initClipRect(); inline void clipPolyObject ( const QPolygonF & sourcePolygon, QVector & clippedPolyObjects, bool isClosed ); 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; inline void clipOnceEdge( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, const QPointF& point, bool isClosed ) const; void labelPosition(const QPolygonF& polygon, QVector& labelNodes, LabelPositionFlags labelPositionFlags); bool pointAllowsLabel( const QPointF& point ); QPointF interpolateLabelPoint( const QPointF& previousPoint, const QPointF& currentPoint, LabelPositionFlags labelPositionFlags ); static inline qreal _m( const QPointF & start, const QPointF & end ); void debugDrawNodes( const QPolygonF & ); qreal m_labelAreaMargin; int m_debugPenBatchColor; int m_debugBrushBatchColor; int m_debugPolygonsLevel; bool m_debugBatchRender; }; } using namespace Marble; // #define MARBLE_DEBUG ClipPainter::ClipPainter(QPaintDevice * pd, bool clip) : QPainter( pd ), d( new ClipPainterPrivate( this ) ) { d->initClipRect(); // m_debugNodeCount = 0; d->m_doClip = clip; } ClipPainter::ClipPainter() : d( new ClipPainterPrivate( this ) ) { } ClipPainter::~ClipPainter() { delete d; } void ClipPainter::setScreenClip(bool enable) { d->m_doClip = enable; } bool ClipPainter::hasScreenClip() const { return d->m_doClip; } void ClipPainter::drawPolygon ( const QPolygonF & polygon, Qt::FillRule fillRule ) { if ( d->m_doClip ) { d->initClipRect(); QVector clippedPolyObjects; d->clipPolyObject( polygon, clippedPolyObjects, true ); for( const QPolygonF & clippedPolyObject: clippedPolyObjects ) { if ( clippedPolyObject.size() > 2 ) { // mDebug() << "Size: " << clippedPolyObject.size(); if (d->m_debugPolygonsLevel) { QBrush brush = QPainter::brush(); QBrush originalBrush = brush; QColor color = brush.color(); color.setAlpha(color.alpha()*0.75); brush.setColor(color); QPainter::setBrush(brush); QPainter::drawPolygon ( clippedPolyObject, fillRule ); QPainter::setBrush(originalBrush); d->debugDrawNodes( clippedPolyObject ); } else { QPainter::drawPolygon ( clippedPolyObject, fillRule ); } } } } else { if (d->m_debugPolygonsLevel) { QBrush brush = QPainter::brush(); QBrush originalBrush = brush; QColor color = brush.color(); color.setAlpha(color.alpha()*0.75); brush.setColor(color); QPainter::setBrush(brush); QPainter::drawPolygon ( polygon, fillRule ); QPainter::setBrush(originalBrush); d->debugDrawNodes( polygon ); } else { QPainter::drawPolygon ( polygon, fillRule ); } } } void ClipPainter::drawPolyline( const QPolygonF & polygon ) { if ( d->m_doClip ) { d->initClipRect(); QVector clippedPolyObjects; d->clipPolyObject( polygon, clippedPolyObjects, false ); for( const QPolygonF & clippedPolyObject: clippedPolyObjects ) { if ( clippedPolyObject.size() > 1 ) { if (d->m_debugPolygonsLevel) { QPen pen = QPainter::pen(); QPen originalPen = pen; QColor color = pen.color(); color.setAlpha(color.alpha()*0.75); pen.setColor(color); QPainter::setPen(pen); QPainter::drawPolyline ( clippedPolyObject ); QPainter::setPen(originalPen); d->debugDrawNodes( clippedPolyObject ); } else { QPainter::drawPolyline ( clippedPolyObject ); } } } } else { if (d->m_debugPolygonsLevel) { QPen pen = QPainter::pen(); QPen originalPen = pen; QColor color = pen.color(); color.setAlpha(color.alpha()*0.75); pen.setColor(color); QPainter::setPen(pen); QPainter::drawPolyline ( polygon ); QPainter::setPen(originalPen); d->debugDrawNodes( polygon ); } else { QPainter::drawPolyline ( polygon ); } } } void ClipPainter::drawPolyline(const QPolygonF & polygon, QVector& labelNodes, LabelPositionFlags positionFlags) { if ( d->m_doClip ) { d->initClipRect(); QVector clippedPolyObjects; d->clipPolyObject( polygon, clippedPolyObjects, false ); for( const QPolygonF & clippedPolyObject: clippedPolyObjects ) { if (d->m_debugPolygonsLevel) { QPen pen = QPainter::pen(); QPen originalPen = pen; QColor color = pen.color(); color.setAlpha(color.alpha()*0.75); pen.setColor(color); QPainter::setPen(pen); QPainter::drawPolyline ( clippedPolyObject ); QPainter::setPen(originalPen); d->debugDrawNodes( clippedPolyObject ); } else { QPainter::drawPolyline ( clippedPolyObject ); } } } else { if (d->m_debugPolygonsLevel) { QPen pen = QPainter::pen(); QPen originalPen = pen; QColor color = pen.color(); color.setAlpha(color.alpha()*0.75); pen.setColor(color); QPainter::setPen(pen); QPainter::drawPolyline ( polygon ); QPainter::setPen(originalPen); d->debugDrawNodes( polygon ); } else { QPainter::drawPolyline ( polygon ); } d->labelPosition( polygon, labelNodes, positionFlags ); } } void ClipPainter::labelPosition(const QPolygonF & polygon, QVector& labelNodes, LabelPositionFlags labelPositionFlags) { d->labelPosition(polygon, labelNodes, labelPositionFlags); } void ClipPainter::setPen(const QColor &color) { if (d->m_debugBatchRender) { qDebug() << Q_FUNC_INFO; } setPen(QPen(color)); } void ClipPainter::setPen(Qt::PenStyle style) { if (d->m_debugBatchRender) { qDebug() << Q_FUNC_INFO; } setPen(QPen(style)); } void ClipPainter::setPen(const QPen & pen) { if (d->m_debugBatchRender) { qDebug() << Q_FUNC_INFO; if (pen != QPainter::pen()) { qDebug() << "--" << pen.color() << QPainter::pen().color() ; QPen newPen = pen; newPen.setColor((Qt::GlobalColor)(d->m_debugPenBatchColor+4)); QPainter::setPen(newPen); d->m_debugPenBatchColor++; d->m_debugPenBatchColor %= 14; } else { qDebug() << "++"; QPainter::setPen(pen); } } else { QPainter::setPen(pen); } } void ClipPainter::setBrush(const QBrush & brush) { if (d->m_debugBatchRender) { qDebug() << Q_FUNC_INFO; if (brush != QPainter::brush()) { qDebug() << "--" << brush.color() << QPainter::brush().color() ; QBrush batchColor(QColor((Qt::GlobalColor)(d->m_debugBrushBatchColor))); QPainter::setBrush(batchColor); d->m_debugBrushBatchColor++; d->m_debugBrushBatchColor %= 20; } else { qDebug() << "++"; QPainter::setBrush(brush); } } else { QPainter::setBrush(brush); } } void ClipPainterPrivate::labelPosition(const QPolygonF & polygon, QVector& labelNodes, LabelPositionFlags labelPositionFlags) { bool currentAllowsLabel = false; if ( labelPositionFlags.testFlag( LineCenter ) ) { // The Label at the center of the polyline: int labelPosition = static_cast( polygon.size() / 2.0 ); if ( polygon.size() > 0 ) { if ( labelPosition >= polygon.size() ) { labelPosition = polygon.size() - 1; } labelNodes << polygon.at( labelPosition ); } } if ( polygon.size() > 0 && labelPositionFlags.testFlag( LineStart ) ) { if ( pointAllowsLabel( polygon.first() ) ) { labelNodes << polygon.first(); } // The Label at the start of the polyline: for ( int it = 1; it < polygon.size(); ++it ) { currentAllowsLabel = pointAllowsLabel( polygon.at( it ) ); if ( currentAllowsLabel ) { // As polygon.size() > 0 it's ensured that it-1 exists. QPointF node = interpolateLabelPoint( polygon.at( it -1 ), polygon.at( it ), labelPositionFlags ); if ( node != QPointF( -1.0, -1.0 ) ) { labelNodes << node; } break; } } } if ( polygon.size() > 1 && labelPositionFlags.testFlag( LineEnd ) ) { if ( pointAllowsLabel( polygon.at( polygon.size() - 1 ) ) ) { labelNodes << polygon.at( polygon.size() - 1 ); } // The Label at the end of the polyline: for ( int it = polygon.size() - 2; it > 0; --it ) { currentAllowsLabel = pointAllowsLabel( polygon.at( it ) ); if ( currentAllowsLabel ) { QPointF node = interpolateLabelPoint( polygon.at( it + 1 ), polygon.at( it ), labelPositionFlags ); if ( node != QPointF( -1.0, -1.0 ) ) { labelNodes << node; } break; } } } } bool ClipPainterPrivate::pointAllowsLabel( const QPointF& point ) { - if ( point.x() > m_labelAreaMargin && point.x() < q->viewport().width() - m_labelAreaMargin - && point.y() > m_labelAreaMargin && point.y() < q->viewport().height() - m_labelAreaMargin ) { - return true; - } - return false; + return point.x() > m_labelAreaMargin && point.x() < q->viewport().width() - m_labelAreaMargin + && point.y() > m_labelAreaMargin && point.y() < q->viewport().height() - m_labelAreaMargin; } QPointF ClipPainterPrivate::interpolateLabelPoint( const QPointF& previousPoint, const QPointF& currentPoint, LabelPositionFlags labelPositionFlags ) { qreal m = _m( previousPoint, currentPoint ); if ( previousPoint.x() <= m_labelAreaMargin ) { if ( labelPositionFlags.testFlag( IgnoreXMargin ) ) { return QPointF( -1.0, -1.0 ); } return QPointF( m_labelAreaMargin, previousPoint.y() + ( m_labelAreaMargin - previousPoint.x() ) * m ); } else if ( previousPoint.x() >= q->viewport().width() - m_labelAreaMargin ) { if ( labelPositionFlags.testFlag( IgnoreXMargin ) ) { return QPointF( -1.0, -1.0 ); } return QPointF( q->viewport().width() - m_labelAreaMargin, previousPoint.y() - ( previousPoint.x() - q->viewport().width() + m_labelAreaMargin ) * m ); } if ( previousPoint.y() <= m_labelAreaMargin ) { if ( labelPositionFlags.testFlag( IgnoreYMargin ) ) { return QPointF( -1.0, -1.0 ); } return QPointF( previousPoint.x() + ( m_labelAreaMargin - previousPoint.y() ) / m, m_labelAreaMargin ); } else if ( previousPoint.y() >= q->viewport().height() - m_labelAreaMargin ) { if ( labelPositionFlags.testFlag( IgnoreYMargin ) ) { return QPointF( -1.0, -1.0 ); } return QPointF( previousPoint.x() - ( previousPoint.y() - q->viewport().height() + m_labelAreaMargin ) / m, q->viewport().height() - m_labelAreaMargin ); } // mDebug() << Q_FUNC_INFO << "Previous and current node position are allowed!"; return QPointF( -1.0, -1.0 ); } ClipPainterPrivate::ClipPainterPrivate( ClipPainter * parent ) : m_doClip( true ), 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()), m_labelAreaMargin(10.0), m_debugPenBatchColor(0), m_debugBrushBatchColor(0), m_debugPolygonsLevel(0), m_debugBatchRender(false) { q = parent; } void ClipPainterPrivate::initClipRect () { qreal penHalfWidth = q->pen().widthF() / 2.0 + 1.0; m_left = -penHalfWidth; m_right = (qreal)(q->device()->width()) + penHalfWidth; m_top = -penHalfWidth; m_bottom = (qreal)(q->device()->height()) + penHalfWidth; } qreal ClipPainterPrivate::_m( const QPointF & start, const QPointF & end ) { qreal divisor = end.x() - start.x(); if ( std::fabs( divisor ) < 0.000001 ) { // this is in screencoordinates so the difference // between 0, 0.000001 and -0.000001 isn't visible at all divisor = 0.000001; } return ( end.y() - start.y() ) / divisor; } QPointF ClipPainterPrivate::clipTop( qreal m, const QPointF & point ) const { return QPointF( ( m_top - point.y() ) / m + point.x(), m_top ); } QPointF ClipPainterPrivate::clipLeft( qreal m, const QPointF & point ) const { return QPointF( m_left, ( m_left - point.x() ) * m + point.y() ); } QPointF ClipPainterPrivate::clipBottom( qreal m, const QPointF & point ) const { return QPointF( ( m_bottom - point.y() ) / m + point.x(), m_bottom ); } QPointF ClipPainterPrivate::clipRight( qreal m, const QPointF & point ) const { return QPointF( m_right, ( m_right - point.x() ) * m + point.y() ); } int ClipPainterPrivate::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 ClipPainterPrivate::clipPolyObject ( const QPolygonF & polygon, QVector & clippedPolyObjects, bool isClosed ) { // mDebug() << "ClipPainter enabled." ; // 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); // mDebug() << "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 ClipPainterPrivate::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 ClipPainterPrivate::clipOnceCorner( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, const QPointF& corner, const QPointF& point, bool isClosed ) const { Q_UNUSED( clippedPolyObjects ) Q_UNUSED( isClosed ) if ( m_currentSector == 4) { // Appearing clippedPolyObject << corner; clippedPolyObject << point; } else { // Disappearing clippedPolyObject << point; clippedPolyObject << corner; } } void ClipPainterPrivate::clipOnceEdge( QPolygonF & clippedPolyObject, QVector & clippedPolyObjects, const QPointF& point, bool isClosed ) const { if ( m_currentSector == 4) { // Appearing if ( !isClosed ) { clippedPolyObject = QPolygonF(); } clippedPolyObject << point; } else { // Disappearing clippedPolyObject << point; if ( !isClosed ) { clippedPolyObjects << clippedPolyObject; } } } void ClipPainterPrivate::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; } } void ClipPainter::setDebugPolygonsLevel( int level ) { d->m_debugPolygonsLevel = level; } void ClipPainter::setDebugBatchRender( bool enabled ) { d->m_debugBatchRender = enabled; } void ClipPainterPrivate::debugDrawNodes( const QPolygonF & polygon ) { q->save(); q->setRenderHint( QPainter::Antialiasing, false ); q->setPen( Qt::red ); q->setBrush(QBrush("#40FF0000")); const QVector::const_iterator itStartPoint = polygon.constBegin(); const QVector::const_iterator itEndPoint = polygon.constEnd(); QVector::const_iterator itPoint = itStartPoint; int i = 0; for (; itPoint != itEndPoint; ++itPoint ) { ++i; if ( itPoint == itStartPoint || itPoint == itStartPoint + 1 || itPoint == itStartPoint + 2 ) { q->setPen( Qt::darkGreen ); q->setBrush(QBrush("#4000FF00")); if ( itPoint == itStartPoint ) { q->drawRect( itPoint->x() - 6.0, itPoint->y() - 6.0 , 12.0, 12.0 ); } else if ( itPoint == itStartPoint + 1 ) { q->drawRect( itPoint->x() - 4.0, itPoint->y() - 4.0 , 8.0, 8.0 ); } else { q->drawRect( itPoint->x() - 2.0, itPoint->y() - 2.0 , 4.0, 4.0 ); } q->setPen( Qt::red ); q->setBrush(QBrush("#40FF0000")); } else if ( itPoint == itEndPoint - 1 || itPoint == itEndPoint - 2 || itPoint == itEndPoint - 3 ) { q->setPen( Qt::blue ); q->setBrush(QBrush("#400000FF")); if ( itPoint == itEndPoint - 3 ) { q->drawRect( itPoint->x() - 6.0, itPoint->y() - 6.0 , 12.0, 12.0 ); } else if ( itPoint == itEndPoint - 2 ) { q->drawRect( itPoint->x() - 4.0, itPoint->y() - 4.0 , 8.0, 8.0 ); } else { q->drawRect( itPoint->x() - 2.0, itPoint->y() - 2.0 , 4.0, 4.0 ); } q->setPen( Qt::red ); q->setBrush(QBrush("#400000FF")); } else { q->drawRect( itPoint->x() - 4, itPoint->y() - 4 , 8.0, 8.0 ); } if (m_debugPolygonsLevel == 2) { q->setFont(QFont(QStringLiteral("Sans Serif"), 7)); q->setPen("black"); q->setBrush(Qt::transparent); q->drawText(itPoint->x() + 6.0, itPoint->y() + (15 - (i * 5) % 30) , QString::number(i)); } } q->restore(); } diff --git a/src/lib/marble/geodata/data/GeoDataChange.cpp b/src/lib/marble/geodata/data/GeoDataChange.cpp index 4c590d52b..e829e234c 100644 --- a/src/lib/marble/geodata/data/GeoDataChange.cpp +++ b/src/lib/marble/geodata/data/GeoDataChange.cpp @@ -1,71 +1,68 @@ // // 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 2014 Sanjiban Bairagya // #include "GeoDataChange.h" #include "GeoDataContainer_p.h" #include "GeoDataTypes.h" namespace Marble { class GeoDataChangePrivate : public GeoDataContainerPrivate { }; GeoDataChange::GeoDataChange() : GeoDataContainer(new GeoDataChangePrivate) { } GeoDataChange::GeoDataChange(const GeoDataChange &other) : GeoDataContainer(other, new GeoDataChangePrivate(*other.d_func())) { } GeoDataChange &GeoDataChange::operator=( const GeoDataChange &other ) { if (this != &other) { Q_D(GeoDataChange); *d = *other.d_func(); } return *this; } bool GeoDataChange::operator==( const GeoDataChange &other ) const { - if ( !GeoDataContainer::equals(other) ){ - return false; - } - return true; + return GeoDataContainer::equals(other); } bool GeoDataChange::operator!=( const GeoDataChange &other ) const { return !this->operator==( other ); } GeoDataChange::~GeoDataChange() { } GeoDataFeature * GeoDataChange::clone() const { return new GeoDataChange(*this); } const char *GeoDataChange::nodeType() const { return GeoDataTypes::GeoDataChangeType; } } diff --git a/src/lib/marble/geodata/data/GeoDataCreate.cpp b/src/lib/marble/geodata/data/GeoDataCreate.cpp index 9e9fc6a7a..ae1bf0bcf 100644 --- a/src/lib/marble/geodata/data/GeoDataCreate.cpp +++ b/src/lib/marble/geodata/data/GeoDataCreate.cpp @@ -1,71 +1,68 @@ // // 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 2014 Sanjiban Bairagya // #include "GeoDataCreate.h" #include "GeoDataContainer_p.h" #include "GeoDataTypes.h" namespace Marble { class GeoDataCreatePrivate : public GeoDataContainerPrivate { }; GeoDataCreate::GeoDataCreate() : GeoDataContainer(new GeoDataCreatePrivate) { } GeoDataCreate::GeoDataCreate(const GeoDataCreate &other) : GeoDataContainer(other, new GeoDataCreatePrivate(*other.d_func())) { } GeoDataCreate &GeoDataCreate::operator=( const GeoDataCreate &other ) { if (this != &other) { Q_D(GeoDataCreate); *d = *other.d_func(); } return *this; } bool GeoDataCreate::operator==( const GeoDataCreate &other ) const { - if ( !GeoDataContainer::equals(other) ){ - return false; - } - return true; + return GeoDataContainer::equals(other); } bool GeoDataCreate::operator!=( const GeoDataCreate &other ) const { return !this->operator==( other ); } GeoDataCreate::~GeoDataCreate() { } GeoDataFeature * GeoDataCreate::clone() const { return new GeoDataCreate(*this); } const char *GeoDataCreate::nodeType() const { return GeoDataTypes::GeoDataCreateType; } } diff --git a/src/lib/marble/geodata/data/GeoDataDelete.cpp b/src/lib/marble/geodata/data/GeoDataDelete.cpp index ae4b35085..6b39d5b8d 100644 --- a/src/lib/marble/geodata/data/GeoDataDelete.cpp +++ b/src/lib/marble/geodata/data/GeoDataDelete.cpp @@ -1,71 +1,68 @@ // // 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 2014 Sanjiban Bairagya // #include "GeoDataDelete.h" #include "GeoDataContainer_p.h" #include "GeoDataTypes.h" namespace Marble { class GeoDataDeletePrivate : public GeoDataContainerPrivate { }; GeoDataDelete::GeoDataDelete() : GeoDataContainer(new GeoDataDeletePrivate) { } GeoDataDelete::GeoDataDelete(const GeoDataDelete &other) : GeoDataContainer(other, new GeoDataDeletePrivate(*other.d_func())) { } GeoDataDelete &GeoDataDelete::operator=( const GeoDataDelete &other ) { if (this != &other) { Q_D(GeoDataDelete); *d = *other.d_func(); } return *this; } bool GeoDataDelete::operator==( const GeoDataDelete &other ) const { - if ( !GeoDataContainer::equals(other) ){ - return false; - } - return true; + return GeoDataContainer::equals(other); } bool GeoDataDelete::operator!=( const GeoDataDelete &other ) const { return !this->operator==( other ); } GeoDataDelete::~GeoDataDelete() { } GeoDataFeature * GeoDataDelete::clone() const { return new GeoDataDelete(*this); } const char *GeoDataDelete::nodeType() const { return GeoDataTypes::GeoDataDeleteType; } } diff --git a/src/lib/marble/geodata/data/GeoDataLatLonAltBox.cpp b/src/lib/marble/geodata/data/GeoDataLatLonAltBox.cpp index 04c926678..ca14a7ce9 100644 --- a/src/lib/marble/geodata/data/GeoDataLatLonAltBox.cpp +++ b/src/lib/marble/geodata/data/GeoDataLatLonAltBox.cpp @@ -1,314 +1,311 @@ // // 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 2007 Andrew Manson // Copyright 2008 Torsten Rahn // #include "GeoDataLatLonAltBox.h" #include "MarbleDebug.h" #include "GeoDataCoordinates.h" #include "GeoDataLineString.h" #include "GeoDataTypes.h" #include namespace Marble { class GeoDataLatLonAltBoxPrivate { public: GeoDataLatLonAltBoxPrivate() : m_minAltitude( 0 ), m_maxAltitude( 0 ), m_altitudeMode( ClampToGround ) { } const char* nodeType() const { return GeoDataTypes::GeoDataLatLonAltBoxType; } qreal m_minAltitude; qreal m_maxAltitude; AltitudeMode m_altitudeMode; }; bool operator==( GeoDataLatLonAltBox const& lhs, GeoDataLatLonAltBox const& rhs ) { return lhs.west() == rhs.west() && lhs.east() == rhs.east() && lhs.north() == rhs.north() && lhs.south() == rhs.south() && lhs.rotation() == rhs.rotation() && lhs.d->m_minAltitude == rhs.d->m_minAltitude && lhs.d->m_maxAltitude == rhs.d->m_maxAltitude && lhs.d->m_altitudeMode == rhs.d->m_altitudeMode; } GeoDataLatLonAltBox& GeoDataLatLonAltBox::operator=( const GeoDataLatLonAltBox &other ) { GeoDataLatLonBox::operator=( other ); *d = *other.d; return *this; } GeoDataLatLonAltBox& GeoDataLatLonAltBox::operator=( const GeoDataCoordinates &other ) { setWest( other.longitude() ); setEast( other.longitude() ); setNorth( other.latitude() ); setSouth( other.latitude() ); setMinAltitude( other.altitude() ); setMaxAltitude( other.altitude() ); return *this; } GeoDataLatLonAltBox::GeoDataLatLonAltBox() : GeoDataLatLonBox(), d( new GeoDataLatLonAltBoxPrivate ) { } GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataLatLonAltBox & other ) : GeoDataLatLonBox( other ), d( new GeoDataLatLonAltBoxPrivate( *other.d )) { } GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataLatLonBox &other, qreal minAltitude, qreal maxAltitude ) : GeoDataLatLonBox( other ), d( new GeoDataLatLonAltBoxPrivate ) { setWest( other.west() ); setEast( other.east() ); setNorth( other.north() ); setSouth( other.south() ); setRotation( other.rotation() ); d->m_minAltitude = minAltitude; d->m_maxAltitude = maxAltitude; } GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataCoordinates & coordinates ) : GeoDataLatLonBox(), d( new GeoDataLatLonAltBoxPrivate ) { setWest( coordinates.longitude() ); setEast( coordinates.longitude() ); setNorth( coordinates.latitude() ); setSouth( coordinates.latitude() ); d->m_minAltitude = coordinates.altitude(); d->m_maxAltitude = coordinates.altitude(); } GeoDataLatLonAltBox::~GeoDataLatLonAltBox() { delete d; } const char* GeoDataLatLonAltBox::nodeType() const { return d->nodeType(); } qreal GeoDataLatLonAltBox::minAltitude() const { return d->m_minAltitude; } void GeoDataLatLonAltBox::setMinAltitude( const qreal minAltitude ) { d->m_minAltitude = minAltitude; } qreal GeoDataLatLonAltBox::maxAltitude() const { return d->m_maxAltitude; } void GeoDataLatLonAltBox::setMaxAltitude( const qreal maxAltitude ) { d->m_maxAltitude = maxAltitude; } AltitudeMode GeoDataLatLonAltBox::altitudeMode() const { return d->m_altitudeMode; } GeoDataCoordinates GeoDataLatLonAltBox::center() const { if ( isEmpty() ) return GeoDataCoordinates(); if( crossesDateLine() ) return GeoDataCoordinates( GeoDataCoordinates::normalizeLon(east() + 2 * M_PI - (east() + 2 * M_PI - west()) / 2), north() - (north() - south()) / 2, d->m_maxAltitude - (d->m_maxAltitude - d->m_minAltitude) / 2); else return GeoDataCoordinates( east() - (east() - west()) / 2, north() - (north() - south()) / 2, d->m_maxAltitude - (d->m_maxAltitude - d->m_minAltitude) / 2); } void GeoDataLatLonAltBox::setAltitudeMode( const AltitudeMode altitudeMode ) { d->m_altitudeMode = altitudeMode; } bool GeoDataLatLonAltBox::contains( const GeoDataCoordinates &point ) const { if ( !GeoDataLatLonBox::contains( point ) ) return false; if ( point.altitude() < d->m_minAltitude || point.altitude() > d->m_maxAltitude ) { return false; } return true; } bool GeoDataLatLonAltBox::contains( const GeoDataLatLonAltBox &other ) const { // check the contain criterion for the altitude first as this is trivial: // mDebug() << "this " << this->toString(GeoDataCoordinates::Degree); // mDebug() << "other" << other.toString(GeoDataCoordinates::Degree); if ( d->m_maxAltitude >= other.maxAltitude() && d->m_minAltitude <= other.minAltitude() ) { return GeoDataLatLonBox::contains( other ); } return false; } bool GeoDataLatLonAltBox::intersects( const GeoDataLatLonAltBox &other ) const { // Case 1: maximum altitude of other box intersects: if ( ( d->m_maxAltitude >= other.maxAltitude() && d->m_minAltitude <= other.maxAltitude() ) // Case 2: maximum altitude of this box intersects: || ( other.maxAltitude() >= d->m_maxAltitude && other.minAltitude() <= d->m_maxAltitude ) // Case 3: minimum altitude of other box intersects: || ( d->m_maxAltitude >= other.minAltitude() && d->m_minAltitude <= other.minAltitude() ) // Case 4: minimum altitude of this box intersects: || ( other.maxAltitude() >= d->m_minAltitude && other.minAltitude() <= d->m_minAltitude ) ) { if ( GeoDataLatLonBox::intersects( other ) ) return true; } return false; } GeoDataLatLonAltBox GeoDataLatLonAltBox::fromLineString( const GeoDataLineString& lineString ) { // If the line string is empty return a boundingbox that contains everything if ( lineString.size() == 0 ) { return GeoDataLatLonAltBox(); } const qreal altitude = lineString.first().altitude(); GeoDataLatLonAltBox temp ( GeoDataLatLonBox::fromLineString( lineString ), altitude, altitude ); qreal maxAltitude = altitude; qreal minAltitude = altitude; // If there's only a single node stored then the boundingbox only contains that point if ( lineString.size() == 1 ) { temp.setMinAltitude( minAltitude ); temp.setMaxAltitude( maxAltitude ); return temp; } QVector::ConstIterator it( lineString.constBegin() ); QVector::ConstIterator itEnd( lineString.constEnd() ); for ( ; it != itEnd; ++it ) { // Get coordinates and normalize them to the desired range. const qreal altitude = (it)->altitude(); // Determining the maximum and minimum altitude if ( altitude > maxAltitude ) { maxAltitude = altitude; } else if ( altitude < minAltitude ) { minAltitude = altitude; } } temp.setMinAltitude( minAltitude ); temp.setMaxAltitude( maxAltitude ); return temp; } QString GeoDataLatLonAltBox::toString( GeoDataCoordinates::Unit unit ) const { switch( unit ){ case GeoDataCoordinates::Radian: return QString( "North: %1; West: %2 MaxAlt: %3\n South: %4; East: %5 MinAlt: %6" ) .arg( north() ).arg( west() ) .arg( d->m_maxAltitude ).arg( south() ) .arg( east() ).arg( d->m_minAltitude ); break; case GeoDataCoordinates::Degree: return QString( "North: %1; West: %2 MaxAlt: %3\n South: %4; East: %5 MinAlt: %6" ) .arg( north() * RAD2DEG ).arg( west() * RAD2DEG ) .arg( d->m_maxAltitude ).arg( south() * RAD2DEG ) .arg( east() * RAD2DEG ).arg( d->m_minAltitude ); break; } return QString( "GeoDataLatLonAltBox::text(): Error in unit: %1\n" ) .arg( unit ); } bool GeoDataLatLonAltBox::isNull() const { - if ( GeoDataLatLonBox::isNull() && d->m_maxAltitude == d->m_minAltitude ) - return true; - - return false; + return GeoDataLatLonBox::isNull() && d->m_maxAltitude == d->m_minAltitude; } void GeoDataLatLonAltBox::clear() { GeoDataLatLonBox::clear(); d->m_minAltitude = 0; d->m_maxAltitude = 0; d->m_altitudeMode = ClampToGround; } void GeoDataLatLonAltBox::pack( QDataStream& stream ) const { GeoDataObject::pack( stream ); stream << d->m_minAltitude << d->m_maxAltitude; stream << d->m_altitudeMode; } void GeoDataLatLonAltBox::unpack( QDataStream& stream ) { GeoDataObject::unpack( stream ); stream >> d->m_minAltitude >> d->m_maxAltitude; int a; stream >> a; d->m_altitudeMode = static_cast( a ); } } diff --git a/src/lib/marble/geodata/data/GeoDataLatLonBox.cpp b/src/lib/marble/geodata/data/GeoDataLatLonBox.cpp index 6457aa6d4..2442fbebe 100644 --- a/src/lib/marble/geodata/data/GeoDataLatLonBox.cpp +++ b/src/lib/marble/geodata/data/GeoDataLatLonBox.cpp @@ -1,886 +1,880 @@ // // 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 2007 Andrew Manson // Copyright 2008-2009 Torsten Rahn // #include "GeoDataLatLonBox.h" #include "MarbleDebug.h" #include "GeoDataCoordinates.h" #include "GeoDataLineString.h" #include "GeoDataTypes.h" #include namespace Marble { const GeoDataLatLonBox GeoDataLatLonBox::empty = GeoDataLatLonBox(); class GeoDataLatLonBoxPrivate { public: GeoDataLatLonBoxPrivate() : m_north( 0.0 ), m_south( 0.0 ), m_east( 0.0 ), m_west( 0.0 ), m_rotation( 0.0 ) { } const char* nodeType() const { return GeoDataTypes::GeoDataLatLonBoxType; } qreal m_north; qreal m_south; qreal m_east; qreal m_west; qreal m_rotation; // NOT implemented yet! }; bool operator==( GeoDataLatLonBox const& lhs, GeoDataLatLonBox const& rhs ) { return lhs.d->m_west == rhs.d->m_west && lhs.d->m_east == rhs.d->m_east && lhs.d->m_north == rhs.d->m_north && lhs.d->m_south == rhs.d->m_south && lhs.d->m_rotation == rhs.d->m_rotation; } bool operator!=( GeoDataLatLonBox const& lhs, GeoDataLatLonBox const& rhs ) { return !( lhs == rhs ); } GeoDataLatLonBox::GeoDataLatLonBox() : GeoDataObject(), d( new GeoDataLatLonBoxPrivate ) { } GeoDataLatLonBox::GeoDataLatLonBox( qreal north, qreal south, qreal east, qreal west, GeoDataCoordinates::Unit unit ) : GeoDataObject(), d( new GeoDataLatLonBoxPrivate ) { setBoundaries( north, south, east, west, unit ); } GeoDataLatLonBox::GeoDataLatLonBox( const GeoDataLatLonBox & other ) : GeoDataObject( other ), d( new GeoDataLatLonBoxPrivate( *other.d ) ) { } GeoDataLatLonBox::~GeoDataLatLonBox() { delete d; } const char* GeoDataLatLonBox::nodeType() const { return d->nodeType(); } qreal GeoDataLatLonBox::north( GeoDataCoordinates::Unit unit ) const { if ( unit == GeoDataCoordinates::Degree ) { return d->m_north * RAD2DEG; } return d->m_north; } void GeoDataLatLonBox::setNorth( const qreal north, GeoDataCoordinates::Unit unit ) { switch( unit ){ default: case GeoDataCoordinates::Radian: d->m_north = GeoDataCoordinates::normalizeLat( north ); break; case GeoDataCoordinates::Degree: d->m_north = GeoDataCoordinates::normalizeLat( north * DEG2RAD ); break; } } qreal GeoDataLatLonBox::south( GeoDataCoordinates::Unit unit ) const { if ( unit == GeoDataCoordinates::Degree ) { return d->m_south * RAD2DEG; } return d->m_south; } void GeoDataLatLonBox::setSouth( const qreal south, GeoDataCoordinates::Unit unit ) { switch( unit ){ default: case GeoDataCoordinates::Radian: d->m_south = GeoDataCoordinates::normalizeLat( south ); break; case GeoDataCoordinates::Degree: d->m_south = GeoDataCoordinates::normalizeLat( south * DEG2RAD ); break; } } qreal GeoDataLatLonBox::east( GeoDataCoordinates::Unit unit ) const { if ( unit == GeoDataCoordinates::Degree ) { return d->m_east * RAD2DEG; } return d->m_east; } void GeoDataLatLonBox::setEast( const qreal east, GeoDataCoordinates::Unit unit ) { switch( unit ){ default: case GeoDataCoordinates::Radian: d->m_east = GeoDataCoordinates::normalizeLon( east ); break; case GeoDataCoordinates::Degree: d->m_east = GeoDataCoordinates::normalizeLon( east * DEG2RAD ); break; } } qreal GeoDataLatLonBox::west( GeoDataCoordinates::Unit unit ) const { if ( unit == GeoDataCoordinates::Degree ) { return d->m_west * RAD2DEG; } return d->m_west; } void GeoDataLatLonBox::setWest( const qreal west, GeoDataCoordinates::Unit unit ) { switch( unit ){ default: case GeoDataCoordinates::Radian: d->m_west = GeoDataCoordinates::normalizeLon( west ); break; case GeoDataCoordinates::Degree: d->m_west = GeoDataCoordinates::normalizeLon( west * DEG2RAD ); break; } } void GeoDataLatLonBox::setRotation( const qreal rotation, GeoDataCoordinates::Unit unit ) { switch( unit ){ default: case GeoDataCoordinates::Radian: d->m_rotation = rotation; break; case GeoDataCoordinates::Degree: d->m_rotation = rotation * DEG2RAD; break; } } qreal GeoDataLatLonBox::rotation( GeoDataCoordinates::Unit unit ) const { if ( unit == GeoDataCoordinates::Degree ) { return d->m_rotation * RAD2DEG; } return d->m_rotation; } void GeoDataLatLonBox::boundaries( qreal &north, qreal &south, qreal &east, qreal &west, GeoDataCoordinates::Unit unit ) const { switch( unit ){ default: case GeoDataCoordinates::Radian: north = d->m_north; south = d->m_south; east = d->m_east; west = d->m_west; break; case GeoDataCoordinates::Degree: north = d->m_north * RAD2DEG; south = d->m_south * RAD2DEG; east = d->m_east * RAD2DEG; west = d->m_west * RAD2DEG; break; } } void GeoDataLatLonBox::setBoundaries( qreal north, qreal south, qreal east, qreal west, GeoDataCoordinates::Unit unit ) { switch( unit ){ default: case GeoDataCoordinates::Radian: d->m_north = GeoDataCoordinates::normalizeLat( north ); d->m_south = GeoDataCoordinates::normalizeLat( south ); d->m_east = GeoDataCoordinates::normalizeLon( east ); d->m_west = GeoDataCoordinates::normalizeLon( west ); break; case GeoDataCoordinates::Degree: d->m_north = GeoDataCoordinates::normalizeLat( north * DEG2RAD ); d->m_south = GeoDataCoordinates::normalizeLat( south * DEG2RAD ); d->m_east = GeoDataCoordinates::normalizeLon( east * DEG2RAD ); d->m_west = GeoDataCoordinates::normalizeLon( west * DEG2RAD ); break; } } void GeoDataLatLonBox::scale(qreal verticalFactor, qreal horizontalFactor) const { GeoDataCoordinates const middle = center(); qreal const deltaY = 0.5 * height() * verticalFactor; qreal const deltaX = 0.5 * width() * horizontalFactor; d->m_north = qMin((middle.latitude() + deltaY), static_cast(M_PI/2)); d->m_south = qMax((middle.latitude() - deltaY), static_cast(-M_PI/2)); if (deltaX > 180 * DEG2RAD) { d->m_east = M_PI; d->m_west = -M_PI; } else { d->m_east = GeoDataCoordinates::normalizeLon(middle.longitude() + deltaX); d->m_west = GeoDataCoordinates::normalizeLon(middle.longitude() - deltaX); } } GeoDataLatLonBox GeoDataLatLonBox::scaled(qreal verticalFactor, qreal horizontalFactor) const { GeoDataLatLonBox result = *this; result.scale(verticalFactor, horizontalFactor); return result; } qreal GeoDataLatLonBox::width( GeoDataCoordinates::Unit unit ) const { return GeoDataLatLonBox::width( d->m_east, d->m_west, unit ); } qreal GeoDataLatLonBox::width( qreal east, qreal west, GeoDataCoordinates::Unit unit ) { qreal width = fabs( (qreal)( GeoDataLatLonBox::crossesDateLine(east, west) ? 2 * M_PI - west + east : east - west ) ); // This also covers the case where this bounding box covers the whole // longitude range ( -180 <= lon <= + 180 ). if ( width > 2 * M_PI ) { width = 2 * M_PI; } if ( unit == GeoDataCoordinates::Degree ) { return width * RAD2DEG; } return width; } qreal GeoDataLatLonBox::height( GeoDataCoordinates::Unit unit ) const { return GeoDataLatLonBox::height(d->m_north, d->m_south, unit); } qreal GeoDataLatLonBox::height(qreal north, qreal south, GeoDataCoordinates::Unit unit) { qreal height = fabs( (qreal)( south - north ) ); if ( unit == GeoDataCoordinates::Degree ) { return height * RAD2DEG; } return height; } bool GeoDataLatLonBox::crossesDateLine() const { return GeoDataLatLonBox::crossesDateLine(d->m_east, d->m_west); } bool GeoDataLatLonBox::crossesDateLine(qreal east, qreal west) { - if ( east < west || ( east == M_PI && west == -M_PI ) ) { - return true; - } - return false; + return east < west || ( east == M_PI && west == -M_PI ); } GeoDataCoordinates GeoDataLatLonBox::center() const { if( isEmpty() ) return GeoDataCoordinates(); if( crossesDateLine() ) return GeoDataCoordinates( GeoDataCoordinates::normalizeLon( east() + 2 * M_PI - ( east() + 2 * M_PI - west() ) / 2 ) , north() - ( north() - south() ) / 2 ); else return GeoDataCoordinates( east() - ( east() - west() ) / 2, north() - ( north() - south() ) / 2 ); } bool GeoDataLatLonBox::containsPole( Pole pole ) const { switch ( pole ) { case NorthPole: return ( 2 * north() == +M_PI ); break; case SouthPole: return ( 2 * south() == -M_PI ); break; default: case AnyPole: return ( 2 * north() == +M_PI || 2 * south() == -M_PI ); break; } mDebug() << Q_FUNC_INFO << "Invalid pole"; return false; } bool GeoDataLatLonBox::contains(qreal lon, qreal lat) const { if ( lat < d->m_south || lat > d->m_north ) { return false; } // We need to take care of the normal case ... if ( ( ( lon < d->m_west || lon > d->m_east ) && ( d->m_west < d->m_east ) ) || // ... and the case where the bounding box crosses the date line: ( ( lon < d->m_west && lon > d->m_east ) && ( d->m_west > d->m_east ) ) ) return false; return true; } bool GeoDataLatLonBox::contains( const GeoDataCoordinates &point ) const { qreal lon, lat; point.geoCoordinates( lon, lat ); return contains(lon, lat); } bool GeoDataLatLonBox::contains( const GeoDataLatLonBox &other ) const { // check the contain criterion for the latitude first as this is trivial: if ( d->m_north >= other.north() && d->m_south <= other.south() ) { if ( !crossesDateLine() ) { if ( !other.crossesDateLine() ) { // "Normal" case: both bounding boxes don't cross the date line if ( d->m_west <= other.west() && d->m_east >= other.east() ) { return true; } } else { // The other bounding box crosses the date line, "this" one does not: // So the date line splits the other bounding box in two parts. // Hence "this" bounding box could be fully contained by one of them. // So for both cases we are able to ignore the "overhanging" portion // and thereby basically reduce the problem to the "normal" case: if ( ( other.west() <= d->m_west && d->m_east <= +M_PI ) || ( other.east() >= d->m_east && d->m_west >= -M_PI ) ) { return true; } } } else { if ( other.crossesDateLine() ) { // Other "Simple" case: both bounding boxes cross the date line if ( d->m_west <= other.west() && d->m_east >= other.east() ) { return true; } } else { // "This" bounding box crosses the date line, the other one does not. // So the date line splits "this" bounding box in two parts. // Hence the other bounding box could be fully contained by one of them. // So for both cases we are able to ignore the "overhanging" portion // and thereby basically reduce the problem to the "normal" case: if ( ( d->m_west <= other.west() && other.east() <= +M_PI ) || ( d->m_east >= other.east() && other.west() >= -M_PI ) ) { return true; } // if this bounding box covers the whole longitude range ( -180 <= lon <= + 180 ) // then of course the "inner" bounding box is "inside" if ( d->m_west == -M_PI && d->m_east == +M_PI ) { return true; } } } } return false; } bool GeoDataLatLonBox::intersects( const GeoDataLatLonBox &other ) const { if ( isEmpty() || other.isEmpty() ) { return false; } // check the intersection criterion for the latitude first: // Case 1: northern boundary of other box intersects: if ( (d->m_north >= other.d->m_north && d->m_south <= other.d->m_north) // Case 2: northern boundary of this box intersects: || (other.d->m_north >= d->m_north && other.d->m_south <= d->m_north) // Case 3: southern boundary of other box intersects: || (d->m_north >= other.d->m_south && d->m_south <= other.d->m_south) // Case 4: southern boundary of this box intersects: || (other.d->m_north >= d->m_south && other.d->m_south <= d->m_south)) { if ( !crossesDateLine() ) { if ( !other.crossesDateLine() ) { // "Normal" case: both bounding boxes don't cross the date line // Case 1: eastern boundary of other box intersects: if ( (d->m_east >= other.d->m_east && d->m_west <= other.d->m_east) // Case 2: eastern boundary of this box intersects: || (other.d->m_east >= d->m_east && other.d->m_west <= d->m_east) // Case 3: western boundary of other box intersects: || (d->m_east >= other.d->m_west && d->m_west <= other.d->m_west) // Case 4: western boundary of this box intersects: || (other.d->m_east >= d->m_west && other.d->m_west <= d->m_west)) { return true; } } else { // The other bounding box crosses the date line, "this" one does not: // So the date line splits the other bounding box in two parts. if ( d->m_west <= other.d->m_east || d->m_east >= other.d->m_west) { return true; } } } else { if ( other.crossesDateLine() ) { // The trivial case: both bounding boxes cross the date line and intersect return true; } else { // "This" bounding box crosses the date line, the other one does not. // So the date line splits "this" bounding box in two parts. // // This also covers the case where this bounding box covers the whole // longitude range ( -180 <= lon <= + 180 ). if ( other.d->m_west <= d->m_east || other.d->m_east >= d->m_west ) { return true; } } } } return false; } GeoDataLatLonBox GeoDataLatLonBox::united( const GeoDataLatLonBox& other ) const { if ( isEmpty() ) { return other; } if ( other.isEmpty() ) { return *this; } GeoDataLatLonBox result; // use the position of the centers of the boxes to determine the "smallest" // box (i.e. should the total box go through IDL or not). this // determination does not depend on one box or the other crossing IDL too GeoDataCoordinates c1 = center(); GeoDataCoordinates c2 = other.center(); // do latitude first, quite simple result.setNorth(qMax( d->m_north, other.north() ) ); result.setSouth( qMin( d->m_south, other.south() ) ); qreal w1 = d->m_west; qreal w2 = other.west(); qreal e1 = d->m_east; qreal e2 = other.east(); bool const idl1 = d->m_east < d->m_west; bool const idl2 = other.d->m_east < other.d->m_west; if ( idl1 ) { w1 += 2* M_PI; e1 += 2* M_PI; } if ( idl2 ) { w2 += 2* M_PI; e2 += 2* M_PI; } // in the usual case, we take the maximum of east bounds, and // the minimum of west bounds. The exceptions are: // - centers of boxes are more than 180 apart // (so the smallest box should go around the IDL) // // - 1 but not 2 boxes are crossing IDL if ( fabs( c2.longitude()-c1.longitude() ) > M_PI || ( idl1 ^ idl2 ) ) { // exceptions, we go the unusual way: // min of east, max of west result.setEast( qMin( e1, e2 ) ); result.setWest( qMax( w1, w2 ) ); } else { // normal case, max of east, min of west result.setEast( qMax( e1, e2 ) ); result.setWest( qMin( w1, w2 ) ); } return result; } GeoDataLatLonBox GeoDataLatLonBox::toCircumscribedRectangle() const { QVector coordinates; coordinates.reserve(4); coordinates.append( GeoDataCoordinates( west(), north() ) ); coordinates.append( GeoDataCoordinates( west(), south() ) ); coordinates.append( GeoDataCoordinates( east() + ( crossesDateLine() ? 2 * M_PI : 0 ), north() ) ); coordinates.append( GeoDataCoordinates( east() + ( crossesDateLine() ? 2 * M_PI : 0 ), south() ) ); const qreal cosRotation = cos( rotation() ); const qreal sinRotation = sin( rotation() ); qreal centerLat = center().latitude(); qreal centerLon = center().longitude(); if ( GeoDataLatLonBox( 0, 0, center().longitude(), west() ).crossesDateLine() ) { if ( !centerLon ) centerLon += M_PI; else centerLon += 2 * M_PI; } GeoDataLatLonBox box; bool northSet = false; bool southSet = false; bool eastSet = false; bool westSet = false; for ( const GeoDataCoordinates& coord: coordinates ) { const qreal lon = coord.longitude(); const qreal lat = coord.latitude(); const qreal rotatedLon = ( lon - centerLon ) * cosRotation - ( lat - centerLat ) * sinRotation + centerLon; const qreal rotatedLat = ( lon - centerLon ) * sinRotation + ( lat - centerLat ) * cosRotation + centerLat; if ( !northSet || rotatedLat > box.north() ) { northSet = true; box.setNorth( rotatedLat ); } if ( !southSet || rotatedLat < box.south() ) { southSet = true; box.setSouth( rotatedLat ); } if ( !westSet || rotatedLon < box.west() ) { westSet = true; box.setWest( rotatedLon ); } if ( !eastSet || rotatedLon > box.east() ) { eastSet = true; box.setEast( rotatedLon ); } } box.setBoundaries( GeoDataCoordinates::normalizeLat( box.north() ), GeoDataCoordinates::normalizeLat( box.south() ), GeoDataCoordinates::normalizeLon( box.east() ), GeoDataCoordinates::normalizeLon( box.west() ) ); return box; } QString GeoDataLatLonBox::toString( GeoDataCoordinates::Unit unit ) const { switch( unit ){ default: case GeoDataCoordinates::Radian: return QString( "North: %1; West: %2; South: %3; East: %4" ) .arg( d->m_north ).arg( d->m_west ).arg( d->m_south ).arg( d->m_east ); break; case GeoDataCoordinates::Degree: return QString( "North: %1; West: %2; South: %3; East: %4" ) .arg( d->m_north * RAD2DEG ).arg( d->m_west * RAD2DEG ).arg( d->m_south * RAD2DEG ).arg( d->m_east * RAD2DEG ); break; } return QString( "GeoDataLatLonBox::text(): Error in unit: %1\n" ) .arg( unit ); } GeoDataLatLonBox& GeoDataLatLonBox::operator=( const GeoDataLatLonBox &other ) { GeoDataObject::operator=( other ); *d = *other.d; return *this; } GeoDataLatLonBox GeoDataLatLonBox::operator|( const GeoDataLatLonBox& other ) const { return united( other ); } GeoDataLatLonBox& GeoDataLatLonBox::operator|=( const GeoDataLatLonBox& other ) { *this = united( other ); return *this; } void GeoDataLatLonBox::pack( QDataStream& stream ) const { GeoDataObject::pack( stream ); stream << d->m_north << d->m_south << d->m_east << d->m_west << d->m_rotation; } void GeoDataLatLonBox::unpack( QDataStream& stream ) { GeoDataObject::unpack( stream ); stream >> d->m_north >> d->m_south >> d->m_east >> d->m_west >> d->m_rotation; } GeoDataLatLonBox GeoDataLatLonBox::fromLineString( const GeoDataLineString& lineString ) { // If the line string is empty return an empty boundingbox if ( lineString.isEmpty() ) { return GeoDataLatLonBox(); } qreal lon, lat; lineString.first().geoCoordinates( lon, lat ); GeoDataCoordinates::normalizeLonLat( lon, lat ); qreal north = lat; qreal south = lat; qreal west = lon; qreal east = lon; // If there's only a single node stored then the boundingbox only contains that point if ( lineString.size() == 1 ) return GeoDataLatLonBox( north, south, east, west ); // Specifies whether the polygon crosses the IDL bool idlCrossed = false; // "idlCrossState" specifies the state concerning IDL crossage. // This is needed in order to create optimal bounding boxes in case of covering the IDL // Every time the IDL gets crossed from east to west the idlCrossState value gets // increased by one. // Every time the IDL gets crossed from west to east the idlCrossState value gets // decreased by one. int idlCrossState = 0; int idlMaxCrossState = 0; int idlMinCrossState = 0; // Holds values for east and west while idlCrossState != 0 qreal otherWest = lon; qreal otherEast = lon; qreal previousLon = lon; int currentSign = ( lon < 0 ) ? -1 : +1; int previousSign = currentSign; QVector::ConstIterator it( lineString.constBegin() ); QVector::ConstIterator itEnd( lineString.constEnd() ); bool processingLastNode = false; while( it != itEnd ) { // Get coordinates and normalize them to the desired range. (it)->geoCoordinates( lon, lat ); GeoDataCoordinates::normalizeLonLat( lon, lat ); // Determining the maximum and minimum latitude if ( lat > north ) { north = lat; } else if ( lat < south ) { south = lat; } currentSign = ( lon < 0 ) ? -1 : +1; // Once the polyline crosses the dateline the covered bounding box // would cover the whole [-M_PI; M_PI] range. // When looking separately at the longitude range that gets covered // east and west from the IDL we get two bounding boxes (we prefix // the resulting longitude range on the "other side" with "other"). // By picking the "inner" range values we get a more appropriate // optimized single bounding box. // IDL check if ( previousSign != currentSign && fabs( previousLon ) + fabs( lon ) > M_PI ) { // Initialize values for otherWest and otherEast if ( idlCrossed == false ) { otherWest = lon; otherEast = lon; idlCrossed = true; } // Determine the new IDL Cross State if ( previousLon < 0 ) { idlCrossState++; if ( idlCrossState > idlMaxCrossState ) { idlMaxCrossState = idlCrossState; } } else { idlCrossState--; if ( idlCrossState < idlMinCrossState ) { idlMinCrossState = idlCrossState; } } } if ( idlCrossState == 0 ) { if ( lon > east ) east = lon; if ( lon < west ) west = lon; } else { if ( lon > otherEast ) otherEast = lon; if ( lon < otherWest ) otherWest = lon; } previousLon = lon; previousSign = currentSign; if ( processingLastNode ) { break; } ++it; if( lineString.isClosed() && it == itEnd ) { it = lineString.constBegin(); processingLastNode = true; } } if ( idlCrossed ) { if ( idlMinCrossState < 0 ) { east = otherEast; } if ( idlMaxCrossState > 0 ) { west = otherWest; } if ( ( idlMinCrossState < 0 && idlMaxCrossState > 0 ) || idlMinCrossState < -1 || idlMaxCrossState > 1 || west <= east ) { east = +M_PI; west = -M_PI; // if polygon fully in south hemisphere, contain south pole if( north < 0 ) { south = -M_PI/2; } else { north = M_PI/2; } } } return GeoDataLatLonBox( north, south, east, west ); } bool GeoDataLatLonBox::isNull() const { - if ( d->m_north == d->m_south && d->m_east == d->m_west ) - return true; - - return false; + return d->m_north == d->m_south && d->m_east == d->m_west; } bool GeoDataLatLonBox::isEmpty() const { return *this == empty; } bool GeoDataLatLonBox::fuzzyCompare(const GeoDataLatLonBox& lhs, const GeoDataLatLonBox& rhs, const qreal factor) { bool equal = true; // Check the latitude for approximate equality double latDelta = lhs.height() * factor; if (fabs(lhs.north() - rhs.north()) > latDelta) equal = false; if (fabs(lhs.south() - rhs.south()) > latDelta) equal = false; // Check the longitude for approximate equality double lonDelta = lhs.width() * factor; double lhsEast = lhs.east(); double rhsEast = rhs.east(); if (!GeoDataLatLonBox::crossesDateLine(lhsEast, rhsEast)) { if (fabs(lhsEast - rhsEast) > lonDelta) equal = false; } else { lhsEast = GeoDataCoordinates::normalizeLat( lhsEast ); rhsEast = GeoDataCoordinates::normalizeLat( rhsEast ); if (lhsEast < 0 && rhsEast > 0) { lhsEast += 2 * M_PI; if (fabs(lhsEast - rhsEast) > lonDelta) equal = false; } if (lhsEast > 0 && rhsEast < 0) { rhsEast += 2 * M_PI; if (fabs(lhsEast - rhsEast) > lonDelta) equal = false; } } double lhsWest = lhs.west(); double rhsWest = rhs.west(); if (!GeoDataLatLonBox::crossesDateLine(lhsWest, rhsWest)) { if (fabs(lhsWest - rhsWest) > lonDelta) equal = false; } else { lhsWest = GeoDataCoordinates::normalizeLat( lhsWest ); rhsWest = GeoDataCoordinates::normalizeLat( rhsWest ); if (lhsWest < 0 && rhsWest > 0) { lhsWest += 2 * M_PI; if (fabs(lhsWest - rhsWest) > lonDelta) equal = false; } if (lhsWest > 0 && rhsWest < 0) { rhsWest += 2 * M_PI; if (fabs(lhsWest - rhsWest) > lonDelta) equal = false; } } return equal; } void GeoDataLatLonBox::clear() { *this = empty; } } diff --git a/src/lib/marble/projections/AzimuthalEquidistantProjection.cpp b/src/lib/marble/projections/AzimuthalEquidistantProjection.cpp index c8f4e6bb8..fb8c7a4af 100644 --- a/src/lib/marble/projections/AzimuthalEquidistantProjection.cpp +++ b/src/lib/marble/projections/AzimuthalEquidistantProjection.cpp @@ -1,187 +1,183 @@ // // 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 2014 Torsten Rahn // // Local #include "AzimuthalEquidistantProjection.h" #include "AbstractProjection_p.h" #include "MarbleDebug.h" // Marble #include "ViewportParams.h" #include "GeoDataPoint.h" #include "GeoDataLineString.h" #include "GeoDataCoordinates.h" #include "MarbleGlobal.h" #include "AzimuthalProjection_p.h" #include #include #define SAFE_DISTANCE namespace Marble { class AzimuthalEquidistantProjectionPrivate : public AzimuthalProjectionPrivate { public: explicit AzimuthalEquidistantProjectionPrivate( AzimuthalEquidistantProjection * parent ); Q_DECLARE_PUBLIC( AzimuthalEquidistantProjection ) }; AzimuthalEquidistantProjection::AzimuthalEquidistantProjection() : AzimuthalProjection( new AzimuthalEquidistantProjectionPrivate( this ) ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } AzimuthalEquidistantProjection::AzimuthalEquidistantProjection( AzimuthalEquidistantProjectionPrivate *dd ) : AzimuthalProjection( dd ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } AzimuthalEquidistantProjection::~AzimuthalEquidistantProjection() { } QString AzimuthalEquidistantProjection::name() const { return QObject::tr( "Azimuthal Equidistant" ); } QString AzimuthalEquidistantProjection::description() const { return QObject::tr( "

Azimuthal Equidistant Projection (\"fish eye\")

Applications: Display of seismic and radio data and for use in digital planetariums.

" ); } QIcon AzimuthalEquidistantProjection::icon() const { return QIcon(QStringLiteral(":/icons/map-globe.png")); } AzimuthalEquidistantProjectionPrivate::AzimuthalEquidistantProjectionPrivate( AzimuthalEquidistantProjection * parent ) : AzimuthalProjectionPrivate( parent ) { } qreal AzimuthalEquidistantProjection::clippingRadius() const { return 1; } bool AzimuthalEquidistantProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { const qreal lambda = coordinates.longitude(); const qreal phi = coordinates.latitude(); const qreal lambdaPrime = viewport->centerLongitude(); const qreal phi1 = viewport->centerLatitude(); qreal cosC = qSin( phi1 ) * qSin( phi ) + qCos( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ); // Prevent division by zero if (cosC <= 0) { globeHidesPoint = true; return false; } qreal c = qAcos(cosC); qreal k = cosC == 1 ? 1 : c / qSin( c ); // Let (x, y) be the position on the screen of the placemark.. x = ( qCos( phi ) * qSin( lambda - lambdaPrime ) ) * k; y = ( qCos( phi1 ) * qSin( phi ) - qSin( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ) ) * k; x *= 2 * viewport->radius() / M_PI; y *= 2 * viewport->radius() / M_PI; const qint64 radius = clippingRadius() * viewport->radius(); if (x*x + y*y > radius * radius) { globeHidesPoint = true; return false; } globeHidesPoint = false; x += viewport->width() / 2; y = viewport->height() / 2 - y; // Skip placemarks that are outside the screen area - if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) { - return false; - } - - return true; + return !(x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height()); } bool AzimuthalEquidistantProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const { pointRepeatNum = 0; globeHidesPoint = false; bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint ); // Skip placemarks that are outside the screen area if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0 || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 ) { return false; } // This projection doesn't have any repetitions, // so the number of screen points referring to the geopoint is one. pointRepeatNum = 1; return visible; } bool AzimuthalEquidistantProjection::geoCoordinates( const int x, const int y, const ViewportParams *viewport, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { const qint64 radius = viewport->radius(); // Calculate how many degrees are being represented per pixel. const qreal rad2Pixel = ( 2 * radius ) / M_PI; const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); const qreal rx = ( - viewport->width() / 2 + x ) / rad2Pixel; const qreal ry = ( viewport->height() / 2 - y ) / rad2Pixel; const qreal c = qMax( qSqrt( rx*rx + ry*ry ), qreal(0.0001) ); // ensure we don't divide by zero const qreal sinc = qSin(c); lon = centerLon + qAtan2( rx*sinc , ( c*qCos( centerLat )*qCos( c ) - ry*qSin( centerLat )*sinc ) ); while ( lon < -M_PI ) lon += 2 * M_PI; while ( lon > M_PI ) lon -= 2 * M_PI; lat = qAsin( qCos(c)*qSin(centerLat) + (ry*sinc*qCos(centerLat))/c ); if ( unit == GeoDataCoordinates::Degree ) { lon *= RAD2DEG; lat *= RAD2DEG; } return true; } } diff --git a/src/lib/marble/projections/EquirectProjection.cpp b/src/lib/marble/projections/EquirectProjection.cpp index 48f0120b0..30ed56d3b 100644 --- a/src/lib/marble/projections/EquirectProjection.cpp +++ b/src/lib/marble/projections/EquirectProjection.cpp @@ -1,286 +1,283 @@ // // 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 2007-2012 Torsten Rahn // Copyright 2007-2008 Inge Wallin // // Local #include "EquirectProjection.h" // Marble #include "ViewportParams.h" #include "GeoDataLatLonAltBox.h" #include "MarbleDebug.h" #include using namespace Marble; EquirectProjection::EquirectProjection() : CylindricalProjection() { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } EquirectProjection::~EquirectProjection() { } QString EquirectProjection::name() const { return QObject::tr( "Flat Map" ); } QString EquirectProjection::description() const { return QObject::tr( "

Equirectangular Projection (\"Plate carrée\")

Applications: De facto standard for global texture data sets for computer software.

" ); } QIcon EquirectProjection::icon() const { return QIcon(QStringLiteral(":/icons/map-flat.png")); } qreal EquirectProjection::maxValidLat() const { return +90.0 * DEG2RAD; } qreal EquirectProjection::minValidLat() const { return -90.0 * DEG2RAD; } bool EquirectProjection::screenCoordinates( const GeoDataCoordinates &geopoint, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { globeHidesPoint = false; // Convenience variables int radius = viewport->radius(); int width = viewport->width(); int height = viewport->height(); qreal lon; qreal lat; qreal rad2Pixel = 2.0 * viewport->radius() / M_PI; const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); geopoint.geoCoordinates( lon, lat ); // Let (x, y) be the position on the screen of the geopoint. x = ((qreal)(viewport->width()) / 2.0 + rad2Pixel * (lon - centerLon)); y = ((qreal)(viewport->height()) / 2.0 - rad2Pixel * (lat - centerLat)); // Return true if the calculated point is inside the screen area, // otherwise return false. return ( ( 0 <= y && y < height ) && ( ( 0 <= x && x < width ) || ( 0 <= x - 4 * radius && x - 4 * radius < width ) || ( 0 <= x + 4 * radius && x + 4 * radius < width ) ) ); } bool EquirectProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const { pointRepeatNum = 0; // On flat projections the observer's view onto the point won't be // obscured by the target planet itself. globeHidesPoint = false; // Convenience variables int radius = viewport->radius(); qreal width = (qreal)(viewport->width()); qreal height = (qreal)(viewport->height()); // Let (itX, y) be the first guess for one possible position on screen. qreal itX; screenCoordinates( coordinates, viewport, itX, y); // Make sure that the requested point is within the visible y range: if ( 0 <= y + size.height() / 2.0 && y < height + size.height() / 2.0 ) { // For the repetition case the same geopoint gets displayed on // the map many times.across the longitude. int xRepeatDistance = 4 * radius; // Finding the leftmost positive x value if ( itX + size.width() > xRepeatDistance ) { const int repeatNum = (int)( ( itX + size.width() ) / xRepeatDistance ); itX = itX - repeatNum * xRepeatDistance; } if ( itX + size.width() / 2.0 < 0 ) { itX += xRepeatDistance; } // The requested point is out of the visible x range: if ( itX > width + size.width() / 2.0 ) { return false; } // Now iterate through all visible x screen coordinates for the point // from left to right. int itNum = 0; while ( itX - size.width() / 2.0 < width ) { *x = itX; ++x; ++itNum; itX += xRepeatDistance; } pointRepeatNum = itNum; return true; } // The requested point is out of the visible y range. return false; } bool EquirectProjection::geoCoordinates( const int x, const int y, const ViewportParams *viewport, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { const int radius = viewport->radius(); const qreal pixel2Rad = M_PI / (2.0 * radius); // Get the Lat and Lon of the center point of the screen. const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); { const int halfImageWidth = viewport->width() / 2; const int xPixels = x - halfImageWidth; lon = + xPixels * pixel2Rad + centerLon; while ( lon > M_PI ) lon -= 2.0 * M_PI; while ( lon < -M_PI ) lon += 2.0 * M_PI; if ( unit == GeoDataCoordinates::Degree ) { lon *= RAD2DEG; } } { // Get yTop and yBottom, the limits of the map on the screen. const int halfImageHeight = viewport->height() / 2; const int yCenterOffset = (int)( centerLat * (qreal)(2 * radius) / M_PI); const int yTop = halfImageHeight - radius + yCenterOffset; const int yBottom = yTop + 2 * radius; // Return here if the y coordinate is outside the map if ( yTop <= y && y < yBottom ) { const int yPixels = y - halfImageHeight; lat = - yPixels * pixel2Rad + centerLat; if ( unit == GeoDataCoordinates::Degree ) { lat *= RAD2DEG; } return true; } } return false; } GeoDataLatLonAltBox EquirectProjection::latLonAltBox( const QRect& screenRect, const ViewportParams *viewport ) const { qreal west; qreal north = 90*DEG2RAD; geoCoordinates( screenRect.left(), screenRect.top(), viewport, west, north, GeoDataCoordinates::Radian ); qreal east; qreal south = -90*DEG2RAD; geoCoordinates( screenRect.right(), screenRect.bottom(), viewport, east, south, GeoDataCoordinates::Radian ); // For the case where the whole viewport gets covered there is a // pretty dirty and generic detection algorithm: GeoDataLatLonAltBox latLonAltBox; latLonAltBox.setNorth( north, GeoDataCoordinates::Radian ); latLonAltBox.setSouth( south, GeoDataCoordinates::Radian ); latLonAltBox.setWest( west, GeoDataCoordinates::Radian ); latLonAltBox.setEast( east, GeoDataCoordinates::Radian ); latLonAltBox.setMinAltitude( -100000000.0 ); latLonAltBox.setMaxAltitude( 100000000000000.0 ); // Convenience variables int radius = viewport->radius(); int width = viewport->width(); // The remaining algorithm should be pretty generic for all kinds of // flat projections: int xRepeatDistance = 4 * radius; if ( width >= xRepeatDistance ) { latLonAltBox.setWest( -M_PI ); latLonAltBox.setEast( +M_PI ); } // Now we need to check whether maxLat (e.g. the north pole) gets displayed // inside the viewport. // We need a point on the screen at maxLat that definitely gets displayed: qreal averageLongitude = latLonAltBox.east(); GeoDataCoordinates maxLatPoint( averageLongitude, maxLat(), 0.0, GeoDataCoordinates::Radian ); GeoDataCoordinates minLatPoint( averageLongitude, minLat(), 0.0, GeoDataCoordinates::Radian ); qreal dummyX, dummyY; // not needed if ( screenCoordinates( maxLatPoint, viewport, dummyX, dummyY ) ) { latLonAltBox.setEast( +M_PI ); latLonAltBox.setWest( -M_PI ); } if ( screenCoordinates( minLatPoint, viewport, dummyX, dummyY ) ) { latLonAltBox.setEast( +M_PI ); latLonAltBox.setWest( -M_PI ); } return latLonAltBox; } bool EquirectProjection::mapCoversViewport( const ViewportParams *viewport ) const { // Convenience variables int radius = viewport->radius(); //int width = viewport->width(); int height = viewport->height(); int halfImageHeight = viewport->height() / 2; // Get the Lat and Lon of the center point of the screen. const qreal centerLat = viewport->centerLatitude(); // Calculate how many pixel are being represented per radians. const float rad2Pixel = (qreal)( 2 * radius )/M_PI; // Get yTop and yBottom, the limits of the map on the screen. int yCenterOffset = (int)( centerLat * rad2Pixel ); int yTop = halfImageHeight - radius + yCenterOffset; int yBottom = yTop + 2 * radius; - if ( yTop >= 0 || yBottom < height ) - return false; - - return true; + return !(yTop >= 0 || yBottom < height); } diff --git a/src/lib/marble/projections/GnomonicProjection.cpp b/src/lib/marble/projections/GnomonicProjection.cpp index 7ed85d4b6..c9f0a56bf 100644 --- a/src/lib/marble/projections/GnomonicProjection.cpp +++ b/src/lib/marble/projections/GnomonicProjection.cpp @@ -1,184 +1,180 @@ // // 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 2013 Bernhard Beschow // // Local #include "GnomonicProjection.h" #include "AbstractProjection_p.h" #include "MarbleDebug.h" // Marble #include "ViewportParams.h" #include "GeoDataPoint.h" #include "GeoDataLineString.h" #include "GeoDataCoordinates.h" #include "MarbleGlobal.h" #include "AzimuthalProjection_p.h" #include #include #define SAFE_DISTANCE namespace Marble { class GnomonicProjectionPrivate : public AzimuthalProjectionPrivate { public: explicit GnomonicProjectionPrivate( GnomonicProjection * parent ); Q_DECLARE_PUBLIC( GnomonicProjection ) }; GnomonicProjection::GnomonicProjection() : AzimuthalProjection( new GnomonicProjectionPrivate( this ) ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } GnomonicProjection::GnomonicProjection( GnomonicProjectionPrivate *dd ) : AzimuthalProjection( dd ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } GnomonicProjection::~GnomonicProjection() { } GnomonicProjectionPrivate::GnomonicProjectionPrivate( GnomonicProjection * parent ) : AzimuthalProjectionPrivate( parent ) { } QString GnomonicProjection::name() const { return QObject::tr( "Gnomonic" ); } QString GnomonicProjection::description() const { return QObject::tr( "

Gnomonic Projection (\"rectilinear\")

Applications: Used for displaying panorama photography. Also used for navigation, radio and seismic work.

" ); } QIcon GnomonicProjection::icon() const { return QIcon(QStringLiteral(":/icons/map-gnomonic.png")); } qreal GnomonicProjection::clippingRadius() const { return 1; } bool GnomonicProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { const qreal lambda = coordinates.longitude(); const qreal phi = coordinates.latitude(); const qreal lambdaPrime = viewport->centerLongitude(); const qreal phi1 = viewport->centerLatitude(); qreal cosC = qSin( phi1 ) * qSin( phi ) + qCos( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ); if ( cosC <= 0) { globeHidesPoint = true; return false; } // Let (x, y) be the position on the screen of the placemark.. x = ( qCos( phi ) * qSin( lambda - lambdaPrime ) ) / cosC; y = ( qCos( phi1 ) * qSin( phi ) - qSin( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ) ) / cosC; x *= viewport->radius() / 2; y *= viewport->radius() / 2; const qint64 radius = clippingRadius() * viewport->radius(); if (x*x + y*y > radius * radius) { globeHidesPoint = true; return false; } globeHidesPoint = false; x += viewport->width() / 2; y = viewport->height() / 2 - y; // Skip placemarks that are outside the screen area - if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) { - return false; - } - - return true; + return !(x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height()); } bool GnomonicProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const { pointRepeatNum = 0; globeHidesPoint = false; bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint ); // Skip placemarks that are outside the screen area if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0 || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 ) { return false; } // This projection doesn't have any repetitions, // so the number of screen points referring to the geopoint is one. pointRepeatNum = 1; return visible; } bool GnomonicProjection::geoCoordinates( const int x, const int y, const ViewportParams *viewport, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { const qint64 radius = viewport->radius(); // Calculate how many degrees are being represented per pixel. const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); const qreal rx = ( - viewport->width() / 2 + x ); const qreal ry = ( viewport->height() / 2 - y ); const qreal p = qMax( qSqrt( rx*rx + ry*ry ), qreal(0.0001) ); // ensure we don't divide by zero const qreal c = qAtan(2 * p / radius); const qreal sinc = qSin(c); lon = centerLon + qAtan2( rx*sinc , ( p*qCos( centerLat )*qCos( c ) - ry*qSin( centerLat )*sinc ) ); while ( lon < -M_PI ) lon += 2 * M_PI; while ( lon > M_PI ) lon -= 2 * M_PI; lat = qAsin( qCos(c)*qSin(centerLat) + ry*sinc*qCos(centerLat)/p ); if ( unit == GeoDataCoordinates::Degree ) { lon *= RAD2DEG; lat *= RAD2DEG; } return true; } } diff --git a/src/lib/marble/projections/LambertAzimuthalProjection.cpp b/src/lib/marble/projections/LambertAzimuthalProjection.cpp index e785291b2..70b9c5718 100644 --- a/src/lib/marble/projections/LambertAzimuthalProjection.cpp +++ b/src/lib/marble/projections/LambertAzimuthalProjection.cpp @@ -1,186 +1,182 @@ // // 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 2014 Torsten Rahn // // Local #include "LambertAzimuthalProjection.h" #include "AbstractProjection_p.h" #include "MarbleDebug.h" // Marble #include "ViewportParams.h" #include "GeoDataPoint.h" #include "GeoDataLineString.h" #include "GeoDataCoordinates.h" #include "MarbleGlobal.h" #include "AzimuthalProjection_p.h" #include #include #define SAFE_DISTANCE namespace Marble { class LambertAzimuthalProjectionPrivate : public AzimuthalProjectionPrivate { public: explicit LambertAzimuthalProjectionPrivate( LambertAzimuthalProjection * parent ); Q_DECLARE_PUBLIC( LambertAzimuthalProjection ) }; LambertAzimuthalProjection::LambertAzimuthalProjection() : AzimuthalProjection( new LambertAzimuthalProjectionPrivate( this ) ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } LambertAzimuthalProjection::LambertAzimuthalProjection( LambertAzimuthalProjectionPrivate *dd ) : AzimuthalProjection( dd ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } LambertAzimuthalProjection::~LambertAzimuthalProjection() { } LambertAzimuthalProjectionPrivate::LambertAzimuthalProjectionPrivate( LambertAzimuthalProjection * parent ) : AzimuthalProjectionPrivate( parent ) { } QString LambertAzimuthalProjection::name() const { return QObject::tr( "Lambert Azimuthal Equal-Area" ); } QString LambertAzimuthalProjection::description() const { return QObject::tr( "

Lambert Azimuthal Equal-Area Projection

Applications: Used in structural geology to plot directional data.

" ); } QIcon LambertAzimuthalProjection::icon() const { return QIcon(QStringLiteral(":/icons/map-globe.png")); } qreal LambertAzimuthalProjection::clippingRadius() const { return 1; } bool LambertAzimuthalProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { const qreal lambda = coordinates.longitude(); const qreal phi = coordinates.latitude(); const qreal lambdaPrime = viewport->centerLongitude(); const qreal phi1 = viewport->centerLatitude(); qreal cosC = qSin( phi1 ) * qSin( phi ) + qCos( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ); // Prevent division by zero if (cosC <= 0) { globeHidesPoint = true; return false; } qreal k = qSqrt(2 / (1 + cosC)); // Let (x, y) be the position on the screen of the placemark.. x = ( qCos( phi ) * qSin( lambda - lambdaPrime ) ) * k; y = ( qCos( phi1 ) * qSin( phi ) - qSin( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ) ) * k; x *= viewport->radius() / qSqrt(2); y *= viewport->radius() / qSqrt(2); const qint64 radius = clippingRadius() * viewport->radius(); if (x*x + y*y > radius * radius) { globeHidesPoint = true; return false; } globeHidesPoint = false; x += viewport->width() / 2; y = viewport->height() / 2 - y; // Skip placemarks that are outside the screen area - if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) { - return false; - } - - return true; + return !(x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height()); } bool LambertAzimuthalProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const { pointRepeatNum = 0; globeHidesPoint = false; bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint ); // Skip placemarks that are outside the screen area if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0 || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 ) { return false; } // This projection doesn't have any repetitions, // so the number of screen points referring to the geopoint is one. pointRepeatNum = 1; return visible; } bool LambertAzimuthalProjection::geoCoordinates( const int x, const int y, const ViewportParams *viewport, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { const qint64 radius = viewport->radius(); // Calculate how many degrees are being represented per pixel. const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); const qreal rx = ( - viewport->width() / 2 + x ); const qreal ry = ( viewport->height() / 2 - y ); const qreal p = qMax( qSqrt( rx*rx + ry*ry ), qreal(0.0001) ); // ensure we don't divide by zero const qreal c = 2 * qAsin( p / (qSqrt(2) * radius) ); const qreal sinc = qSin(c); lon = centerLon + qAtan2( rx*sinc , ( p*qCos( centerLat )*qCos( c ) - ry*qSin( centerLat )*sinc ) ); while ( lon < -M_PI ) lon += 2 * M_PI; while ( lon > M_PI ) lon -= 2 * M_PI; lat = qAsin( qCos(c)*qSin(centerLat) + (ry*sinc*qCos(centerLat))/p ); if ( unit == GeoDataCoordinates::Degree ) { lon *= RAD2DEG; lat *= RAD2DEG; } return true; } } diff --git a/src/lib/marble/projections/MercatorProjection.cpp b/src/lib/marble/projections/MercatorProjection.cpp index 62dcef009..8975f8066 100644 --- a/src/lib/marble/projections/MercatorProjection.cpp +++ b/src/lib/marble/projections/MercatorProjection.cpp @@ -1,275 +1,272 @@ // // 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 2007-2008 Inge Wallin // Copyright 2007-2012 Torsten Rahn // // Local #include "MercatorProjection.h" #include "MarbleDebug.h" // Marble #include "ViewportParams.h" #include "GeoDataLatLonAltBox.h" #include "MathHelper.h" #include "GeoDataPoint.h" #include "MarbleMath.h" #include using namespace Marble; MercatorProjection::MercatorProjection() : CylindricalProjection(), m_lastCenterLat(200.0), m_lastCenterLatInv(0.0) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } MercatorProjection::~MercatorProjection() { } QString MercatorProjection::name() const { return QObject::tr( "Mercator" ); } QString MercatorProjection::description() const { return QObject::tr( "

Mercator Projection

Applications: popular standard map projection for navigation.

" ); } QIcon MercatorProjection::icon() const { return QIcon(QStringLiteral(":/icons/map-mercator.png")); } qreal MercatorProjection::maxValidLat() const { // This is the max value where gd( lat ) is defined. return +85.05113 * DEG2RAD; } qreal MercatorProjection::minValidLat() const { // This is the min value where gd( lat ) is defined. return -85.05113 * DEG2RAD; } bool MercatorProjection::screenCoordinates( const GeoDataCoordinates &geopoint, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { globeHidesPoint = false; qreal lon; qreal originalLat; geopoint.geoCoordinates( lon, originalLat ); qreal const lat = qBound(minLat(), originalLat, maxLat()); const bool isLatValid = lat == originalLat; // Convenience variables int radius = viewport->radius(); qreal width = (qreal)(viewport->width()); qreal height = (qreal)(viewport->height()); qreal rad2Pixel = 2 * radius / M_PI; const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); if (centerLat != m_lastCenterLat) { m_lastCenterLatInv = gdInv(centerLat); m_lastCenterLat = centerLat; } // Let (x, y) be the position on the screen of the placemark.. x = ( width / 2 + rad2Pixel * ( lon - centerLon ) ); y = ( height / 2 - rad2Pixel * ( gdInv( lat ) - m_lastCenterLatInv ) ); // Return true if the calculated point is inside the screen area, // otherwise return false. return isLatValid && ( ( 0 <= y && y < height ) && ( ( 0 <= x && x < width ) || ( 0 <= x - 4 * radius && x - 4 * radius < width ) || ( 0 <= x + 4 * radius && x + 4 * radius < width ) ) ); } bool MercatorProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const { pointRepeatNum = 0; // On flat projections the observer's view onto the point won't be // obscured by the target planet itself. globeHidesPoint = false; // Convenience variables int radius = viewport->radius(); qreal width = (qreal)(viewport->width()); qreal height = (qreal)(viewport->height()); // Let (itX, y) be the first guess for one possible position on screen.. qreal itX; bool visible = screenCoordinates( coordinates, viewport, itX, y); // Make sure that the requested point is within the visible y range: if ( 0 <= y + size.height() / 2.0 && y < height + size.height() / 2.0 ) { // For the repetition case the same geopoint gets displayed on // the map many times.across the longitude. int xRepeatDistance = 4 * radius; // Finding the leftmost positive x value if ( itX + size.width() / 2.0 >= xRepeatDistance ) { const int repeatNum = (int)( ( itX + size.width() / 2.0 ) / xRepeatDistance ); itX = itX - repeatNum * xRepeatDistance; } if ( itX + size.width() / 2.0 < 0 ) { itX += xRepeatDistance; } // the requested point is out of the visible x range: if ( itX > width + size.width() / 2.0 ) { return false; } // Now iterate through all visible x screen coordinates for the point // from left to right. int itNum = 0; while ( itX - size.width() / 2.0 < width ) { *x = itX; ++x; ++itNum; itX += xRepeatDistance; } pointRepeatNum = itNum; - return visible && true; + return visible; } // the requested point is out of the visible y range: return false; } bool MercatorProjection::geoCoordinates( const int x, const int y, const ViewportParams *viewport, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { const int radius = viewport->radius(); Q_ASSERT( radius > 0 ); // Calculate translation of center point const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); // Calculate how many pixel are being represented per radians. const float rad2Pixel = (qreal)( 2 * radius )/M_PI; const qreal pixel2Rad = M_PI / (2 * radius); { const int halfImageWidth = viewport->width() / 2; const int xPixels = x - halfImageWidth; lon = xPixels * pixel2Rad + centerLon; while ( lon > M_PI ) lon -= 2*M_PI; while ( lon < -M_PI ) lon += 2*M_PI; if ( unit == GeoDataCoordinates::Degree ) { lon *= RAD2DEG; } } { const int halfImageHeight = viewport->height() / 2; const int yCenterOffset = (int)( asinh( tan( centerLat ) ) * rad2Pixel ); const int yTop = halfImageHeight - 2 * radius + yCenterOffset; const int yBottom = yTop + 4 * radius; if ( y >= yTop && y < yBottom ) { lat = gd( ( ( halfImageHeight + yCenterOffset ) - y) * pixel2Rad ); if ( unit == GeoDataCoordinates::Degree ) { lat *= RAD2DEG; } return true; // lat successfully calculated } } return false; // lat unchanged } GeoDataLatLonAltBox MercatorProjection::latLonAltBox( const QRect& screenRect, const ViewportParams *viewport ) const { qreal west; qreal north = 85*DEG2RAD; geoCoordinates( screenRect.left(), screenRect.top(), viewport, west, north, GeoDataCoordinates::Radian ); qreal east; qreal south = -85*DEG2RAD; geoCoordinates( screenRect.right(), screenRect.bottom(), viewport, east, south, GeoDataCoordinates::Radian ); // For the case where the whole viewport gets covered there is a // pretty dirty and generic detection algorithm: GeoDataLatLonAltBox latLonAltBox; latLonAltBox.setNorth( north, GeoDataCoordinates::Radian ); latLonAltBox.setSouth( south, GeoDataCoordinates::Radian ); latLonAltBox.setWest( west, GeoDataCoordinates::Radian ); latLonAltBox.setEast( east, GeoDataCoordinates::Radian ); latLonAltBox.setMinAltitude( -100000000.0 ); latLonAltBox.setMaxAltitude( 100000000000000.0 ); // The remaining algorithm should be pretty generic for all kinds of // flat projections: // If the whole globe is visible we can easily calculate // analytically the lon-/lat- range. // qreal pitch = GeoDataPoint::normalizeLat( viewport->planetAxis().pitch() ); int xRepeatDistance = 4 * viewport->radius(); if ( viewport->width() >= xRepeatDistance ) { latLonAltBox.setWest( -M_PI ); latLonAltBox.setEast( +M_PI ); } return latLonAltBox; } bool MercatorProjection::mapCoversViewport( const ViewportParams *viewport ) const { int radius = viewport->radius(); int height = viewport->height(); // Calculate translation of center point const qreal centerLat = viewport->centerLatitude(); // Calculate how many pixel are being represented per radians. const float rad2Pixel = (float)( 2 * radius )/M_PI; int yCenterOffset = (int)( asinh( tan( centerLat ) ) * rad2Pixel ); int yTop = height / 2 - 2 * radius + yCenterOffset; int yBottom = yTop + 4 * radius; - if ( yTop >= 0 || yBottom < height ) - return false; - - return true; + return !(yTop >= 0 || yBottom < height); } diff --git a/src/lib/marble/projections/StereographicProjection.cpp b/src/lib/marble/projections/StereographicProjection.cpp index fb073db62..dcfb02e24 100644 --- a/src/lib/marble/projections/StereographicProjection.cpp +++ b/src/lib/marble/projections/StereographicProjection.cpp @@ -1,186 +1,182 @@ // // 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 2014 Torsten Rahn // // Local #include "StereographicProjection.h" #include "AbstractProjection_p.h" #include "MarbleDebug.h" // Marble #include "ViewportParams.h" #include "GeoDataPoint.h" #include "GeoDataLineString.h" #include "GeoDataCoordinates.h" #include "MarbleGlobal.h" #include "AzimuthalProjection_p.h" #include #include #define SAFE_DISTANCE namespace Marble { class StereographicProjectionPrivate : public AzimuthalProjectionPrivate { public: explicit StereographicProjectionPrivate( StereographicProjection * parent ); Q_DECLARE_PUBLIC( StereographicProjection ) }; StereographicProjection::StereographicProjection() : AzimuthalProjection( new StereographicProjectionPrivate( this ) ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } StereographicProjection::StereographicProjection( StereographicProjectionPrivate *dd ) : AzimuthalProjection( dd ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } StereographicProjection::~StereographicProjection() { } StereographicProjectionPrivate::StereographicProjectionPrivate( StereographicProjection * parent ) : AzimuthalProjectionPrivate( parent ) { } QString StereographicProjection::name() const { return QObject::tr( "Stereographic" ); } QString StereographicProjection::description() const { return QObject::tr( "

Stereographic Projection (\"orthogonal\")

Applications: Used for planetary cartography, geology and panorama photography.

" ); } QIcon StereographicProjection::icon() const { return QIcon(QStringLiteral(":/icons/map-globe.png")); } qreal StereographicProjection::clippingRadius() const { return 1; } bool StereographicProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { const qreal lambda = coordinates.longitude(); const qreal phi = coordinates.latitude(); const qreal lambdaPrime = viewport->centerLongitude(); const qreal phi1 = viewport->centerLatitude(); qreal cosC = qSin( phi1 ) * qSin( phi ) + qCos( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ); // Prevent division by zero if (cosC <= 0) { globeHidesPoint = true; return false; } qreal k = 1 / (1 + cosC); // Let (x, y) be the position on the screen of the placemark.. x = ( qCos( phi ) * qSin( lambda - lambdaPrime ) ) * k; y = ( qCos( phi1 ) * qSin( phi ) - qSin( phi1 ) * qCos( phi ) * qCos( lambda - lambdaPrime ) ) * k; x *= viewport->radius(); y *= viewport->radius(); const qint64 radius = clippingRadius() * viewport->radius(); if (x*x + y*y > radius * radius) { globeHidesPoint = true; return false; } globeHidesPoint = false; x += viewport->width() / 2; y = viewport->height() / 2 - y; // Skip placemarks that are outside the screen area - if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) { - return false; - } - - return true; + return !(x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height()); } bool StereographicProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const { pointRepeatNum = 0; globeHidesPoint = false; bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint ); // Skip placemarks that are outside the screen area if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0 || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 ) { return false; } // This projection doesn't have any repetitions, // so the number of screen points referring to the geopoint is one. pointRepeatNum = 1; return visible; } bool StereographicProjection::geoCoordinates( const int x, const int y, const ViewportParams *viewport, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { const qint64 radius = viewport->radius(); // Calculate how many degrees are being represented per pixel. const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); const qreal rx = ( - viewport->width() / 2 + x ); const qreal ry = ( viewport->height() / 2 - y ); const qreal p = qMax( qSqrt( rx*rx + ry*ry ), qreal(0.0001) ); // ensure we don't divide by zero const qreal c = 2 * qAtan2( p , radius ); const qreal sinc = qSin(c); lon = centerLon + qAtan2( rx*sinc , ( p*qCos( centerLat )*qCos( c ) - ry*qSin( centerLat )*sinc ) ); while ( lon < -M_PI ) lon += 2 * M_PI; while ( lon > M_PI ) lon -= 2 * M_PI; lat = qAsin( qCos(c)*qSin(centerLat) + (ry*sinc*qCos(centerLat))/p ); if ( unit == GeoDataCoordinates::Degree ) { lon *= RAD2DEG; lat *= RAD2DEG; } return true; } } diff --git a/src/lib/marble/projections/VerticalPerspectiveProjection.cpp b/src/lib/marble/projections/VerticalPerspectiveProjection.cpp index 60027106c..77429b0ed 100644 --- a/src/lib/marble/projections/VerticalPerspectiveProjection.cpp +++ b/src/lib/marble/projections/VerticalPerspectiveProjection.cpp @@ -1,230 +1,226 @@ // // 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 2014 Torsten Rahn // // Local #include "VerticalPerspectiveProjection.h" #include "AbstractProjection_p.h" #include "MarbleDebug.h" // Marble #include "ViewportParams.h" #include "GeoDataPoint.h" #include "GeoDataLineString.h" #include "GeoDataCoordinates.h" #include "MarbleGlobal.h" #include "AzimuthalProjection_p.h" #include #include #define SAFE_DISTANCE namespace Marble { class VerticalPerspectiveProjectionPrivate : public AzimuthalProjectionPrivate { public: explicit VerticalPerspectiveProjectionPrivate( VerticalPerspectiveProjection * parent ); void calculateConstants(qreal radius) const; mutable qreal m_P; ///< Distance of the point of perspective in earth diameters mutable qreal m_previousRadius; mutable qreal m_altitudeToPixel; mutable qreal m_perspectiveRadius; mutable qreal m_pPfactor; Q_DECLARE_PUBLIC( VerticalPerspectiveProjection ) }; VerticalPerspectiveProjection::VerticalPerspectiveProjection() : AzimuthalProjection( new VerticalPerspectiveProjectionPrivate( this ) ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } VerticalPerspectiveProjection::VerticalPerspectiveProjection( VerticalPerspectiveProjectionPrivate *dd ) : AzimuthalProjection( dd ) { setMinLat( minValidLat() ); setMaxLat( maxValidLat() ); } VerticalPerspectiveProjection::~VerticalPerspectiveProjection() { } VerticalPerspectiveProjectionPrivate::VerticalPerspectiveProjectionPrivate( VerticalPerspectiveProjection * parent ) : AzimuthalProjectionPrivate( parent ), m_P(1), m_previousRadius(1), m_altitudeToPixel(1), m_perspectiveRadius(1), m_pPfactor(1) { } QString VerticalPerspectiveProjection::name() const { return QObject::tr( "Vertical Perspective Projection" ); } QString VerticalPerspectiveProjection::description() const { return QObject::tr( "

Vertical Perspective Projection (\"orthogonal\")

Shows the earth as it appears from a relatively short distance above the surface. Applications: Used for Virtual Globes.

" ); } QIcon VerticalPerspectiveProjection::icon() const { return QIcon(QStringLiteral(":/icons/map-globe.png")); } void VerticalPerspectiveProjectionPrivate::calculateConstants(qreal radius) const { if (radius == m_previousRadius) return; m_previousRadius = radius; m_P = 1.5 + 3 * 1000 * 0.4 / radius / qTan(0.5 * 110 * DEG2RAD); m_altitudeToPixel = radius / (EARTH_RADIUS * qSqrt((m_P-1)/(m_P+1))); m_perspectiveRadius = radius / qSqrt((m_P-1)/(m_P+1)); m_pPfactor = (m_P+1)/(m_perspectiveRadius*m_perspectiveRadius*(m_P-1)); } qreal VerticalPerspectiveProjection::clippingRadius() const { return 1; } bool VerticalPerspectiveProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { Q_D(const VerticalPerspectiveProjection); d->calculateConstants(viewport->radius()); const qreal P = d->m_P; const qreal deltaLambda = coordinates.longitude() - viewport->centerLongitude(); const qreal phi = coordinates.latitude(); const qreal phi1 = viewport->centerLatitude(); qreal cosC = qSin( phi1 ) * qSin( phi ) + qCos( phi1 ) * qCos( phi ) * qCos( deltaLambda ); // Don't display placemarks that are below 10km altitude and // are on the Earth's backside (where cosC < 1/P) if (cosC < 1/P && coordinates.altitude() < 10000) { globeHidesPoint = true; return false; } // Let (x, y) be the position on the screen of the placemark .. // First determine the position in unit coordinates: qreal k = (P - 1) / (P - cosC); // scale factor x = ( qCos( phi ) * qSin( deltaLambda ) ) * k; y = ( qCos( phi1 ) * qSin( phi ) - qSin( phi1 ) * qCos( phi ) * qCos( deltaLambda ) ) * k; // Transform to screen coordinates qreal pixelAltitude = (coordinates.altitude() + EARTH_RADIUS) * d->m_altitudeToPixel; x *= pixelAltitude; y *= pixelAltitude; // Don't display satellites that are on the Earth's backside: if (cosC < 1/P && x*x+y*y < viewport->radius() * viewport->radius()) { globeHidesPoint = true; return false; } // The remaining placemarks are definetely not on the Earth's backside globeHidesPoint = false; x += viewport->width() / 2; y = viewport->height() / 2 - y; // Skip placemarks that are outside the screen area - if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) { - return false; - } - - return true; + return !(x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height()); } bool VerticalPerspectiveProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal *x, qreal &y, int &pointRepeatNum, const QSizeF& size, bool &globeHidesPoint ) const { pointRepeatNum = 0; globeHidesPoint = false; bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint ); // Skip placemarks that are outside the screen area if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0 || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 ) { return false; } // This projection doesn't have any repetitions, // so the number of screen points referring to the geopoint is one. pointRepeatNum = 1; return visible; } bool VerticalPerspectiveProjection::geoCoordinates( const int x, const int y, const ViewportParams *viewport, qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { Q_D(const VerticalPerspectiveProjection); d->calculateConstants(viewport->radius()); const qreal P = d->m_P; const qreal rx = ( - viewport->width() / 2 + x ); const qreal ry = ( viewport->height() / 2 - y ); const qreal p2 = rx*rx + ry*ry; if (p2 == 0) { lon = viewport->centerLongitude(); lat = viewport->centerLatitude(); return true; } const qreal pP = p2*d->m_pPfactor; if ( pP > 1) return false; const qreal p = qSqrt(p2); const qreal fract = d->m_perspectiveRadius*(P-1)/p; const qreal c = qAsin((P-qSqrt(1-pP))/(fract+1/fract)); const qreal sinc = qSin(c); const qreal centerLon = viewport->centerLongitude(); const qreal centerLat = viewport->centerLatitude(); lon = centerLon + qAtan2(rx*sinc, (p*qCos(centerLat)*qCos(c) - ry*qSin(centerLat)*sinc)); while ( lon < -M_PI ) lon += 2 * M_PI; while ( lon > M_PI ) lon -= 2 * M_PI; lat = qAsin(qCos(c)*qSin(centerLat) + (ry*sinc*qCos(centerLat))/p); if ( unit == GeoDataCoordinates::Degree ) { lon *= RAD2DEG; lat *= RAD2DEG; } return true; } } diff --git a/src/plugins/render/annotate/GroundOverlayFrame.cpp b/src/plugins/render/annotate/GroundOverlayFrame.cpp index 3cd29d748..67f76f02d 100644 --- a/src/plugins/render/annotate/GroundOverlayFrame.cpp +++ b/src/plugins/render/annotate/GroundOverlayFrame.cpp @@ -1,402 +1,398 @@ // // 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 2013 Adrian Draghici // // Self #include "GroundOverlayFrame.h" // Marble #include "GeoDataPlacemark.h" #include "GeoDataGroundOverlay.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" #include "GeoPainter.h" #include "ViewportParams.h" #include "SceneGraphicsTypes.h" #include "TextureLayer.h" #include "MarbleDirs.h" // Qt #include namespace Marble { GroundOverlayFrame::GroundOverlayFrame( GeoDataPlacemark *placemark, GeoDataGroundOverlay *overlay, TextureLayer *textureLayer ) : SceneGraphicsItem( placemark ), m_overlay( overlay ), m_textureLayer( textureLayer ), m_movedHandle( NoRegion ), m_hoveredHandle( NoRegion ), m_editStatus( Resize ), m_editStatusChangeNeeded( false ), m_previousRotation( 0.0 ), m_viewport( 0 ) { m_resizeIcons.reserve(16); // NorthWest m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-diagonal-topleft.png"))); m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-diagonal-topleft-active.png"))); // SouthWest m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-diagonal-topright.png"))); m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-diagonal-topright-active.png"))); // SouthEast m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-diagonal-topleft.png"))); m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-diagonal-topleft-active.png"))); // NorthEast m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-diagonal-topright.png"))); m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-diagonal-topright-active.png"))); // North m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-vertical.png"))); m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-vertical-active.png"))); // South m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-vertical.png"))); m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-vertical-active.png"))); // East m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-horizontal.png"))); m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-horizontal-active.png"))); // West m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-horizontal.png"))); m_resizeIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-horizontal-active.png"))); m_rotateIcons.reserve(16); // NorthWest m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-rotation-topleft.png"))); m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-rotation-topleft-active.png"))); // SouthWest m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-rotation-bottomleft.png"))); m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-rotation-bottomleft-active.png"))); // SouthEast m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-rotation-bottomright.png"))); m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-rotation-bottomright-active.png"))); // NorthEast m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-rotation-topright.png"))); m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-rotation-topright-active.png"))); // North m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-horizontal.png"))); m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-horizontal-active.png"))); // South m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-horizontal.png"))); m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-horizontal-active.png"))); // East m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-vertical.png"))); m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-vertical-active.png"))); // West m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-vertical.png"))); m_rotateIcons.append(QImage(MarbleDirs::systemPath() + QLatin1String("/bitmaps/editarrows/arrow-vertical-active.png"))); update(); setPaintLayers(QStringList() << "GroundOverlayFrame"); } void GroundOverlayFrame::paint(GeoPainter *painter, const ViewportParams *viewport , const QString &layer, int tileZoomLevel) { Q_UNUSED(layer); Q_UNUSED(tileZoomLevel); m_viewport = viewport; m_regionList.clear(); painter->save(); if (const auto polygon = geodata_cast(placemark()->geometry())) { const GeoDataLinearRing &ring = polygon->outerBoundary(); QVector coordinateList; coordinateList.reserve(8); coordinateList.append( ring.at( NorthWest ) ); coordinateList.append( ring.at( SouthWest ) ); coordinateList.append( ring.at( SouthEast ) ); coordinateList.append( ring.at( NorthEast ) ); GeoDataCoordinates northernHandle = ring.at( NorthEast ).interpolate( ring.at( NorthWest ), 0.5 ); GeoDataCoordinates southernHandle = ring.at( SouthEast ).interpolate( ring.at( SouthWest ), 0.5 ); // Special case handle position to take tessellation // along latitude circles into account if (m_overlay->latLonBox().rotation() == 0) { northernHandle.setLatitude(ring.at( NorthEast ).latitude()); southernHandle.setLatitude(ring.at( SouthEast ).latitude()); } coordinateList.append( northernHandle ); coordinateList.append( southernHandle ); coordinateList.append( ring.at( NorthEast ).interpolate( ring.at( SouthEast ), 0.5 ) ); coordinateList.append( ring.at( NorthWest ).interpolate( ring.at( SouthWest ), 0.5 ) ); m_regionList.reserve(9); m_regionList.append( painter->regionFromEllipse( coordinateList.at( NorthWest ), 16, 16 ) ); m_regionList.append( painter->regionFromEllipse( coordinateList.at( SouthWest ), 16, 16 ) ); m_regionList.append( painter->regionFromEllipse( coordinateList.at( SouthEast ), 16, 16 ) ); m_regionList.append( painter->regionFromEllipse( coordinateList.at( NorthEast ), 16, 16 ) ); m_regionList.append( painter->regionFromEllipse( coordinateList.at( North ), 16, 16 ) ); m_regionList.append( painter->regionFromEllipse( coordinateList.at( South ), 16, 16 ) ); m_regionList.append( painter->regionFromEllipse( coordinateList.at( East ), 16, 16 ) ); m_regionList.append( painter->regionFromEllipse( coordinateList.at( West ), 16, 16 ) ); m_regionList.append( painter->regionFromPolygon( ring, Qt::OddEvenFill ) ); // Calculate handle icon orientation due to the projection qreal xNW, yNW, xSW, ySW; viewport->screenCoordinates(ring.at( NorthWest ), xNW, yNW); viewport->screenCoordinates(ring.at( SouthWest ), xSW, ySW); qreal westernAngle = qAtan2(ySW - yNW, xSW - xNW) - M_PI/2; qreal xNE, yNE, xSE, ySE; viewport->screenCoordinates(ring.at( NorthEast ), xNE, yNE); viewport->screenCoordinates(ring.at( SouthEast ), xSE, ySE); qreal easternAngle = qAtan2(ySE - yNE, xSE - xNE) - M_PI/2; painter->setPen( Qt::DashLine ); painter->setBrush( Qt::NoBrush ); painter->drawPolygon( ring ); qreal projectedAngle = 0; for( int i = NorthWest; i != Polygon; ++i ) { // Assign handle icon orientation due to the projection if (i == NorthWest || i == West || i == SouthWest) { projectedAngle = westernAngle; } else if (i == NorthEast || i == East || i == SouthEast) { projectedAngle = easternAngle; } else if (i == North || i == South) { projectedAngle = (westernAngle + easternAngle) / 2; } QTransform trans; trans.rotateRadians( projectedAngle ); if ( m_editStatus == Resize ){ if( m_hoveredHandle != i ) { painter->drawImage( coordinateList.at( i ), m_resizeIcons.at( 2*i ).transformed( trans, Qt::SmoothTransformation ) ); } else { painter->drawImage( coordinateList.at( i ), m_resizeIcons.at( 2*i + 1 ).transformed( trans, Qt::SmoothTransformation ) ); } } else if ( m_editStatus == Rotate ) { if( m_hoveredHandle != i ) { painter->drawImage( coordinateList.at( i ), m_rotateIcons.at( 2*i ).transformed( trans, Qt::SmoothTransformation ) ); } else { painter->drawImage( coordinateList.at( i ), m_rotateIcons.at( 2*i + 1 ).transformed( trans, Qt::SmoothTransformation ) ); } } } } painter->restore(); } bool GroundOverlayFrame::containsPoint( const QPoint &eventPos ) const { for ( const QRegion ®ion: m_regionList ) { if ( region.contains( eventPos ) ) { return true; } } // This is a bugfix to handle the events even if they occur outside of this object, // so when rotating or resizing the mouseReleaseEvent is handled successfully // TODO: maybe find a better way? - if( m_movedHandle != NoRegion || - m_hoveredHandle != NoRegion ) { - return true; - } - - return false; + return m_movedHandle != NoRegion || + m_hoveredHandle != NoRegion; } void GroundOverlayFrame::dealWithItemChange( const SceneGraphicsItem *other ) { Q_UNUSED( other ); } void GroundOverlayFrame::move( const GeoDataCoordinates &source, const GeoDataCoordinates &destination ) { // not implemented yet Q_UNUSED( source ); Q_UNUSED( destination ); } bool GroundOverlayFrame::mousePressEvent( QMouseEvent *event ) { // React to all ellipse as well as to the polygon. for ( int i = 0; i < m_regionList.size(); ++i ) { if ( m_regionList.at(i).contains( event->pos() ) ) { m_movedHandle = i; qreal lon, lat; m_viewport->geoCoordinates( event->pos().x(), event->pos().y(), lon, lat, GeoDataCoordinates::Radian ); m_movedHandleGeoCoordinates.set( lon, lat ); m_movedHandleScreenCoordinates = event->pos(); m_previousRotation = m_overlay->latLonBox().rotation(); if ( m_movedHandle == Polygon ) { m_editStatusChangeNeeded = true; } return true; } } return false; } bool GroundOverlayFrame::mouseMoveEvent( QMouseEvent *event ) { if ( !m_viewport ) { return false; } // Catch hover events. if ( m_movedHandle == NoRegion ) { for ( int i = 0; i < m_regionList.size(); ++i ) { if ( m_regionList.at(i).contains( event->pos() ) ) { if ( i == Polygon ) { setRequest( ChangeCursorOverlayBodyHover ); } else { setRequest( ChangeCursorOverlayRotateHover ); } m_hoveredHandle = i; return true; } } m_hoveredHandle = NoRegion; return true; } else { m_editStatusChangeNeeded = false; } if (geodata_cast(placemark()->geometry())) { qreal lon, lat; m_viewport->geoCoordinates( event->pos().x(), event->pos().y(), lon, lat, GeoDataCoordinates::Radian ); if ( m_editStatus == Resize ) { GeoDataCoordinates coord(lon, lat); GeoDataCoordinates rotatedCoord(coord); if (m_overlay->latLonBox().rotation() != 0) { rotatedCoord = coord.rotateAround(m_overlay->latLonBox().center(), -m_overlay->latLonBox().rotation()); } if ( m_movedHandle == NorthWest ) { m_overlay->latLonBox().setNorth( rotatedCoord.latitude() ); m_overlay->latLonBox().setWest( rotatedCoord.longitude() ); } else if ( m_movedHandle == SouthWest ) { m_overlay->latLonBox().setSouth( rotatedCoord.latitude() ); m_overlay->latLonBox().setWest( rotatedCoord.longitude() ); } else if ( m_movedHandle == SouthEast ) { m_overlay->latLonBox().setSouth( rotatedCoord.latitude() ); m_overlay->latLonBox().setEast( rotatedCoord.longitude() ); } else if ( m_movedHandle == NorthEast ) { m_overlay->latLonBox().setNorth( rotatedCoord.latitude() ); m_overlay->latLonBox().setEast( rotatedCoord.longitude() ); } else if ( m_movedHandle == North ) { m_overlay->latLonBox().setNorth( rotatedCoord.latitude() ); } else if ( m_movedHandle == South ) { m_overlay->latLonBox().setSouth( rotatedCoord.latitude() ); } else if ( m_movedHandle == East ) { m_overlay->latLonBox().setEast( rotatedCoord.longitude() ); } else if ( m_movedHandle == West ) { m_overlay->latLonBox().setWest( rotatedCoord.longitude() ); } } else if ( m_editStatus == Rotate ) { if ( m_movedHandle != Polygon ) { QPoint center = m_regionList.at( Polygon ).boundingRect().center(); qreal angle1 = qAtan2( event->pos().y() - center.y(), event->pos().x() - center.x() ); qreal angle2 = qAtan2( m_movedHandleScreenCoordinates.y() - center.y(), m_movedHandleScreenCoordinates.x() - center.x() ); m_overlay->latLonBox().setRotation( angle2 - angle1 + m_previousRotation ); } } if ( m_movedHandle == Polygon ) { const qreal centerLonDiff = lon - m_movedHandleGeoCoordinates.longitude(); const qreal centerLatDiff = lat - m_movedHandleGeoCoordinates.latitude(); m_overlay->latLonBox().setBoundaries( m_overlay->latLonBox().north() + centerLatDiff, m_overlay->latLonBox().south() + centerLatDiff, m_overlay->latLonBox().east() + centerLonDiff, m_overlay->latLonBox().west() + centerLonDiff ); m_movedHandleGeoCoordinates.set( lon, lat ); } update(); return true; } return false; } bool GroundOverlayFrame::mouseReleaseEvent( QMouseEvent *event ) { Q_UNUSED( event ); m_movedHandle = NoRegion; m_textureLayer->reset(); if( m_editStatusChangeNeeded ) { if( m_editStatus == Resize ) { m_editStatus = Rotate; } else { m_editStatus = Resize; } } return true; } void GroundOverlayFrame::update() { GeoDataLatLonBox overlayLatLonBox = m_overlay->latLonBox(); GeoDataPolygon *poly = dynamic_cast( placemark()->geometry() ); poly->outerBoundary().clear(); GeoDataCoordinates rotatedCoord; GeoDataCoordinates northWest(overlayLatLonBox.west(), overlayLatLonBox.north()); rotatedCoord = northWest.rotateAround(overlayLatLonBox.center(), overlayLatLonBox.rotation()); poly->outerBoundary().append( rotatedCoord ); GeoDataCoordinates southWest(overlayLatLonBox.west(), overlayLatLonBox.south()); rotatedCoord = southWest.rotateAround(overlayLatLonBox.center(), overlayLatLonBox.rotation()); poly->outerBoundary().append( rotatedCoord ); GeoDataCoordinates southEast(overlayLatLonBox.east(), overlayLatLonBox.south()); rotatedCoord = southEast.rotateAround(overlayLatLonBox.center(), overlayLatLonBox.rotation()); poly->outerBoundary().append( rotatedCoord ); GeoDataCoordinates northEast(overlayLatLonBox.east(), overlayLatLonBox.north()); rotatedCoord = northEast.rotateAround(overlayLatLonBox.center(), overlayLatLonBox.rotation()); poly->outerBoundary().append( rotatedCoord ); } void GroundOverlayFrame::dealWithStateChange( SceneGraphicsItem::ActionState previousState ) { Q_UNUSED( previousState ); } const char *GroundOverlayFrame::graphicType() const { return SceneGraphicsTypes::SceneGraphicGroundOverlay; } } diff --git a/src/plugins/render/weather/WeatherData.cpp b/src/plugins/render/weather/WeatherData.cpp index 8c5a14c63..355e4e42b 100644 --- a/src/plugins/render/weather/WeatherData.cpp +++ b/src/plugins/render/weather/WeatherData.cpp @@ -1,886 +1,883 @@ // // 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 2009 Bastian Holst // // Self #include "WeatherData.h" // Marble #include "MarbleGlobal.h" #include "MarbleDirs.h" #include "MarbleDebug.h" // Qt #include #include #include #include #include #include #include namespace Marble { // Factors // m/s to knots const qreal MPS2KN = 1.9437; const qreal KN2MPS = 1 / MPS2KN; // m/s to kilometer per hour const qreal MPS2KPH = 3.6; const qreal KPH2MPS = 1 / MPS2KPH; // m/s to miles per hour const qreal MPS2MPH = MPS2KPH * KM2MI; const qreal MPH2MPS = 1 / MPS2MPH; // HectoPascal to KiloPascal const qreal HPA2KPA = 10; const qreal KPA2HPA = 1/HPA2KPA; // Bar to HectoPascal const qreal BAR2HPA = 1000; const qreal HPA2BAR = 1/BAR2HPA; // mmHg to HectoPascal const qreal HG2HPA = 1.33; const qreal HPA2HG = 1/HG2HPA; // inchHg to HectoPascal const qreal IHG2HPA = HG2HPA * 25.4; const qreal HPA2IHG = HPA2HG / 25.4; // Summands // Kelvin to degree Celsius const qreal KEL2CEL = -273.15; const qreal CEL2KEL = -KEL2CEL; class WeatherDataPrivate { Q_DECLARE_TR_FUNCTIONS ( WeatherDataPrivate ) public: WeatherDataPrivate() : m_pubTime(), m_dataDate(), m_condition( WeatherData::ConditionNotAvailable ), m_windDirection( WeatherData::DirectionNotAvailable ), m_windSpeed( -1.0 ), m_temperature( -1.0 ), m_maxTemperature( -1.0 ), m_minTemperature( -1.0 ), m_visibility( WeatherData::VisibilityNotAvailable ), m_pressure( -1.0 ), m_pressureDevelopment( WeatherData::PressureDevelopmentNotAvailable ), m_humidity( -1.0 ), ref( 1 ) { initializeIcons(); } WeatherDataPrivate( const WeatherDataPrivate &other ) : m_pubTime( other.m_pubTime ), m_dataDate( other.m_dataDate ), m_condition( other.m_condition ), m_windDirection( other.m_windDirection ), m_windSpeed( other.m_windSpeed ), m_temperature( other.m_temperature ), m_maxTemperature( other.m_maxTemperature ), m_minTemperature( other.m_minTemperature ), m_visibility( other.m_visibility ), m_pressure( other.m_pressure ), m_pressureDevelopment( other.m_pressureDevelopment ), m_humidity( other.m_humidity ), ref( other.ref ) { initializeIcons(); } static void initializeIcons() { if( s_iconPath.size() == 0 ) { // Clouds s_iconPath.insert( WeatherData::ConditionNotAvailable, MarbleDirs::path(QStringLiteral("weather/weather-none-available.png"))); s_iconPath.insert( WeatherData::ClearDay, MarbleDirs::path(QStringLiteral("weather/weather-clear.png"))); s_iconPath.insert( WeatherData::ClearNight, MarbleDirs::path(QStringLiteral("weather/weather-clear-night.png"))); s_iconPath.insert( WeatherData::FewCloudsDay, MarbleDirs::path(QStringLiteral("weather/weather-few-clouds.png"))); s_iconPath.insert( WeatherData::FewCloudsNight, MarbleDirs::path(QStringLiteral("weather/weather-few-clouds-night.png"))); s_iconPath.insert( WeatherData::PartlyCloudyDay, MarbleDirs::path(QStringLiteral("weather/weather-clouds.png"))); s_iconPath.insert( WeatherData::PartlyCloudyNight, MarbleDirs::path(QStringLiteral("weather/weather-clouds-night.png"))); s_iconPath.insert( WeatherData::Overcast, MarbleDirs::path(QStringLiteral("weather/weather-many-clouds.png"))); // Rain s_iconPath.insert( WeatherData::LightShowersDay, MarbleDirs::path(QStringLiteral("weather/weather-showers-scattered-day.png"))); s_iconPath.insert( WeatherData::LightShowersNight, MarbleDirs::path(QStringLiteral("weather/weather-showers-scattered-night.png"))); s_iconPath.insert( WeatherData::ShowersDay, MarbleDirs::path(QStringLiteral("weather/weather-showers-day.png"))); s_iconPath.insert( WeatherData::ShowersNight, MarbleDirs::path(QStringLiteral("weather/weather-showers-night.png"))); s_iconPath.insert( WeatherData::LightRain, MarbleDirs::path(QStringLiteral("weather/weather-showers-scattered.png"))); s_iconPath.insert( WeatherData::Rain, MarbleDirs::path(QStringLiteral("weather/weather-showers.png"))); // Special s_iconPath.insert( WeatherData::ChanceThunderstormDay, MarbleDirs::path(QStringLiteral("weather/weather-storm-day.png"))); s_iconPath.insert( WeatherData::ChanceThunderstormNight, MarbleDirs::path(QStringLiteral("weather/weather-storm-night.png"))); s_iconPath.insert( WeatherData::Thunderstorm, MarbleDirs::path(QStringLiteral("weather/weather-storm.png"))); s_iconPath.insert( WeatherData::Hail, MarbleDirs::path(QStringLiteral("weather/weather-hail.png"))); s_iconPath.insert( WeatherData::ChanceSnowDay, MarbleDirs::path(QStringLiteral("weather/weather-snow-scattered-day.png"))); s_iconPath.insert( WeatherData::ChanceSnowNight, MarbleDirs::path(QStringLiteral("weather/weather-snow-scattered-night.png"))); s_iconPath.insert( WeatherData::LightSnow, MarbleDirs::path(QStringLiteral("weather/weather-snow-scattered.png"))); s_iconPath.insert( WeatherData::Snow, MarbleDirs::path(QStringLiteral("weather/weather-snow.png"))); s_iconPath.insert( WeatherData::RainSnow, MarbleDirs::path(QStringLiteral("weather/weather-snow-rain.png"))); s_iconPath.insert( WeatherData::Mist, MarbleDirs::path(QStringLiteral("weather/weather-mist.png"))); s_iconPath.insert( WeatherData::SandStorm, MarbleDirs::path(QStringLiteral("weather/weather-none-available.png"))); } } static qreal fromKelvin( qreal temp, WeatherData::TemperatureUnit format ) { if( WeatherData::Kelvin == format ) { return temp; } else if ( WeatherData::Celsius == format ) { return temp + KEL2CEL; } else if ( WeatherData::Fahrenheit == format ) { return ( temp * 1.8 ) - 459.67; } else { mDebug() << "Wrong temperature format"; return 0; } } static qreal toKelvin( qreal temp, WeatherData::TemperatureUnit format ) { if( WeatherData::Kelvin == format ) { return temp; } else if ( WeatherData::Celsius == format ) { return temp + CEL2KEL; } else if ( WeatherData::Fahrenheit == format ) { return ( temp + 459.67 ) / 1.8; } else { mDebug() << "Wrong temperature format"; return 0; } } static bool isPositiveValue( qreal value ) { // A small tolerance - if( value > -0.5 ) { - return true; - } - return false; + return value > -0.5; } static QString generateTemperatureString( qreal temp, WeatherData::TemperatureUnit format ) { QLocale locale = QLocale::system(); // We round to integer. QString string = locale.toString( floor( fromKelvin( temp, format ) + 0.5 ) ); switch ( format ) { case WeatherData::Kelvin: string += QLatin1String(" K"); break; case WeatherData::Celsius: string += QString::fromUtf8("°C"); break; case WeatherData::Fahrenheit: string += QString::fromUtf8("°F"); break; } return string; } WeatherDataPrivate& operator=( const WeatherDataPrivate &other ) { m_pubTime = other.m_pubTime; m_dataDate = other.m_dataDate; m_condition = other.m_condition; m_windDirection = other.m_windDirection; m_windSpeed = other.m_windSpeed; m_temperature = other.m_temperature; m_maxTemperature = other.m_maxTemperature; m_minTemperature = other.m_minTemperature; m_visibility = other.m_visibility; m_pressure = other.m_pressure; m_pressureDevelopment = other.m_pressureDevelopment; m_humidity = other.m_humidity; ref = other.ref; return *this; } QDateTime m_pubTime; QDate m_dataDate; WeatherData::WeatherCondition m_condition; WeatherData::WindDirection m_windDirection; // Wind speed stored in m/s qreal m_windSpeed; // Temperatures stored in Kelvin qreal m_temperature; qreal m_maxTemperature; qreal m_minTemperature; WeatherData::Visibility m_visibility; // Pressure stored in hecto pascal qreal m_pressure; WeatherData::PressureDevelopment m_pressureDevelopment; // Relative humidity qreal m_humidity; QAtomicInt ref; static QHash s_icons; static QHash s_iconPath; static WeatherData::TemperatureUnit s_standardTemperatureUnit; }; QHash WeatherDataPrivate::s_icons = QHash(); QHash WeatherDataPrivate::s_iconPath = QHash(); WeatherData::TemperatureUnit WeatherDataPrivate::s_standardTemperatureUnit = WeatherData::Celsius; WeatherData::WeatherData() : d( new WeatherDataPrivate() ) { } WeatherData::WeatherData( const WeatherData &other ) : d( other.d ) { d->ref.ref(); } WeatherData::~WeatherData() { if ( !d->ref.deref() ) delete d; } bool WeatherData::isValid() const { return hasValidPublishingTime() || hasValidDataDate() || hasValidCondition() || hasValidWindDirection() || hasValidWindSpeed() || hasValidTemperature() || hasValidMaxTemperature() || hasValidMinTemperature() || hasValidVisibility() || hasValidPressure() || hasValidPressureDevelopment() || hasValidHumidity(); } QDateTime WeatherData::publishingTime() const { return d->m_pubTime; } void WeatherData::setPublishingTime( const QDateTime& dateTime ) { detach(); d->m_pubTime = dateTime.toUTC(); } bool WeatherData::hasValidPublishingTime() const { return d->m_pubTime.isValid(); } QDate WeatherData::dataDate() const { return d->m_dataDate; } void WeatherData::setDataDate( const QDate& date ) { detach(); d->m_dataDate = date; } bool WeatherData::hasValidDataDate() const { return d->m_dataDate.isValid(); } WeatherData::WeatherCondition WeatherData::condition() const { return d->m_condition; } void WeatherData::setCondition( WeatherData::WeatherCondition condition ) { detach(); d->m_condition = condition; } bool WeatherData::hasValidCondition() const { return d->m_condition != WeatherData::ConditionNotAvailable; } QString WeatherData::conditionString() const { switch ( condition() ) { case ClearDay: return tr( "sunny" ); case ClearNight: return tr( "clear" ); case FewCloudsDay: case FewCloudsNight: return tr( "few clouds" ); case PartlyCloudyDay: case PartlyCloudyNight: return tr( "partly cloudy" ); case Overcast: return tr( "overcast" ); case LightShowersDay: case LightShowersNight: return tr( "light showers" ); case ShowersDay: case ShowersNight: return tr( "showers" ); case LightRain: return tr( "light rain" ); case Rain: return tr( "rain" ); case ChanceThunderstormDay: case ChanceThunderstormNight: return tr( "occasionally thunderstorm" ); case Thunderstorm: return tr( "thunderstorm" ); case Hail: return tr( "hail" ); case ChanceSnowDay: case ChanceSnowNight: return tr( "occasionally snow" ); case LightSnow: return tr( "light snow" ); case Snow: return tr( "snow" ); case RainSnow: return tr( "rain and snow" ); case Mist: return tr( "mist" ); case SandStorm: return tr( "sandstorm" ); default: return "Condition not available"; } } QImage WeatherData::icon() const { QImage icon = WeatherDataPrivate::s_icons.value( condition() ); // If the icon is in the hash, simply return it. if ( !icon.isNull() ) { return icon; } // If it isn't in the hash, the icon will be created (from the value stored in s_iconPath). else { icon = QImage( WeatherDataPrivate::s_iconPath.value( condition() ) ); WeatherDataPrivate::s_icons.insert( condition(), icon ); return icon; } } QString WeatherData::iconSource() const { QString const invalid = MarbleDirs::path(QStringLiteral("weather/weather-none-available.png")); QString const icon = WeatherDataPrivate::s_iconPath.value( condition() ); return icon == invalid ? "" : icon; } WeatherData::WindDirection WeatherData::windDirection() const { return d->m_windDirection; } void WeatherData::setWindDirection( WeatherData::WindDirection direction ) { detach(); d->m_windDirection = direction; } bool WeatherData::hasValidWindDirection() const { return d->m_windDirection != WeatherData::DirectionNotAvailable; } QString WeatherData::windDirectionString() const { switch ( windDirection() ) { case N: return tr( "N" ); case NNE: return tr( "NNE" ); case NE: return tr( "NE" ); case ENE: return tr( "ENE" ); case E: return tr( "E" ); case SSE: return tr( "SSE" ); case SE: return tr( "SE" ); case ESE: return tr( "ESE" ); case S: return tr( "S" ); case NNW: return tr( "NNW" ); case NW: return tr( "NW" ); case WNW: return tr( "WNW" ); case W: return tr( "W" ); case SSW: return tr( "SSW" ); case SW: return tr( "SW" ); case WSW: return tr( "WSW" ); default: return ""; } } qreal WeatherData::windSpeed( WeatherData::SpeedUnit format ) const { if ( WeatherData::mps == format ) { return d->m_windSpeed; } if ( WeatherData::kph == format ) { return d->m_windSpeed * MPS2KPH; } else if ( WeatherData::mph == format ) { return d->m_windSpeed * MPS2MPH; } else if ( WeatherData::knots == format ) { return d->m_windSpeed * MPS2KN; } else if ( WeatherData::beaufort == format ) { if( d->m_windSpeed < 0.3 ) return 0; else if( d->m_windSpeed < 1.6 ) return 1; else if( d->m_windSpeed < 3.4 ) return 2; else if( d->m_windSpeed < 5.5 ) return 3; else if( d->m_windSpeed < 8.0 ) return 4; else if( d->m_windSpeed < 10.8 ) return 5; else if( d->m_windSpeed < 13.9 ) return 6; else if( d->m_windSpeed < 17.2 ) return 7; else if( d->m_windSpeed < 20.8 ) return 8; else if( d->m_windSpeed < 24.5 ) return 9; else if( d->m_windSpeed < 28.5 ) return 10; else if( d->m_windSpeed < 32.7 ) return 11; else return 12; } else { mDebug() << "Wrong speed format"; return 0; } } void WeatherData::setWindSpeed( qreal speed, WeatherData::SpeedUnit format ) { detach(); if ( WeatherData::mps == format ) { d->m_windSpeed = speed; } if ( WeatherData::kph == format ) { d->m_windSpeed = speed * KPH2MPS; } else if ( WeatherData::mph == format ) { d->m_windSpeed = speed * MPH2MPS; } else if ( WeatherData::knots == format ) { d->m_windSpeed = speed * KN2MPS; } else if ( WeatherData::beaufort == format ) { int rounded = (int) speed; if( 0 == rounded ) d->m_windSpeed = 0.15; else if( 1 == rounded ) d->m_windSpeed = 0.95; else if( 2 == rounded ) d->m_windSpeed = 2.5; else if( 3 == rounded ) d->m_windSpeed = 4.45; else if( 4 == rounded ) d->m_windSpeed = 6.75; else if( 5 == rounded ) d->m_windSpeed = 9.4; else if( 6 == rounded ) d->m_windSpeed = 12.35; else if( 7 == rounded ) d->m_windSpeed = 15.55; else if( 8 == rounded ) d->m_windSpeed = 19.0; else if( 9 == rounded ) d->m_windSpeed = 22.65; else if( 10 == rounded ) d->m_windSpeed = 26.5; else if( 11 == rounded ) d->m_windSpeed = 30.6; else d->m_windSpeed = 34; } else { mDebug() << "Wrong speed format"; } } bool WeatherData::hasValidWindSpeed() const { return d->isPositiveValue( d->m_windSpeed ); } QString WeatherData::windSpeedString( WeatherData::SpeedUnit unit ) const { QLocale locale = QLocale::system(); // We round to integer. QString string = locale.toString( floor( windSpeed( unit ) + 0.5 ) ); string += QLatin1Char(' '); switch ( unit ) { case WeatherData::kph: string += QObject::tr("km/h"); break; case WeatherData::mph: string += QObject::tr("mph"); break; case WeatherData::mps: string += QObject::tr( "m/s" ); break; case WeatherData::knots: string += QObject::tr( "knots" ); break; case WeatherData::beaufort: string += QObject::tr( "Beaufort" ); break; } return string; } qreal WeatherData::temperature( WeatherData::TemperatureUnit format ) const { return d->fromKelvin( d->m_temperature, format ); } void WeatherData::setTemperature( qreal temp, WeatherData::TemperatureUnit format ) { detach(); d->m_temperature = d->toKelvin( temp, format ); } bool WeatherData::hasValidTemperature() const { return d->isPositiveValue( d->m_temperature ); } QString WeatherData::temperatureString( WeatherData::TemperatureUnit format ) const { return d->generateTemperatureString( d->m_temperature, format ); } qreal WeatherData::maxTemperature( WeatherData::TemperatureUnit format ) const { return d->fromKelvin( d->m_maxTemperature, format ); } void WeatherData::setMaxTemperature( qreal temp, WeatherData::TemperatureUnit format ) { detach(); d->m_maxTemperature = d->toKelvin( temp, format ); } QString WeatherData::maxTemperatureString( WeatherData::TemperatureUnit format ) const { return d->generateTemperatureString( d->m_maxTemperature, format ); } bool WeatherData::hasValidMaxTemperature() const { return d->isPositiveValue( d->m_maxTemperature ); } qreal WeatherData::minTemperature( WeatherData::TemperatureUnit format ) const { return d->fromKelvin( d->m_minTemperature, format ); } QString WeatherData::minTemperatureString( WeatherData::TemperatureUnit format ) const { return d->generateTemperatureString( d->m_minTemperature, format ); } void WeatherData::setMinTemperature( qreal temp, WeatherData::TemperatureUnit format ) { detach(); d->m_minTemperature = d->toKelvin( temp, format ); } bool WeatherData::hasValidMinTemperature() const { return d->isPositiveValue( d->m_minTemperature ); } WeatherData::Visibility WeatherData::visibility() const { return d->m_visibility; } void WeatherData::setVisibilty( WeatherData::Visibility visibility ) { detach(); d->m_visibility = visibility; } bool WeatherData::hasValidVisibility() const { return d->m_visibility != WeatherData::VisibilityNotAvailable; } qreal WeatherData::pressure( WeatherData::PressureUnit format ) const { if ( WeatherData::HectoPascal == format ) { return d->m_pressure; } else if ( WeatherData::KiloPascal == format ) { return d->m_pressure * HPA2KPA; } else if ( WeatherData::Bar == format ) { return d->m_pressure * HPA2BAR; } else if ( WeatherData::mmHg == format ) { return d->m_pressure * HPA2HG; } else if ( WeatherData::inchHg == format ) { return d->m_pressure * HPA2IHG; } else { mDebug() << "Wrong pressure format"; return 0; } } void WeatherData::setPressure( qreal pressure, WeatherData::PressureUnit format ) { detach(); if ( WeatherData::HectoPascal == format ) { d->m_pressure = pressure; } else if ( WeatherData::KiloPascal == format ) { d->m_pressure = pressure * KPA2HPA; } else if ( WeatherData::Bar == format ) { d->m_pressure = pressure * BAR2HPA; } else if ( WeatherData::mmHg == format ) { d->m_pressure = pressure * HG2HPA; } else if ( WeatherData::inchHg == format ) { d->m_pressure = pressure * IHG2HPA; } else { mDebug() << "Wrong pressure format"; } } bool WeatherData::hasValidPressure() const { return d->isPositiveValue( d->m_pressure ); } QString WeatherData::pressureString( WeatherData::PressureUnit unit ) const { QLocale locale = QLocale::system(); // We round to integer. QString string = locale.toString( pressure( unit ), 'f', 2 ); string += QLatin1Char(' '); switch ( unit ) { case WeatherData::HectoPascal: string += tr( "hPa" ); break; case WeatherData::KiloPascal: string += tr( "kPa" ); break; case WeatherData::Bar: string += tr( "Bar" ); break; case WeatherData::mmHg: string += tr( "mmHg" ); break; case WeatherData::inchHg: string += tr( "inch Hg" ); break; } return string; } WeatherData::PressureDevelopment WeatherData::pressureDevelopment() const { return d->m_pressureDevelopment; } void WeatherData::setPressureDevelopment( WeatherData::PressureDevelopment pressureDevelopment ) { detach(); d->m_pressureDevelopment = pressureDevelopment; } bool WeatherData::hasValidPressureDevelopment() const { return d->m_pressureDevelopment != WeatherData::PressureDevelopmentNotAvailable; } QString WeatherData::pressureDevelopmentString() const { switch ( pressureDevelopment() ) { case Rising: return tr( "rising", "air pressure is rising" ); case NoChange: return tr( "steady", "air pressure has no change" ); case Falling: return tr( "falling", "air pressure falls" ); default: return ""; } } qreal WeatherData::humidity() const { return d->m_humidity; } void WeatherData::setHumidity( qreal humidity ) { detach(); d->m_humidity = humidity; } bool WeatherData::hasValidHumidity() const { return d->isPositiveValue( d->m_humidity ); } QString WeatherData::humidityString() const { return QString( "%1 %" ).arg( humidity() ); } QString WeatherData::toHtml( WeatherData::TemperatureUnit temperatureUnit, WeatherData::SpeedUnit speedUnit, WeatherData::PressureUnit pressureUnit ) const { QString html; if ( hasValidPublishingTime() ) { html += tr( "Publishing time: %1
" ) .arg( publishingTime().toLocalTime().toString() ); } if ( hasValidCondition() ) { html += tr( "Condition: %1
" ) .arg( conditionString() ); } if ( hasValidTemperature() ) { html += tr( "Temperature: %1
" ) .arg( temperatureString( temperatureUnit ) ); } if ( hasValidMaxTemperature() ) { html += tr( "Max temperature: %1
" ) .arg( maxTemperatureString( temperatureUnit ) ); } if ( hasValidMinTemperature() ) { html += tr( "Min temperature: %1
" ) .arg( minTemperatureString( temperatureUnit ) ); } if ( hasValidWindDirection() ) { html += tr( "Wind direction: %1
" ) .arg( windDirectionString() ); } if ( hasValidWindSpeed() ) { html += tr( "Wind speed: %1
" ) .arg( windSpeedString( speedUnit ) ); } if ( hasValidPressure() ) { html += tr( "Pressure: %1
" ) .arg( pressureString( pressureUnit ) ); } if ( hasValidPressureDevelopment() ) { html += tr( "Pressure development: %1
") .arg( pressureDevelopmentString() ); } if ( hasValidHumidity() ) { html += tr( "Humidity: %1
" ) .arg( humidityString() ); } return html; } WeatherData& WeatherData::operator=( const WeatherData &other ) { qAtomicAssign( d, other.d ); return *this; } void WeatherData::detach() { qAtomicDetach( d ); } } // namespace Marble diff --git a/src/plugins/runner/pn2/Pn2Runner.cpp b/src/plugins/runner/pn2/Pn2Runner.cpp index 11a788232..32a8f8afe 100644 --- a/src/plugins/runner/pn2/Pn2Runner.cpp +++ b/src/plugins/runner/pn2/Pn2Runner.cpp @@ -1,414 +1,408 @@ // // 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. // // // For the Natural Earth Layer providing the Default data set at 0.5 arcminute resolution should be enough. // This fileformat allows for even better packed data than the PNT format. For detailed polygons at arcminute // scale on average it should use only 33% of the amount used by PNT. // // Description of the file format // // In the fileformat initially a file header is provided that provides the file format version and the number // of polygons stored inside the file. A Polygon starts with the Polygon Header which provides the feature id // and the number of so called "absolute nodes" that are about to follow. Absolute nodes always contain // absolute geodetic coordinates. The Polygon Header also provides a flag that allows to specify whether the // polygon is supposed to represent a line string ("0") or a linear ring ("1"). Each absolute node can be followed // by relative nodes: These relative nodes are always nodes that follow in correct order inside the polygon after // "their" absolute node. Each absolute node specifies the number of relative nodes which contain relative // coordinates in reference to their absolute node. So an absolute node provides the absolute reference for // relative nodes across a theoretical area of 2x2 squaredegree-area (which in practice frequently might rather // amount to 1x1 square degrees). // // So much of the compression works by just referencing lat/lon diffs to special "absolute nodes". Hence the // compression will especially work well for polygons with many nodes with a high node density. // // The parser has to convert these relative coordinates to absolute coordinates. // // Copyright 2012 Torsten Rahn // Copyright 2012 Cezar Mocan // Copyright 2014 Abhinav Gangwar // #include "Pn2Runner.h" #include "GeoDataDocument.h" #include "GeoDataPlacemark.h" #include "GeoDataStyle.h" #include "GeoDataPolyStyle.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" #include "GeoDataMultiGeometry.h" #include "MarbleDebug.h" #include #include namespace Marble { // Polygon header flags, representing the type of polygon enum polygonFlagType { LINESTRING = 0, LINEARRING = 1, OUTERBOUNDARY = 2, INNERBOUNDARY = 3, MULTIGEOMETRY = 4 }; Pn2Runner::Pn2Runner(QObject *parent) : ParsingRunner(parent) { } Pn2Runner::~Pn2Runner() { } bool Pn2Runner::errorCheckLat( qint16 lat ) { - if ( lat >= -10800 && lat <= +10800 ) - return false; - else - return true; + return !(lat >= -10800 && lat <= +10800); } bool Pn2Runner::errorCheckLon( qint16 lon ) { - if ( lon >= -21600 && lon <= +21600 ) - return false; - else - return true; + return !(lon >= -21600 && lon <= +21600); } bool Pn2Runner::importPolygon( QDataStream &stream, GeoDataLineString* linestring, quint32 nrAbsoluteNodes ) { qint16 lat, lon, nrRelativeNodes; qint8 relativeLat, relativeLon; bool error = false; for ( quint32 absoluteNode = 1; absoluteNode <= nrAbsoluteNodes; absoluteNode++ ) { stream >> lat >> lon >> nrRelativeNodes; error = error | errorCheckLat( lat ) | errorCheckLon( lon ); qreal degLat = ( 1.0 * lat / 120.0 ); qreal degLon = ( 1.0 * lon / 120.0 ); GeoDataCoordinates coord( degLon / 180 * M_PI, degLat / 180 * M_PI ); linestring->append( coord ); for ( qint16 relativeNode = 1; relativeNode <= nrRelativeNodes; ++relativeNode ) { stream >> relativeLat >> relativeLon; qint16 currLat = relativeLat + lat; qint16 currLon = relativeLon + lon; error = error | errorCheckLat( currLat ) | errorCheckLon( currLon ); qreal currDegLat = ( 1.0 * currLat / 120.0 ); qreal currDegLon = ( 1.0 * currLon / 120.0 ); GeoDataCoordinates currCoord( currDegLon / 180 * M_PI, currDegLat / 180 * M_PI ); linestring->append( currCoord ); } } *linestring = linestring->optimized(); return error; } GeoDataDocument *Pn2Runner::parseFile(const QString &fileName, DocumentRole role, QString &error) { QFileInfo fileinfo( fileName ); if (fileinfo.suffix().compare(QLatin1String("pn2"), Qt::CaseInsensitive) != 0) { error = QStringLiteral("File %1 does not have a pn2 suffix").arg(fileName); mDebug() << error; return nullptr; } QFile file( fileName ); if ( !file.exists() ) { error = QStringLiteral("File %1 does not exist").arg(fileName); mDebug() << error; return nullptr; } file.open( QIODevice::ReadOnly ); m_stream.setDevice( &file ); // read the data serialized from the file m_stream >> m_fileHeaderVersion >> m_fileHeaderPolygons >> m_isMapColorField; switch( m_fileHeaderVersion ) { case 1: return parseForVersion1( fileName, role ); break; case 2: return parseForVersion2( fileName, role ); break; default: qDebug() << "File can't be parsed. We don't have parser for file header version:" << m_fileHeaderVersion; break; } return nullptr; } GeoDataDocument* Pn2Runner::parseForVersion1(const QString& fileName, DocumentRole role) { GeoDataDocument *document = new GeoDataDocument(); document->setDocumentRole( role ); bool error = false; quint32 ID, nrAbsoluteNodes; quint8 flag, prevFlag = -1; GeoDataStyle::Ptr style; GeoDataPolygon *polygon = new GeoDataPolygon; for ( quint32 currentPoly = 1; ( currentPoly <= m_fileHeaderPolygons ) && ( !error ) && ( !m_stream.atEnd() ); currentPoly++ ) { m_stream >> ID >> nrAbsoluteNodes >> flag; if ( flag != INNERBOUNDARY && ( prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY ) ) { GeoDataPlacemark *placemark = new GeoDataPlacemark; placemark->setGeometry( polygon ); if ( m_isMapColorField ) { if ( style ) { placemark->setStyle( style ); } } document->append( placemark ); } if ( flag == LINESTRING ) { GeoDataLineString *linestring = new GeoDataLineString; error = error | importPolygon( m_stream, linestring, nrAbsoluteNodes ); GeoDataPlacemark *placemark = new GeoDataPlacemark; placemark->setGeometry( linestring ); document->append( placemark ); } if ( ( flag == LINEARRING ) || ( flag == OUTERBOUNDARY ) || ( flag == INNERBOUNDARY ) ) { if ( flag == OUTERBOUNDARY && m_isMapColorField ) { quint8 colorIndex; m_stream >> colorIndex; style = GeoDataStyle::Ptr(new GeoDataStyle); GeoDataPolyStyle polyStyle; polyStyle.setColorIndex( colorIndex ); style->setPolyStyle( polyStyle ); } GeoDataLinearRing* linearring = new GeoDataLinearRing; error = error | importPolygon( m_stream, linearring, nrAbsoluteNodes ); if ( flag == LINEARRING ) { GeoDataPlacemark *placemark = new GeoDataPlacemark; placemark->setGeometry( linearring ); document->append( placemark ); } if ( flag == OUTERBOUNDARY ) { polygon = new GeoDataPolygon; polygon->setOuterBoundary( *linearring ); } if ( flag == INNERBOUNDARY ) { polygon->appendInnerBoundary( *linearring ); } } if ( flag == MULTIGEOMETRY ) { // not implemented yet, for now elements inside a multigeometry are separated as individual geometries } prevFlag = flag; } if ( prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY ) { GeoDataPlacemark *placemark = new GeoDataPlacemark; if ( m_isMapColorField ) { if ( style ) { placemark->setStyle( style ); } } placemark->setGeometry( polygon ); document->append( placemark ); } if ( error ) { delete document; document = 0; return nullptr; } document->setFileName( fileName ); return document; } GeoDataDocument* Pn2Runner::parseForVersion2( const QString &fileName, DocumentRole role ) { GeoDataDocument *document = new GeoDataDocument(); document->setDocumentRole( role ); bool error = false; quint32 nrAbsoluteNodes; quint32 placemarkCurrentID = 1; quint32 placemarkPrevID = 0; quint8 flag, prevFlag = -1; GeoDataPolygon *polygon = new GeoDataPolygon; GeoDataStyle::Ptr style; GeoDataPlacemark *placemark =0; // new GeoDataPlacemark; quint32 currentPoly; for ( currentPoly = 1; ( currentPoly <= m_fileHeaderPolygons ) && ( !error ) && ( !m_stream.atEnd() ); currentPoly++ ) { m_stream >> flag >> placemarkCurrentID; if ( flag == MULTIGEOMETRY && ( prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY ) ) { if ( placemark ) { placemark->setGeometry( polygon ); } } if ( flag != MULTIGEOMETRY && flag != INNERBOUNDARY && ( prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY ) ) { if ( placemark ) { placemark->setGeometry( polygon ); } } /** * If the parsed placemark id @p placemarkCurrentID is different * from the id of previous placemark @p placemarkPrevID, it means * we have encountered a new placemark. So, prepare a style @p style * if file has color indices */ if ( placemarkCurrentID != placemarkPrevID ) { placemark = new GeoDataPlacemark; // Handle the color index if( m_isMapColorField ) { quint8 colorIndex; m_stream >> colorIndex; style = GeoDataStyle::Ptr(new GeoDataStyle); GeoDataPolyStyle polyStyle; polyStyle.setColorIndex( colorIndex ); polyStyle.setFill( true ); style->setPolyStyle( polyStyle ); placemark->setStyle( style ); } document->append( placemark ); } placemarkPrevID = placemarkCurrentID; if ( flag != MULTIGEOMETRY ) { m_stream >> nrAbsoluteNodes; if ( flag == LINESTRING ) { GeoDataLineString *linestring = new GeoDataLineString; error = error | importPolygon( m_stream, linestring, nrAbsoluteNodes ); if ( placemark ) { placemark->setGeometry( linestring ); } } if ( ( flag == LINEARRING ) || ( flag == OUTERBOUNDARY ) || ( flag == INNERBOUNDARY ) ) { GeoDataLinearRing* linearring = new GeoDataLinearRing; error = error || importPolygon( m_stream, linearring, nrAbsoluteNodes ); if ( flag == LINEARRING ) { if ( placemark ) { placemark->setGeometry( linearring ); } } else { if ( flag == OUTERBOUNDARY ) { polygon = new GeoDataPolygon; polygon->setOuterBoundary( *linearring ); } if ( flag == INNERBOUNDARY ) { polygon->appendInnerBoundary( *linearring ); } delete linearring; } } prevFlag = flag; } else { quint32 placemarkCurrentIDInMulti; quint8 flagInMulti; quint8 prevFlagInMulti = -1; quint8 multiSize = 0; m_stream >> multiSize; GeoDataMultiGeometry *multigeom = new GeoDataMultiGeometry; /** * Read @p multiSize GeoDataGeometry objects */ for ( int iter = 0; iter < multiSize; ++iter ) { m_stream >> flagInMulti >> placemarkCurrentIDInMulti >> nrAbsoluteNodes; if ( flagInMulti != INNERBOUNDARY && ( prevFlagInMulti == INNERBOUNDARY || prevFlagInMulti == OUTERBOUNDARY ) ) { multigeom->append( polygon ); } if ( flagInMulti == LINESTRING ) { GeoDataLineString *linestring = new GeoDataLineString; error = error || importPolygon( m_stream, linestring, nrAbsoluteNodes ); multigeom->append( linestring ); } if ( ( flagInMulti == LINEARRING ) || ( flagInMulti == OUTERBOUNDARY ) || ( flagInMulti == INNERBOUNDARY ) ) { GeoDataLinearRing* linearring = new GeoDataLinearRing; error = error | importPolygon( m_stream, linearring, nrAbsoluteNodes ); if ( flagInMulti == LINEARRING ) { multigeom->append( linearring ); } else { if ( flagInMulti == OUTERBOUNDARY ) { polygon = new GeoDataPolygon; polygon->setOuterBoundary( *linearring ); } if ( flagInMulti == INNERBOUNDARY ) { polygon->appendInnerBoundary( *linearring ); } delete linearring; } } prevFlagInMulti = flagInMulti; } if ( prevFlagInMulti == INNERBOUNDARY || prevFlagInMulti == OUTERBOUNDARY ) { multigeom->append( polygon ); } if ( placemark ) { placemark->setGeometry( multigeom ); } prevFlag = MULTIGEOMETRY; } } if ( (prevFlag == INNERBOUNDARY || prevFlag == OUTERBOUNDARY) && prevFlag != MULTIGEOMETRY ) { placemark->setGeometry( polygon ); } if ( error ) { delete document; document = 0; return nullptr; } document->setFileName( fileName ); return document; } } #include "moc_Pn2Runner.cpp"