Git commit 983724a4534fd7861ba33d969f48362b56818a56 by Torsten Rahn. Committed on 31/08/2014 at 22:02. Pushed by rahn into branch 'master'. - Cleaned up and refactored the AzimuthalProjection and GnomonicProjection. - Turned GnomonicScanlineTextureMapper into a GenericScanlineTextureMapper. - Got rid of all remaining projection specific code that used viewport->pro= jection. - Moved the Gnomonic projection to the right toolbutton menu. - Prepared for further inclusion of "spherical" projections. - Fixed a crash bug in the GnomonicProjection that would result from another division by zero. M +1 -1 src/lib/marble/CMakeLists.txt A +281 -0 src/lib/marble/GenericScanlineTextureMapper.cpp [License= : LGPL] R +5 -5 src/lib/marble/GenericScanlineTextureMapper.h [from: src/lib= /marble/GnomonicScanlineTextureMapper.h - 080% similarity] M +5 -2 src/lib/marble/GeoPainter.cpp D +0 -201 src/lib/marble/GnomonicScanlineTextureMapper.cpp M +8 -16 src/lib/marble/MapViewWidget.cpp M +13 -18 src/lib/marble/MercatorScanlineTextureMapper.cpp M +11 -21 src/lib/marble/TextureColorizer.cpp M +2 -2 src/lib/marble/layers/TextureLayer.cpp M +11 -0 src/lib/marble/projections/AbstractProjection.cpp M +5 -1 src/lib/marble/projections/AbstractProjection.h M +523 -1 src/lib/marble/projections/AzimuthalProjection.cpp M +10 -0 src/lib/marble/projections/AzimuthalProjection.h M +43 -0 src/lib/marble/projections/AzimuthalProjection_p.h M +7 -561 src/lib/marble/projections/GnomonicProjection.cpp M +2 -8 src/lib/marble/projections/GnomonicProjection.h M +1 -590 src/lib/marble/projections/SphericalProjection.cpp M +0 -6 src/lib/marble/projections/SphericalProjection.h http://commits.kde.org/marble/983724a4534fd7861ba33d969f48362b56818a56 diff --git a/src/lib/marble/CMakeLists.txt b/src/lib/marble/CMakeLists.txt index de46c41..9b3986e 100644 --- a/src/lib/marble/CMakeLists.txt +++ b/src/lib/marble/CMakeLists.txt @@ -166,7 +166,7 @@ set(marblewidget_SRCS EquirectScanlineTextureMapper.cpp MercatorScanlineTextureMapper.cpp TileScalingTextureMapper.cpp - GnomonicScanlineTextureMapper.cpp + GenericScanlineTextureMapper.cpp VectorTileModel.cpp DiscCache.cpp ServerLayout.cpp diff --git a/src/lib/marble/GenericScanlineTextureMapper.cpp b/src/lib/marb= le/GenericScanlineTextureMapper.cpp new file mode 100644 index 0000000..2b7c73a --- /dev/null +++ b/src/lib/marble/GenericScanlineTextureMapper.cpp @@ -0,0 +1,281 @@ +// +// 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 Carlos Licea +// Copyright 2008 Inge Wallin +// Copyright 2011 Bernhard Beschow +// + + +// local +#include "GenericScanlineTextureMapper.h" + +// Qt +#include +#include +#include + +// Marble +#include "GeoPainter.h" +#include "MarbleDirs.h" +#include "MarbleDebug.h" +#include "ScanlineTextureMapperContext.h" +#include "StackedTileLoader.h" +#include "TextureColorizer.h" +#include "ViewParams.h" +#include "ViewportParams.h" +#include "MathHelper.h" +#include "AbstractProjection.h" + +using namespace Marble; + +class GenericScanlineTextureMapper::RenderJob : public QRunnable +{ +public: + RenderJob( StackedTileLoader *tileLoader, int tileLevel, QImage *canva= sImage, const ViewportParams *viewport, MapQuality mapQuality, int yTop, in= t yBottom ); + + virtual void run(); + +private: + StackedTileLoader *const m_tileLoader; + const int m_tileLevel; + QImage *const m_canvasImage; + const ViewportParams *const m_viewport; + const MapQuality m_mapQuality; + const int m_yTop; + const int m_yBottom; +}; + +GenericScanlineTextureMapper::RenderJob::RenderJob( StackedTileLoader *til= eLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewport= , MapQuality mapQuality, int yTop, int yBottom ) + : m_tileLoader( tileLoader ), + m_tileLevel( tileLevel ), + m_canvasImage( canvasImage ), + m_viewport( viewport ), + m_mapQuality( mapQuality ), + m_yTop( yTop ), + m_yBottom( yBottom ) +{ +} + + +GenericScanlineTextureMapper::GenericScanlineTextureMapper( StackedTileLoa= der *tileLoader ) + : TextureMapperInterface() + , m_tileLoader( tileLoader ) + , m_radius( 0 ) + , m_threadPool() +{ +} + +void GenericScanlineTextureMapper::mapTexture( GeoPainter *painter, + const ViewportParams *view= port, + int tileZoomLevel, + const QRect &dirtyRect, + TextureColorizer *texColor= izer ) +{ + if ( m_canvasImage.size() !=3D viewport->size() || m_radius !=3D viewp= ort->radius() ) { + const QImage::Format optimalFormat =3D ScanlineTextureMapperContex= t::optimalCanvasImageFormat( viewport ); + + if ( m_canvasImage.size() !=3D viewport->size() || m_canvasImage.f= ormat() !=3D optimalFormat ) { + m_canvasImage =3D QImage( viewport->size(), optimalFormat ); + } + + if ( !viewport->mapCoversViewport() ) { + m_canvasImage.fill( 0 ); + } + + m_radius =3D viewport->radius(); + m_repaintNeeded =3D true; + } + + if ( m_repaintNeeded ) { + mapTexture( viewport, tileZoomLevel, painter->mapQuality() ); + + if ( texColorizer ) { + texColorizer->colorize( &m_canvasImage, viewport, painter->map= Quality() ); + } + + m_repaintNeeded =3D false; + } + + const int radius =3D viewport->radius() * viewport->currentProjection(= )->clippingRadius(); + + QRect rect( viewport->width() / 2 - radius, viewport->height() / 2 - r= adius, + 2 * radius, 2 * radius); +#if QT_VERSION < 0x050000 + rect =3D rect.intersect( dirtyRect ); +#else + rect =3D rect.intersected( dirtyRect ); +#endif + painter->drawImage( rect, m_canvasImage, rect ); +} + +void GenericScanlineTextureMapper::mapTexture( const ViewportParams *viewp= ort, int tileZoomLevel, MapQuality mapQuality ) +{ + // Reset backend + m_tileLoader->resetTilehash(); + + const int imageHeight =3D viewport->height(); + const qint64 radius =3D viewport->radius() * viewport->currentPr= ojection()->clippingRadius(); + + // Calculate the actual y-range of the map on the screen + const int skip =3D ( mapQuality =3D=3D LowQuality ) ? 1 + : 0; + const int yTop =3D ( imageHeight / 2 - radius >=3D 0 ) ? imageHeight /= 2 - radius + : 0; + const int yBottom =3D ( yTop =3D=3D 0 ) ? imageHeight - skip + : yTop + radius + radius - skip; + + const int numThreads =3D m_threadPool.maxThreadCount(); + const int yStep =3D qCeil(qreal( yBottom - yTop ) / qreal(numThreads)); + for ( int i =3D 0; i < numThreads; ++i ) { + const int yStart =3D yTop + i * yStep; + const int yEnd =3D qMin(yBottom, yTop + (i + 1) * yStep); + QRunnable *const job =3D new RenderJob( m_tileLoader, tileZoomLeve= l, &m_canvasImage, viewport, mapQuality, yStart, yEnd ); + m_threadPool.start( job ); + } + + m_threadPool.waitForDone(); + + m_tileLoader->cleanupTilehash(); +} + +void GenericScanlineTextureMapper::RenderJob::run() +{ + const int imageWidth =3D m_canvasImage->width(); + const int imageHeight =3D m_canvasImage->height(); + const qint64 radius =3D m_viewport->radius(); + // Calculate how many degrees are being represented per pixel. + const qreal rad2Pixel =3D ( 2 * radius ) / M_PI; + const qreal pixel2Rad =3D 1.0/rad2Pixel; + + const bool interlaced =3D ( m_mapQuality =3D=3D LowQuality ); + const bool highQuality =3D ( m_mapQuality =3D=3D HighQuality + || m_mapQuality =3D=3D PrintQuality ); + const bool printQuality =3D ( m_mapQuality =3D=3D PrintQuality ); + + // Evaluate the degree of interpolation + const int n =3D ScanlineTextureMapperContext::interpolationStep( m_vie= wport, m_mapQuality ); + + // Calculate north pole position to decrease pole distortion later on + qreal northPoleX, northPoleY; + bool globeHidesNorthPole; + GeoDataCoordinates northPole(0, m_viewport->currentProjection()->maxLa= t(), 0); + m_viewport->screenCoordinates(northPole, northPoleX, northPoleY, globe= HidesNorthPole ); + + // initialize needed variables that are modified during texture mappin= g: + + ScanlineTextureMapperContext context( m_tileLoader, m_tileLevel ); + + qreal clipRadius =3D radius * m_viewport->currentProjection()->clippin= gRadius(); + + + // Paint the map. + for ( int y =3D m_yTop; y < m_yBottom; ++y ) { + + // rx is the radius component in x direction + const int rx =3D (int)sqrt( (qreal)( clipRadius * clipRadius + - ( ( y - imageHeight / 2 ) + * ( y - imageHeight / 2 ) ) ) ); + + // Calculate the actual x-range of the map within the current scan= line. + // + // If the circular border of the earth disk is still visible then = xLeft + // equals the scanline position of the most left pixel that gets c= overed + // by the earth disk. In terms of math this equals the half image = width minus + // the radius component on the current scanline in x direction ("r= x"). + // + // If the zoom factor is high enough then the whole screen gets co= vered + // by the earth and the border of the earth disk isn't visible any= more. + // In that situation xLeft equals zero. + // For xRight the situation is similar. + + const int xLeft =3D ( imageWidth / 2 - rx > 0 ) ? imageWidth / 2 = - rx + : 0; + const int xRight =3D ( imageWidth / 2 - rx > 0 ) ? xLeft + rx + rx + : imageWidth; + + QRgb * scanLine =3D (QRgb*)( m_canvasImage->scanLine( y ) ) + xLef= t; + + const int xIpLeft =3D ( imageWidth / 2 - rx > 0 ) ? n * (int)( xL= eft / n + 1 ) + : 1; + const int xIpRight =3D ( imageWidth / 2 - rx > 0 ) ? n * (int)( xR= ight / n - 1 ) + : n * (int)( xRig= ht / n - 1 ) + 1; + + // Decrease pole distortion due to linear approximation ( y-axis ) + bool crossingPoleArea =3D false; + if ( !globeHidesNorthPole + && northPoleY - ( n * 0.75 ) <=3D y + && northPoleY + ( n * 0.75 ) >=3D y ) + { + crossingPoleArea =3D true; + } + + int ncount =3D 0; + + + for ( int x =3D xLeft; x < xRight; ++x ) { + + // Prepare for interpolation + const int leftInterval =3D xIpLeft + ncount * n; + + bool interpolate =3D false; + + if ( x >=3D xIpLeft && x <=3D xIpRight ) { + + // Decrease pole distortion due to linear approximation ( = x-axis ) + if ( crossingPoleArea + && northPoleX >=3D leftInterval + n + && northPoleX < leftInterval + 2 * n + && x < leftInterval + 3 * n ) + { + interpolate =3D false; + } + else { + x +=3D n - 1; + interpolate =3D !printQuality; + ++ncount; + } + } + else + interpolate =3D false; + + qreal lon; + qreal lat; + m_viewport->geoCoordinates(x,y, lon, lat, GeoDataCoordinates::= Radian); + + if ( interpolate ) { + if ( highQuality ) + context.pixelValueApproxF( lon, lat, scanLine, n ); + else + context.pixelValueApprox( lon, lat, scanLine, n ); + + scanLine +=3D ( n - 1 ); + } + + if ( x < imageWidth ) { + if ( highQuality ) + context.pixelValueF( lon, lat, scanLine ); + else + context.pixelValue( lon, lat, scanLine ); + } + + ++scanLine; + lon +=3D pixel2Rad; + } + + // copy scanline to improve performance + if ( interlaced && y + 1 < m_yBottom ) { + + const int pixelByteSize =3D m_canvasImage->bytesPerLine() / im= ageWidth; + + memcpy( m_canvasImage->scanLine( y + 1 ) + xLeft * pixelByteSi= ze, + m_canvasImage->scanLine( y ) + xLeft * pixelByteSi= ze, + ( xRight - xLeft ) * pixelByteSize ); + ++y; + } + } +} diff --git a/src/lib/marble/GnomonicScanlineTextureMapper.h b/src/lib/marbl= e/GenericScanlineTextureMapper.h similarity index 80% rename from src/lib/marble/GnomonicScanlineTextureMapper.h rename to src/lib/marble/GenericScanlineTextureMapper.h index f30e9ba..196c14d 100644 --- a/src/lib/marble/GnomonicScanlineTextureMapper.h +++ b/src/lib/marble/GenericScanlineTextureMapper.h @@ -8,8 +8,8 @@ // Copyright 2011 Bernhard Beschow // = -#ifndef MARBLE_GNOMONICSCANLINETEXTUREMAPPER_H -#define MARBLE_GNOMONICSCANLINETEXTUREMAPPER_H +#ifndef MARBLE_GENERICSCANLINETEXTUREMAPPER_H +#define MARBLE_GENERICSCANLINETEXTUREMAPPER_H = = #include "TextureMapperInterface.h" @@ -25,10 +25,10 @@ namespace Marble = class StackedTileLoader; = -class GnomonicScanlineTextureMapper : public TextureMapperInterface +class GenericScanlineTextureMapper : public TextureMapperInterface { public: - GnomonicScanlineTextureMapper( StackedTileLoader *tileLoader ); + GenericScanlineTextureMapper( StackedTileLoader *tileLoader ); = virtual void mapTexture( GeoPainter *painter, const ViewportParams *viewport, @@ -49,4 +49,4 @@ class GnomonicScanlineTextureMapper : public TextureMappe= rInterface = } = -#endif \ No newline at end of file +#endif diff --git a/src/lib/marble/GeoPainter.cpp b/src/lib/marble/GeoPainter.cpp index 9ba3f40..a2d7e0e 100644 --- a/src/lib/marble/GeoPainter.cpp +++ b/src/lib/marble/GeoPainter.cpp @@ -25,6 +25,7 @@ = #include "MarbleGlobal.h" #include "ViewportParams.h" +#include "AbstractProjection.h" = // #define MARBLE_DEBUG = @@ -165,10 +166,12 @@ GeoDataLinearRing GeoPainterPrivate::createLinearRing= FromGeoRect( const GeoDataC = bool GeoPainterPrivate::doClip( const ViewportParams *viewport ) { - if ( viewport->projection() !=3D Spherical ) + if ( !viewport->currentProjection()->isClippedToSphere() ) return true; = - return ( viewport->radius() > viewport->width() / 2 || viewport->radiu= s() > viewport->height() / 2 ); + const qint64 radius =3D viewport->radius() * viewport->currentProject= ion()->clippingRadius(); + + return ( radius > viewport->width() / 2 || radius > viewport->height()= / 2 ); } = // -----------------------------------------------------------------------= -------------------------- diff --git a/src/lib/marble/GnomonicScanlineTextureMapper.cpp b/src/lib/mar= ble/GnomonicScanlineTextureMapper.cpp deleted file mode 100644 index 38f32f2..0000000 --- a/src/lib/marble/GnomonicScanlineTextureMapper.cpp +++ /dev/null @@ -1,201 +0,0 @@ -// -// 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 Carlos Licea -// Copyright 2008 Inge Wallin -// Copyright 2011 Bernhard Beschow -// - - -// local -#include "GnomonicScanlineTextureMapper.h" - -// Qt -#include -#include -#include - -// Marble -#include "GeoPainter.h" -#include "MarbleDirs.h" -#include "MarbleDebug.h" -#include "ScanlineTextureMapperContext.h" -#include "StackedTileLoader.h" -#include "TextureColorizer.h" -#include "ViewParams.h" -#include "ViewportParams.h" -#include "MathHelper.h" - -using namespace Marble; - -class GnomonicScanlineTextureMapper::RenderJob : public QRunnable -{ -public: - RenderJob( StackedTileLoader *tileLoader, int tileLevel, QImage *canva= sImage, const ViewportParams *viewport, MapQuality mapQuality, int yTop, in= t yBottom ); - - virtual void run(); - -private: - StackedTileLoader *const m_tileLoader; - const int m_tileLevel; - QImage *const m_canvasImage; - const ViewportParams *const m_viewport; - const MapQuality m_mapQuality; - const int m_yPaintedTop; - const int m_yPaintedBottom; -}; - -GnomonicScanlineTextureMapper::RenderJob::RenderJob( StackedTileLoader *ti= leLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewpor= t, MapQuality mapQuality, int yTop, int yBottom ) - : m_tileLoader( tileLoader ), - m_tileLevel( tileLevel ), - m_canvasImage( canvasImage ), - m_viewport( viewport ), - m_mapQuality( mapQuality ), - m_yPaintedTop( yTop ), - m_yPaintedBottom( yBottom ) -{ -} - - -GnomonicScanlineTextureMapper::GnomonicScanlineTextureMapper( StackedTileL= oader *tileLoader ) - : TextureMapperInterface() - , m_tileLoader( tileLoader ) - , m_radius( 0 ) -{ -} - - -void GnomonicScanlineTextureMapper::mapTexture( GeoPainter *painter, - const ViewportParams *view= port, - int tileZoomLevel, - const QRect &dirtyRect, - TextureColorizer *texColor= izer ) -{ - if ( m_canvasImage.size() !=3D viewport->size() || m_radius !=3D viewp= ort->radius() ) { - const QImage::Format optimalFormat =3D ScanlineTextureMapperContex= t::optimalCanvasImageFormat( viewport ); - - if ( m_canvasImage.size() !=3D viewport->size() || m_canvasImage.f= ormat() !=3D optimalFormat ) { - m_canvasImage =3D QImage( viewport->size(), optimalFormat ); - } - - if ( !viewport->mapCoversViewport() ) { - m_canvasImage.fill( 0 ); - } - - m_radius =3D viewport->radius(); - m_repaintNeeded =3D true; - } - - if ( m_repaintNeeded ) { - mapTexture( viewport, tileZoomLevel, painter->mapQuality() ); - - if ( texColorizer ) { - texColorizer->colorize( &m_canvasImage, viewport, painter->map= Quality() ); - } - - m_repaintNeeded =3D false; - } - - painter->drawImage( dirtyRect, m_canvasImage, dirtyRect ); -} - -void GnomonicScanlineTextureMapper::mapTexture( const ViewportParams *view= port, int tileZoomLevel, MapQuality mapQuality ) -{ - // Reset backend - m_tileLoader->resetTilehash(); - - const int imageHeight =3D viewport->height(); - - const int numThreads =3D m_threadPool.maxThreadCount(); - const int yStep =3D imageHeight / numThreads; - for ( int i =3D 0; i < numThreads; ++i ) { - const int yStart =3D i * yStep; - const int yEnd =3D (i + 1) * yStep; - QRunnable *const job =3D new RenderJob( m_tileLoader, tileZoomLeve= l, &m_canvasImage, viewport, mapQuality, yStart, yEnd ); - m_threadPool.start( job ); - } - - m_threadPool.waitForDone(); - - m_tileLoader->cleanupTilehash(); -} - -void GnomonicScanlineTextureMapper::RenderJob::run() -{ - const int imageWidth =3D m_canvasImage->width(); - const qint64 radius =3D m_viewport->radius(); - // Calculate how many degrees are being represented per pixel. - const qreal rad2Pixel =3D ( 2 * radius ) / M_PI; - const qreal pixel2Rad =3D 1.0/rad2Pixel; - - const bool interlaced =3D ( m_mapQuality =3D=3D LowQuality ); - const bool highQuality =3D ( m_mapQuality =3D=3D HighQuality - || m_mapQuality =3D=3D PrintQuality ); - const bool printQuality =3D ( m_mapQuality =3D=3D PrintQuality ); - - // Evaluate the degree of interpolation - const int n =3D ScanlineTextureMapperContext::interpolationStep( m_vie= wport, m_mapQuality ); - const int maxInterpolationPointX =3D n * (int)( imageWidth / n - 1 ) += 1; - - // initialize needed variables that are modified during texture mappin= g: - - ScanlineTextureMapperContext context( m_tileLoader, m_tileLevel ); - - - // Paint the map. - for ( int y =3D m_yPaintedTop; y < m_yPaintedBottom; ++y ) { - - QRgb * scanLine =3D (QRgb*)m_canvasImage->scanLine( y ); - - for ( int x =3D 0; x < imageWidth; ++x ) { - - // Prepare for interpolation - bool interpolate =3D false; - if ( x >=3D 1 && x <=3D maxInterpolationPointX ) { - x +=3D n - 1; - interpolate =3D !printQuality; - } - else { - interpolate =3D false; - } - - qreal lon; - qreal lat; - m_viewport->geoCoordinates(x,y, lon, lat, GeoDataCoordinates::= Radian); - - if ( interpolate ) { - if ( highQuality ) - context.pixelValueApproxF( lon, lat, scanLine, n ); - else - context.pixelValueApprox( lon, lat, scanLine, n ); - - scanLine +=3D ( n - 1 ); - } - - if ( x < imageWidth ) { - if ( highQuality ) - context.pixelValueF( lon, lat, scanLine ); - else - context.pixelValue( lon, lat, scanLine ); - } - - ++scanLine; - lon +=3D pixel2Rad; - } - - // copy scanline to improve performance - if ( interlaced && y + 1 < m_yPaintedBottom ) { - - const int pixelByteSize =3D m_canvasImage->bytesPerLine() / im= ageWidth; - - memcpy( m_canvasImage->scanLine( y + 1 ), - m_canvasImage->scanLine( y ), - imageWidth * pixelByteSize ); - ++y; - } - } -} diff --git a/src/lib/marble/MapViewWidget.cpp b/src/lib/marble/MapViewWidge= t.cpp index e076ea3..cc292cd 100644 --- a/src/lib/marble/MapViewWidget.cpp +++ b/src/lib/marble/MapViewWidget.cpp @@ -239,27 +239,13 @@ class MapViewWidget::Private { m_globeViewButton->setToolTip( tr("Globe View") ); m_globeViewButton->setCheckable(true); m_globeViewButton->setChecked(false); - m_globeViewButton->setPopupMode(QToolButton::MenuButtonPopup); - - m_popupMenuSpherical =3D new QMenu; = m_globeViewAction =3D new QAction( QIcon(":/icons/map-globe.png"), tr( "Spherical view" ), - m_popupMenuSpherical ); + m_globeViewButton ); m_globeViewAction->setCheckable( true ); m_globeViewAction->setChecked( false ); = - m_gnomonicViewAction =3D new QAction( QIcon(":/icons/map-gnomonic.= png"), - tr( "Gnomonic view" ), - m_popupMenuSpherical); - m_gnomonicViewAction->setCheckable( true ); - m_gnomonicViewAction->setChecked( false ); - - m_popupMenuSpherical->addAction( m_globeViewAction ); - m_popupMenuSpherical->addAction( m_gnomonicViewAction ); - - m_globeViewButton->setMenu( m_popupMenuSpherical ); - m_mercatorViewButton =3D new QToolButton; m_mercatorViewButton->setIcon( QIcon(":/icons/map-mercator.png") ); m_mercatorViewButton->setToolTip( tr("Mercator View") ); @@ -281,8 +267,15 @@ class MapViewWidget::Private { m_flatViewAction->setCheckable(true); m_flatViewAction->setChecked(false); = + m_gnomonicViewAction =3D new QAction( QIcon(":/icons/map-gnomonic.= png"), + tr( "Gnomonic view" ), + m_popupMenuFlat); + m_gnomonicViewAction->setCheckable( true ); + m_gnomonicViewAction->setChecked( false ); + m_popupMenuFlat->addAction(m_mercatorViewAction); m_popupMenuFlat->addAction(m_flatViewAction); + m_popupMenuFlat->addAction(m_gnomonicViewAction); m_mercatorViewButton->setMenu(m_popupMenuFlat); = m_toolBar->addWidget(m_globeViewButton); @@ -347,7 +340,6 @@ class MapViewWidget::Private { QToolButton *m_globeViewButton; QToolButton *m_mercatorViewButton; QMenu *m_popupMenuFlat; - QMenu *m_popupMenuSpherical; QAction *m_flatViewAction; QAction *m_mercatorViewAction; QAction *m_celestialBodyAction; diff --git a/src/lib/marble/MercatorScanlineTextureMapper.cpp b/src/lib/mar= ble/MercatorScanlineTextureMapper.cpp index 62348fb..1fe7d83 100644 --- a/src/lib/marble/MercatorScanlineTextureMapper.cpp +++ b/src/lib/marble/MercatorScanlineTextureMapper.cpp @@ -28,6 +28,7 @@ #include "TextureColorizer.h" #include "ViewportParams.h" #include "MathHelper.h" +#include "AbstractProjection.h" = using namespace Marble; = @@ -109,28 +110,22 @@ void MercatorScanlineTextureMapper::mapTexture( const= ViewportParams *viewport, // Initialize needed constants: = const int imageHeight =3D m_canvasImage.height(); - const qint64 radius =3D viewport->radius(); - // Calculate how many degrees are being represented per pixel. - const float rad2Pixel =3D (float)( 2 * radius ) / M_PI; - - //mDebug() << "m_maxGlobalX: " << m_maxGlobalX; - //mDebug() << "radius : " << radius << endl; - - // Calculate translation of center point - const qreal centerLat =3D viewport->centerLatitude(); - - const int yCenterOffset =3D (int)( asinh( tan( centerLat ) ) * rad2Pix= el ); = // Calculate y-range the represented by the center point, yTop and // what actually can be painted - const int yTop =3D imageHeight / 2 - 2 * radius + yCenterOffset; - int yPaintedTop =3D imageHeight / 2 - 2 * radius + yCenterOffset; - int yPaintedBottom =3D imageHeight / 2 + 2 * radius + yCenterOffset; + + qreal realYTop, realYBottom, dummyX; + GeoDataCoordinates yNorth(0, viewport->currentProjection()->maxLat(), = 0); + GeoDataCoordinates ySouth(0, viewport->currentProjection()->minLat(), = 0); + viewport->screenCoordinates(yNorth, dummyX, realYTop ); + viewport->screenCoordinates(ySouth, dummyX, realYBottom ); + + const int yTop =3D qBound(0.0, realYTop, (qreal)(imageHeight)); + int yPaintedTop =3D yTop; + int yPaintedBottom =3D qBound(0.0, realYBottom, (qreal)(imageHeight)); = - if (yPaintedTop < 0) yPaintedTop =3D 0; - if (yPaintedTop > imageHeight) yPaintedTop =3D imageHeight; - if (yPaintedBottom < 0) yPaintedBottom =3D 0; - if (yPaintedBottom > imageHeight) yPaintedBottom =3D imageHeight; + yPaintedTop =3D qBound(0, yPaintedTop, imageHeight); + yPaintedBottom =3D qBound(0, yPaintedBottom, imageHeight); = const int numThreads =3D m_threadPool.maxThreadCount(); const int yStep =3D ( yPaintedBottom - yPaintedTop ) / numThreads; diff --git a/src/lib/marble/TextureColorizer.cpp b/src/lib/marble/TextureCo= lorizer.cpp index 2d9f6f8..4c790ca 100644 --- a/src/lib/marble/TextureColorizer.cpp +++ b/src/lib/marble/TextureColorizer.cpp @@ -33,6 +33,7 @@ #include "GeoDataTypes.h" #include "GeoDataPlacemark.h" #include "GeoDataDocument.h" +#include "AbstractProjection.h" = namespace Marble { @@ -241,7 +242,7 @@ void TextureColorizer::colorize( QImage *origimg, const= ViewportParams *viewport = drawTextureMap( &painter ); = - const qint64 radius =3D viewport->radius(); + const qint64 radius =3D viewport->radius() * viewport->currentProjecti= on()->clippingRadius(); = const int imgheight =3D origimg->height(); const int imgwidth =3D origimg->width(); @@ -253,31 +254,20 @@ void TextureColorizer::colorize( QImage *origimg, con= st ViewportParams *viewport int bump =3D 8; = if ( radius * radius > imgradius - || viewport->projection() !=3D Spherical ) + || !viewport->currentProjection()->isClippedToSphere() ) { int yTop =3D 0; int yBottom =3D imgheight; = - if( viewport->projection() !=3D Spherical ) + if( !viewport->currentProjection()->isClippedToSphere() && !viewpo= rt->currentProjection()->traversablePoles() ) { - // Calculate translation of center point - const qreal centerLat =3D viewport->centerLatitude(); - - const float rad2Pixel =3D (qreal)( 2 * radius ) / M_PI; - if ( viewport->projection() =3D=3D Equirectangular ) { - int yCenterOffset =3D (int)( centerLat * rad2Pixel ); - yTop =3D ( imgry - radius + yCenterOffset < 0)? 0 : imgry = - radius + yCenterOffset; - yBottom =3D ( imgry + yCenterOffset + radius > imgheight )= ? imgheight : imgry + yCenterOffset + radius; - } - else if ( viewport->projection() =3D=3D Mercator ) { - int yCenterOffset =3D (int)( asinh( tan( centerLat ) ) * r= ad2Pixel ); - yTop =3D ( imgry - 2 * radius + yCenterOffset < 0 ) ? 0 : = imgry - 2 * radius + yCenterOffset; - yBottom =3D ( imgry + 2 * radius + yCenterOffset > imgheig= ht )? imgheight : imgry + 2 * radius + yCenterOffset; - } - else { - yTop =3D 0; - yBottom =3D imgheight; - } + qreal realYTop, realYBottom, dummyX; + GeoDataCoordinates yNorth(0, viewport->currentProjection()->ma= xLat(), 0); + GeoDataCoordinates ySouth(0, viewport->currentProjection()->mi= nLat(), 0); + viewport->screenCoordinates(yNorth, dummyX, realYTop ); + viewport->screenCoordinates(ySouth, dummyX, realYBottom ); + yTop =3D qBound(0.0, realYTop, (qreal)(imgheight)); + yBottom =3D qBound(0.0, realYBottom, (qreal)(imgheight)); } = const int itEnd =3D yBottom; diff --git a/src/lib/marble/layers/TextureLayer.cpp b/src/lib/marble/layers= /TextureLayer.cpp index e7ad718..777ce63 100644 --- a/src/lib/marble/layers/TextureLayer.cpp +++ b/src/lib/marble/layers/TextureLayer.cpp @@ -20,7 +20,7 @@ #include "SphericalScanlineTextureMapper.h" #include "EquirectScanlineTextureMapper.h" #include "MercatorScanlineTextureMapper.h" -#include "GnomonicScanlineTextureMapper.h" +#include "GenericScanlineTextureMapper.h" #include "TileScalingTextureMapper.h" #include "GeoDataGroundOverlay.h" #include "GeoPainter.h" @@ -399,7 +399,7 @@ void TextureLayer::setProjection( Projection projection= ) } break; case Gnomonic: - d->m_texmapper =3D new GnomonicScanlineTextureMapper( &d->m_ti= leLoader ); + d->m_texmapper =3D new GenericScanlineTextureMapper( &d->m_til= eLoader ); break; default: d->m_texmapper =3D 0; diff --git a/src/lib/marble/projections/AbstractProjection.cpp b/src/lib/ma= rble/projections/AbstractProjection.cpp index f5f7831..591adc0 100644 --- a/src/lib/marble/projections/AbstractProjection.cpp +++ b/src/lib/marble/projections/AbstractProjection.cpp @@ -115,6 +115,17 @@ bool AbstractProjection::isOrientedNormal() const return true; } = +bool AbstractProjection::isClippedToSphere() const +{ + return false; +} + +qreal AbstractProjection::clippingRadius() const +{ + return 0; +} + + bool AbstractProjection::screenCoordinates( const qreal lon, const qreal l= at, const ViewportParams *viewport, qreal &x, qreal &y ) const diff --git a/src/lib/marble/projections/AbstractProjection.h b/src/lib/marb= le/projections/AbstractProjection.h index 302c199..9b7dbb9 100644 --- a/src/lib/marble/projections/AbstractProjection.h +++ b/src/lib/marble/projections/AbstractProjection.h @@ -95,7 +95,11 @@ class MARBLE_EXPORT AbstractProjection // - transverse: orthogonally oriented compared to the Earth's axis // - oblique: somewhere in between = - virtual bool isOrientedNormal() const; + virtual bool isOrientedNormal() const; + + virtual bool isClippedToSphere() const; + + virtual qreal clippingRadius() const; = /** * @brief Get the screen coordinates corresponding to geographical coo= rdinates in the map. diff --git a/src/lib/marble/projections/AzimuthalProjection.cpp b/src/lib/m= arble/projections/AzimuthalProjection.cpp index ac141cc..ccb7171 100644 --- a/src/lib/marble/projections/AzimuthalProjection.cpp +++ b/src/lib/marble/projections/AzimuthalProjection.cpp @@ -31,6 +31,52 @@ qreal AzimuthalProjection::minValidLat() const return -90.0 * DEG2RAD; } = +bool AzimuthalProjection::isClippedToSphere() const +{ + return true; +} + +qreal AzimuthalProjection::clippingRadius() const +{ + return 1; +} + +bool AzimuthalProjection::screenCoordinates( const GeoDataLineString &line= String, + const ViewportParams *vi= ewport, + QVector &po= lygons ) const +{ + + Q_D( const AzimuthalProjection ); + // Compare bounding box size of the line string with the angularResolu= tion + // Immediately return if the latLonAltBox is smaller. + if ( !viewport->resolves( lineString.latLonAltBox() ) ) { +// mDebug() << "Object too small to be resolved"; + return false; + } + + d->lineStringToPolygon( lineString, viewport, polygons ); + return true; +} + +bool AzimuthalProjection::mapCoversViewport( const ViewportParams *viewpor= t ) const +{ + qint64 radius =3D viewport->radius() * viewport->currentProjectio= n()->clippingRadius(); + qint64 width =3D viewport->width(); + qint64 height =3D viewport->height(); + + // This first test is a quick one that will catch all really big + // radii and prevent overflow in the real test. + if ( radius > width + height ) + return true; + + // This is the real test. The 4 is because we are really + // comparing to width/2 and height/2. + if ( 4 * radius * radius >=3D width * width + height * height ) + return true; + + return false; +} + GeoDataLatLonAltBox AzimuthalProjection::latLonAltBox( const QRect& screen= Rect, const ViewportParam= s *viewport ) const { @@ -93,7 +139,7 @@ GeoDataLatLonAltBox AzimuthalProjection::latLonAltBox( c= onst QRect& screenRect, = QPainterPath AzimuthalProjection::mapShape( const ViewportParams *viewport= ) const { - int radius =3D viewport->radius(); + int radius =3D viewport->radius() * viewport->currentProjection()-= >clippingRadius(); int imgWidth =3D viewport->width(); int imgHeight =3D viewport->height(); = @@ -126,6 +172,482 @@ AzimuthalProjection::~AzimuthalProjection() { } = +void AzimuthalProjectionPrivate::tessellateLineSegment( const GeoDataCoord= inates &aCoords, + qreal ax, qreal ay, + const GeoDataCoordinates &= bCoords, + qreal bx, qreal by, + QVector &polyg= ons, + const ViewportParams *view= port, + TessellationFlags f) const +{ + // We take the manhattan length as a distance approximation + // that can be too big by a factor of sqrt(2) + qreal distance =3D fabs((bx - ax)) + fabs((by - ay)); +#ifdef SAFE_DISTANCE + // Interpolate additional nodes if the line segment that connects the + // current or previous nodes might cross the viewport. + // The latter can pretty safely be excluded for most projections if bo= th points + // are located on the same side relative to the viewport boundaries an= d if they are + // located more than half the line segment distance away from the view= port. + const qreal safeDistance =3D - 0.5 * distance; + if ( !( bx < safeDistance && ax < safeDistance ) + || !( by < safeDistance && ay < safeDistance ) + || !( bx + safeDistance > viewport->width() + && ax + safeDistance > viewport->width() ) + || !( by + safeDistance > viewport->height() + && ay + safeDistance > viewport->height() ) + ) + { +#endif + bool const smallScreen =3D MarbleGlobal::getInstance()->profiles()= & MarbleGlobal::SmallScreen; + int const finalTessellationPrecision =3D smallScreen ? 3 * tessell= ationPrecision : tessellationPrecision; + + // Let the line segment follow the spherical surface + // if the distance between the previous point and the current point + // on screen is too big + + if ( distance > finalTessellationPrecision ) { + const int tessellatedNodes =3D qMin( distance / finalTess= ellationPrecision, maxTessellationNodes ); + + processTessellation( aCoords, bCoords, + tessellatedNodes, + polygons, + viewport, + f ); + } + else { + crossHorizon( bCoords, polygons, viewport ); + } +#ifdef SAFE_DISTANCE + } +#endif +} + + +void AzimuthalProjectionPrivate::processTessellation( const GeoDataCoordin= ates &previousCoords, + const GeoDataCoordinat= es ¤tCoords, + int tessellatedNodes, + QVector &p= olygons, + const ViewportParams *= viewport, + TessellationFlags f) c= onst +{ + + const bool clampToGround =3D f.testFlag( FollowGround ); + const bool followLatitudeCircle =3D f.testFlag( RespectLatitudeCircle ) + && previousCoords.latitude() =3D=3D = currentCoords.latitude(); + + // Calculate steps for tessellation: lonDiff and altDiff + qreal lonDiff =3D 0.0; + if ( followLatitudeCircle ) { + const int previousSign =3D previousCoords.longitude() > 0 ? 1 : -1; + const int currentSign =3D currentCoords.longitude() > 0 ? 1 : -1; + + lonDiff =3D currentCoords.longitude() - previousCoords.longitude(); + if ( previousSign !=3D currentSign + && fabs(previousCoords.longitude()) + fabs(currentCoords.long= itude()) > M_PI ) { + if ( previousSign > currentSign ) { + // going eastwards -> + lonDiff +=3D 2 * M_PI ; + } else { + // going westwards -> + lonDiff -=3D 2 * M_PI; + } + } + } + + const qreal altDiff =3D currentCoords.altitude() - previousCoords.alti= tude(); + + // Create the tessellation nodes. + GeoDataCoordinates previousTessellatedCoords =3D previousCoords; + for ( int i =3D 1; i <=3D tessellatedNodes; ++i ) { + const qreal t =3D (qreal)(i) / (qreal)( tessellatedNodes + 1 ); + + // interpolate the altitude, too + const qreal altitude =3D clampToGround ? 0 : altDiff * t + previou= sCoords.altitude(); + + qreal lon =3D 0.0; + qreal lat =3D 0.0; + if ( followLatitudeCircle ) { + // To tessellate along latitude circles use the + // linear interpolation of the longitude. + lon =3D lonDiff * t + previousCoords.longitude(); + lat =3D previousTessellatedCoords.latitude(); + } + else { + // To tessellate along great circles use the + // normalized linear interpolation ("NLERP") for latitude and = longitude. + const Quaternion itpos =3D Quaternion::nlerp( previousCoords.q= uaternion(), currentCoords.quaternion(), t ); + itpos. getSpherical( lon, lat ); + } + + const GeoDataCoordinates currentTessellatedCoords( lon, lat, altit= ude ); + crossHorizon( currentTessellatedCoords, polygons, viewport ); + previousTessellatedCoords =3D currentTessellatedCoords; + } + + // For the clampToGround case add the "current" coordinate after addin= g all other nodes. + GeoDataCoordinates currentModifiedCoords( currentCoords ); + if ( clampToGround ) { + currentModifiedCoords.setAltitude( 0.0 ); + } + crossHorizon( currentModifiedCoords, polygons, viewport ); +} + +void AzimuthalProjectionPrivate::crossHorizon( const GeoDataCoordinates & = bCoord, + QVector &polygon= s, + const ViewportParams *viewpo= rt ) const +{ + qreal x, y; + bool globeHidesPoint; + + Q_Q( const AbstractProjection ); + + q->screenCoordinates( bCoord, viewport, x, y, globeHidesPoint ); + + if( !globeHidesPoint ) { + *polygons.last() << QPointF( x, y ); + } + else { + if ( !polygons.last()->isEmpty() ) { + QPolygonF *path =3D new QPolygonF; + polygons.append( path ); + } + } +} + +bool AzimuthalProjectionPrivate::lineStringToPolygon( const GeoDataLineStr= ing &lineString, + const ViewportParams *viewpo= rt, + QVector &polygo= ns ) const +{ + Q_Q( const AzimuthalProjection ); + + const TessellationFlags f =3D lineString.tessellationFlags(); + + qreal x =3D 0; + qreal y =3D 0; + bool globeHidesPoint =3D false; + + qreal previousX =3D -1.0; + qreal previousY =3D -1.0; + bool previousGlobeHidesPoint =3D false; + + qreal horizonX =3D -1.0; + qreal horizonY =3D -1.0; + + polygons.append( new QPolygonF ); + + GeoDataLineString::ConstIterator itCoords =3D lineString.constBegin(); + GeoDataLineString::ConstIterator itPreviousCoords =3D lineString.const= Begin(); + + // Some projections display the earth in a way so that there is a + // foreside and a backside. + // The horizon is the line (usually a circle) which separates both + // sides from each other and resembles the map shape. + GeoDataCoordinates horizonCoords; + + // A horizon pair is a pair of two subsequent horizon crossings: + // The first one describes the point where a line string disappears be= hind the horizon. + // and where horizonPair is set to true. + // The second one describes the point where the line string reappears. + // In this case the two points are connected and horizonPair is set to= false again. + bool horizonPair =3D false; + GeoDataCoordinates horizonDisappearCoords; + + // If the first horizon crossing in a line string describes the appear= ance of + // a line string then we call it a "horizon orphan" and horizonOrphan = is set to true. + // In this case once the last horizon crossing in the line string is r= eached + // it needs to be connected to the orphan. + bool horizonOrphan =3D false; + GeoDataCoordinates horizonOrphanCoords; + + GeoDataLineString::ConstIterator itBegin =3D lineString.constBegin(); + GeoDataLineString::ConstIterator itEnd =3D lineString.constEnd(); + + bool processingLastNode =3D false; + + // We use a while loop to be able to cover linestrings as well as line= ar rings: + // Linear rings require to tessellate the path from the last node to t= he first node + // which isn't really convenient to achieve with a for loop ... + + const bool isLong =3D lineString.size() > 50; + const int maximumDetail =3D ( viewport->radius() > 5000 ) ? 5 : + ( viewport->radius() > 2500 ) ? 4 : + ( viewport->radius() > 1000 ) ? 3 : + ( viewport->radius() > 600 ) ? 2 : + ( viewport->radius() > 50 ) ? 1 : + 0; + + while ( itCoords !=3D itEnd ) + { + + // Optimization for line strings with a big amount of nodes + bool skipNode =3D itCoords !=3D itBegin && isLong && !processingLa= stNode && + ( (*itCoords).detail() > maximumDetail + || viewport->resolves( *itPreviousCoords, *itCoords ) ); + + if ( !skipNode ) { + + q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoi= nt ); + + // Initializing variables that store the values of the previou= s iteration + if ( !processingLastNode && itCoords =3D=3D itBegin ) { + previousGlobeHidesPoint =3D globeHidesPoint; + itPreviousCoords =3D itCoords; + previousX =3D x; + previousY =3D y; + } + + // Check for the "horizon case" (which is present e.g. for the= spherical projection + const bool isAtHorizon =3D ( globeHidesPoint || previousGlobeH= idesPoint ) && + ( globeHidesPoint !=3D previousGlobe= HidesPoint ); + + if ( isAtHorizon ) { + // Handle the "horizon case" + horizonCoords =3D findHorizon( *itPreviousCoords, *itCoord= s, viewport, f ); + + if ( lineString.isClosed() ) { + if ( horizonPair ) { + horizonToPolygon( viewport, horizonDisappearCoords= , horizonCoords, polygons.last() ); + horizonPair =3D false; + } + else { + if ( globeHidesPoint ) { + horizonDisappearCoords =3D horizonCoords; + horizonPair =3D true; + } + else { + horizonOrphanCoords =3D horizonCoords; + horizonOrphan =3D true; + } + } + } + + q->screenCoordinates( horizonCoords, viewport, horizonX, h= orizonY ); + + // If the line appears on the visible half we need + // to add an interpolated point at the horizon as the prev= ious point. + if ( previousGlobeHidesPoint ) { + *polygons.last() << QPointF( horizonX, horizonY ); + } + } + + // This if-clause contains the section that tessellates the li= ne + // segments of a linestring. If you are about to learn how the= code of + // this class works you can safely ignore this section for a s= tart. + + if ( lineString.tessellate() /* && ( isVisible || previousIsVi= sible ) */ ) { + + if ( !isAtHorizon ) { + + tessellateLineSegment( *itPreviousCoords, previousX, p= reviousY, + *itCoords, x, y, + polygons, viewport, + f ); + + } + else { + // Connect the interpolated point at the horizon with= the + // current or previous point in the line. + if ( previousGlobeHidesPoint ) { + tessellateLineSegment( horizonCoords, horizonX, ho= rizonY, + *itCoords, x, y, + polygons, viewport, + f ); + } + else { + tessellateLineSegment( *itPreviousCoords, previous= X, previousY, + horizonCoords, horizonX, ho= rizonY, + polygons, viewport, + f ); + } + } + } + else { + if ( !globeHidesPoint ) { + *polygons.last() << QPointF( x, y ); + } + else { + if ( !previousGlobeHidesPoint && isAtHorizon ) { + *polygons.last() << QPointF( horizonX, horizonY ); + } + } + } + + if ( globeHidesPoint ) { + if ( !previousGlobeHidesPoint + && !lineString.isClosed() + ) { + polygons.append( new QPolygonF ); + } + } + + previousGlobeHidesPoint =3D globeHidesPoint; + itPreviousCoords =3D itCoords; + previousX =3D x; + previousY =3D y; + } + + // Here we modify the condition to be able to process the + // first node after the last node in a LinearRing. + + if ( processingLastNode ) { + break; + } + ++itCoords; + + if ( itCoords =3D=3D itEnd && lineString.isClosed() ) { + itCoords =3D itBegin; + processingLastNode =3D true; + } + } + + // In case of horizon crossings, make sure that we always get a + // polygon closed correctly. + if ( horizonOrphan && lineString.isClosed() ) { + horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, po= lygons.last() ); + } + + if ( polygons.last()->size() <=3D 1 ){ + polygons.pop_back(); // Clean up "unused" empty polygon instances + } + + return polygons.isEmpty(); +} + +void AzimuthalProjectionPrivate::horizonToPolygon( const ViewportParams *v= iewport, + const GeoDataCoordinates & disa= ppearCoords, + const GeoDataCoordinates & reap= pearCoords, + QPolygonF * polygon ) const +{ + qreal x, y; + + const qreal imageHalfWidth =3D viewport->width() / 2; + const qreal imageHalfHeight =3D viewport->height() / 2; + + bool dummyGlobeHidesPoint =3D false; + + Q_Q( const AzimuthalProjection ); + // Calculate the angle of the position vectors of both coordinates + q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHides= Point ); + qreal alpha =3D atan2( y - imageHalfHeight, + x - imageHalfWidth ); + + q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesP= oint ); + qreal beta =3D atan2( y - imageHalfHeight, + x - imageHalfWidth ); + + // Calculate the difference between both + qreal diff =3D GeoDataCoordinates::normalizeLon( beta - alpha ); + + qreal sgndiff =3D diff < 0 ? -1 : 1; + + const qreal arcradius =3D q->clippingRadius() * viewport->radius(); + const int itEnd =3D fabs(diff * RAD2DEG); + + // Create a polygon that resembles an arc between the two position vec= tors + for ( int it =3D 1; it <=3D itEnd; ++it ) { + const qreal angle =3D alpha + DEG2RAD * sgndiff * it; + const qreal itx =3D imageHalfWidth + arcradius * cos( angle ); + const qreal ity =3D imageHalfHeight + arcradius * sin( angle ); + *polygon << QPointF( itx, ity ); + } +} + + +GeoDataCoordinates AzimuthalProjectionPrivate::findHorizon( const GeoDataC= oordinates & previousCoords, + const GeoDataCoordinat= es & currentCoords, + const ViewportParams *= viewport, + TessellationFlags f, + int recursionCounter )= const +{ + bool currentHide =3D globeHidesPoint( currentCoords, viewport ) ; + + if ( recursionCounter > 20 ) { + return currentHide ? previousCoords : currentCoords; + } + ++recursionCounter; + + bool followLatitudeCircle =3D false; + + // Calculate steps for tessellation: lonDiff and altDiff + qreal lonDiff =3D 0.0; + qreal previousLongitude =3D 0.0; + qreal previousLatitude =3D 0.0; + + if ( f.testFlag( RespectLatitudeCircle ) ) { + previousCoords.geoCoordinates( previousLongitude, previousLatitude= ); + qreal previousSign =3D previousLongitude > 0 ? 1 : -1; + + qreal currentLongitude =3D 0.0; + qreal currentLatitude =3D 0.0; + currentCoords.geoCoordinates( currentLongitude, currentLatitude ); + qreal currentSign =3D currentLongitude > 0 ? 1 : -1; + + if ( previousLatitude =3D=3D currentLatitude ) { + followLatitudeCircle =3D true; + + lonDiff =3D currentLongitude - previousLongitude; + if ( previousSign !=3D currentSign + && fabs(previousLongitude) + fabs(currentLongitude) > M_P= I ) { + if ( previousSign > currentSign ) { + // going eastwards -> + lonDiff +=3D 2 * M_PI ; + } else { + // going westwards -> + lonDiff -=3D 2 * M_PI; + } + } + + } + else { +// mDebug() << "Don't FollowLatitudeCircle"; + } + } + + qreal lon =3D 0.0; + qreal lat =3D 0.0; + + qreal altDiff =3D currentCoords.altitude() - previousCoords.altitude(); + + if ( followLatitudeCircle ) { + // To tessellate along latitude circles use the + // linear interpolation of the longitude. + lon =3D lonDiff * 0.5 + previousLongitude; + lat =3D previousLatitude; + } + else { + // To tessellate along great circles use the + // normalized linear interpolation ("NLERP") for latitude and long= itude. + const Quaternion itpos =3D Quaternion::nlerp( previousCoords.quate= rnion(), currentCoords.quaternion(), 0.5 ); + itpos. getSpherical( lon, lat ); + } + + qreal altitude =3D previousCoords.altitude() + 0.5 * altDiff; + + GeoDataCoordinates horizonCoords( lon, lat, altitude ); + + bool horizonHide =3D globeHidesPoint( horizonCoords, viewport ); + + if ( horizonHide !=3D currentHide ) { + return findHorizon( horizonCoords, currentCoords, viewport, f, rec= ursionCounter ); + } + + return findHorizon( previousCoords, horizonCoords, viewport, f, recurs= ionCounter ); +} + + +bool AzimuthalProjectionPrivate::globeHidesPoint( const GeoDataCoordinates= &coordinates, + const ViewportParams *viewport )= const +{ + bool globeHidesPoint; + qreal dummyX, dummyY; + + Q_Q( const AzimuthalProjection ); + q->screenCoordinates(coordinates, viewport, dummyX, dummyY, globeHides= Point); + return globeHidesPoint; +} + + } = = diff --git a/src/lib/marble/projections/AzimuthalProjection.h b/src/lib/mar= ble/projections/AzimuthalProjection.h index cfb5709..b5d1c8a 100644 --- a/src/lib/marble/projections/AzimuthalProjection.h +++ b/src/lib/marble/projections/AzimuthalProjection.h @@ -47,6 +47,16 @@ class AzimuthalProjection : public AbstractProjection = virtual PreservationType preservationType() const { return NoPreservat= ion; } = + virtual bool isClippedToSphere() const; + + virtual qreal clippingRadius() const; + + bool mapCoversViewport( const ViewportParams *viewport ) const; + + virtual bool screenCoordinates( const GeoDataLineString &lineString, + const ViewportParams *viewport, + QVector &polygons ) const; + using AbstractProjection::screenCoordinates; = virtual QPainterPath mapShape( const ViewportParams *viewport ) const; diff --git a/src/lib/marble/projections/AzimuthalProjection_p.h b/src/lib/m= arble/projections/AzimuthalProjection_p.h index f22e3c6..20bc049 100644 --- a/src/lib/marble/projections/AzimuthalProjection_p.h +++ b/src/lib/marble/projections/AzimuthalProjection_p.h @@ -34,6 +34,49 @@ public: = virtual ~AzimuthalProjectionPrivate() {}; = + // This method tessellates a line segment in a way that the line segme= nt + // follows great circles. The count parameter specifies the + // number of nodes generated for the polygon. If the + // clampToGround flag is added the polygon contains count + 2 + // nodes as the clamped down start and end node get added. + + void tessellateLineSegment( const GeoDataCoordinates &aCoords, + qreal ax, qreal ay, + const GeoDataCoordinates &bCoords, + qreal bx, qreal by, + QVector &polygons, + const ViewportParams *viewport, + TessellationFlags f =3D 0 ) const; + + void processTessellation( const GeoDataCoordinates &previousCoords, + const GeoDataCoordinates ¤tCoords, + int count, + QVector &polygons, + const ViewportParams *viewport, + TessellationFlags f =3D 0 ) const; + + void crossHorizon( const GeoDataCoordinates & bCoord, + QVector &polygons, + const ViewportParams *viewport ) const; + + virtual bool lineStringToPolygon( const GeoDataLineString &lineString, + const ViewportParams *viewport, + QVector &polygons ) const; + + void horizonToPolygon( const ViewportParams *viewport, + const GeoDataCoordinates & disappearCoords, + const GeoDataCoordinates & reappearCoords, + QPolygonF* ) const; + + GeoDataCoordinates findHorizon( const GeoDataCoordinates & previousCoo= rds, + const GeoDataCoordinates & currentCoor= ds, + const ViewportParams *viewport, + TessellationFlags f =3D 0, + int recursionCounter =3D 0 ) const; + + bool globeHidesPoint( const GeoDataCoordinates &coordinates, + const ViewportParams *viewport ) const; + AzimuthalProjection * const q_ptr; = Q_DECLARE_PUBLIC( AzimuthalProjection ) diff --git a/src/lib/marble/projections/GnomonicProjection.cpp b/src/lib/ma= rble/projections/GnomonicProjection.cpp index 66bdfc7..1413e50 100644 --- a/src/lib/marble/projections/GnomonicProjection.cpp +++ b/src/lib/marble/projections/GnomonicProjection.cpp @@ -34,49 +34,6 @@ class GnomonicProjectionPrivate : public AzimuthalProjec= tionPrivate public: explicit GnomonicProjectionPrivate( GnomonicProjection * parent ); = - // This method tessellates a line segment in a way that the line segme= nt - // follows great circles. The count parameter specifies the - // number of nodes generated for the polygon. If the - // clampToGround flag is added the polygon contains count + 2 - // nodes as the clamped down start and end node get added. - - void tessellateLineSegment( const GeoDataCoordinates &aCoords, - qreal ax, qreal ay, - const GeoDataCoordinates &bCoords, - qreal bx, qreal by, - QVector &polygons, - const ViewportParams *viewport, - TessellationFlags f =3D 0 ) const; - - void processTessellation( const GeoDataCoordinates &previousCoords, - const GeoDataCoordinates ¤tCoords, - int count, - QVector &polygons, - const ViewportParams *viewport, - TessellationFlags f =3D 0 ) const; - - void crossHorizon( const GeoDataCoordinates & bCoord, - QVector &polygons, - const ViewportParams *viewport ) const; - - virtual bool lineStringToPolygon( const GeoDataLineString &lineString, - const ViewportParams *viewport, - QVector &polygons ) const; - - void horizonToPolygon( const ViewportParams *viewport, - const GeoDataCoordinates & disappearCoords, - const GeoDataCoordinates & reappearCoords, - QPolygonF* ) const; - - GeoDataCoordinates findHorizon( const GeoDataCoordinates & previousCoo= rds, - const GeoDataCoordinates & currentCoor= ds, - const ViewportParams *viewport, - TessellationFlags f =3D 0, - int recursionCounter =3D 0 ) const; - - bool globeHidesPoint( const GeoDataCoordinates &coordinates, - const ViewportParams *viewport ) const; - Q_DECLARE_PUBLIC( GnomonicProjection ) }; = @@ -102,7 +59,11 @@ GnomonicProjection::~GnomonicProjection() GnomonicProjectionPrivate::GnomonicProjectionPrivate( GnomonicProjection *= parent ) : AzimuthalProjectionPrivate( parent ) { +} = +qreal GnomonicProjection::clippingRadius() const +{ + return 3; } = bool GnomonicProjection::screenCoordinates( const GeoDataCoordinates &coor= dinates, @@ -126,10 +87,9 @@ bool GnomonicProjection::screenCoordinates( const GeoDa= taCoordinates &coordinate x *=3D 2 * viewport->radius() / M_PI; y *=3D 2 * viewport->radius() / M_PI; = - const qint64 radius =3D viewport->radius(); + const qint64 radius =3D clippingRadius() * viewport->radius(); = - // Introduce arteficial 10*radius horizon to avoid projection artefacts - if (x*x + y*y > 100 * radius * radius) { + if (x*x + y*y > radius * radius) { globeHidesPoint =3D true; return false; } @@ -185,7 +145,7 @@ bool GnomonicProjection::geoCoordinates( const int x, c= onst int y, const qreal centerLat =3D viewport->centerLatitude(); const qreal rx =3D ( - viewport->width() / 2 + x ) / rad2Pixel; const qreal ry =3D ( viewport->height() / 2 - y ) / rad2Pixel; - const qreal p =3D qSqrt( rx*rx + ry*ry ); + const qreal p =3D qMax( qSqrt( rx*rx + ry*ry ), 0.0001 ); // ensure we= don't divide by zero const qreal c =3D qAtan( p ); const qreal sinc =3D qSin(c); = @@ -204,518 +164,4 @@ bool GnomonicProjection::geoCoordinates( const int x,= const int y, return true; } = -bool GnomonicProjection::mapCoversViewport( const ViewportParams *viewport= ) const -{ - return true; -} - -bool GnomonicProjection::screenCoordinates( const GeoDataLineString &lineS= tring, - const ViewportParams *vi= ewport, - QVector &po= lygons ) const -{ - - Q_D( const GnomonicProjection ); - // Compare bounding box size of the line string with the angularResolu= tion - // Immediately return if the latLonAltBox is smaller. - if ( !viewport->resolves( lineString.latLonAltBox() ) ) { -// mDebug() << "Object too small to be resolved"; - return false; - } - - d->lineStringToPolygon( lineString, viewport, polygons ); - return true; -} - -void GnomonicProjectionPrivate::tessellateLineSegment( const GeoDataCoordi= nates &aCoords, - qreal ax, qreal ay, - const GeoDataCoordinates &= bCoords, - qreal bx, qreal by, - QVector &polyg= ons, - const ViewportParams *view= port, - TessellationFlags f) const -{ - // We take the manhattan length as a distance approximation - // that can be too big by a factor of sqrt(2) - qreal distance =3D fabs((bx - ax)) + fabs((by - ay)); -#ifdef SAFE_DISTANCE - // Interpolate additional nodes if the line segment that connects the - // current or previous nodes might cross the viewport. - // The latter can pretty safely be excluded for most projections if bo= th points - // are located on the same side relative to the viewport boundaries an= d if they are - // located more than half the line segment distance away from the view= port. - const qreal safeDistance =3D - 0.5 * distance; - if ( !( bx < safeDistance && ax < safeDistance ) - || !( by < safeDistance && ay < safeDistance ) - || !( bx + safeDistance > viewport->width() - && ax + safeDistance > viewport->width() ) - || !( by + safeDistance > viewport->height() - && ay + safeDistance > viewport->height() ) - ) - { -#endif - bool const smallScreen =3D MarbleGlobal::getInstance()->profiles()= & MarbleGlobal::SmallScreen; - int const finalTessellationPrecision =3D smallScreen ? 3 * tessell= ationPrecision : tessellationPrecision; - - // Let the line segment follow the spherical surface - // if the distance between the previous point and the current point - // on screen is too big - - if ( distance > finalTessellationPrecision ) { - const int tessellatedNodes =3D qMin( distance / finalTess= ellationPrecision, maxTessellationNodes ); - - processTessellation( aCoords, bCoords, - tessellatedNodes, - polygons, - viewport, - f ); - } - else { - crossHorizon( bCoords, polygons, viewport ); - } -#ifdef SAFE_DISTANCE - } -#endif -} - - -void GnomonicProjectionPrivate::processTessellation( const GeoDataCoordina= tes &previousCoords, - const GeoDataCoordinat= es ¤tCoords, - int tessellatedNodes, - QVector &p= olygons, - const ViewportParams *= viewport, - TessellationFlags f) c= onst -{ - - const bool clampToGround =3D f.testFlag( FollowGround ); - const bool followLatitudeCircle =3D f.testFlag( RespectLatitudeCircle ) - && previousCoords.latitude() =3D=3D = currentCoords.latitude(); - - // Calculate steps for tessellation: lonDiff and altDiff - qreal lonDiff =3D 0.0; - if ( followLatitudeCircle ) { - const int previousSign =3D previousCoords.longitude() > 0 ? 1 : -1; - const int currentSign =3D currentCoords.longitude() > 0 ? 1 : -1; - - lonDiff =3D currentCoords.longitude() - previousCoords.longitude(); - if ( previousSign !=3D currentSign - && fabs(previousCoords.longitude()) + fabs(currentCoords.long= itude()) > M_PI ) { - if ( previousSign > currentSign ) { - // going eastwards -> - lonDiff +=3D 2 * M_PI ; - } else { - // going westwards -> - lonDiff -=3D 2 * M_PI; - } - } - } - - const qreal altDiff =3D currentCoords.altitude() - previousCoords.alti= tude(); - - // Create the tessellation nodes. - GeoDataCoordinates previousTessellatedCoords =3D previousCoords; - for ( int i =3D 1; i <=3D tessellatedNodes; ++i ) { - const qreal t =3D (qreal)(i) / (qreal)( tessellatedNodes + 1 ); - - // interpolate the altitude, too - const qreal altitude =3D clampToGround ? 0 : altDiff * t + previou= sCoords.altitude(); - - qreal lon =3D 0.0; - qreal lat =3D 0.0; - if ( followLatitudeCircle ) { - // To tessellate along latitude circles use the - // linear interpolation of the longitude. - lon =3D lonDiff * t + previousCoords.longitude(); - lat =3D previousTessellatedCoords.latitude(); - } - else { - // To tessellate along great circles use the - // normalized linear interpolation ("NLERP") for latitude and = longitude. - const Quaternion itpos =3D Quaternion::nlerp( previousCoords.q= uaternion(), currentCoords.quaternion(), t ); - itpos. getSpherical( lon, lat ); - } - - const GeoDataCoordinates currentTessellatedCoords( lon, lat, altit= ude ); - crossHorizon( currentTessellatedCoords, polygons, viewport ); - previousTessellatedCoords =3D currentTessellatedCoords; - } - - // For the clampToGround case add the "current" coordinate after addin= g all other nodes. - GeoDataCoordinates currentModifiedCoords( currentCoords ); - if ( clampToGround ) { - currentModifiedCoords.setAltitude( 0.0 ); - } - crossHorizon( currentModifiedCoords, polygons, viewport ); -} - -void GnomonicProjectionPrivate::crossHorizon( const GeoDataCoordinates & b= Coord, - QVector &polygon= s, - const ViewportParams *viewpo= rt ) const -{ - qreal x, y; - bool globeHidesPoint; - - Q_Q( const AbstractProjection ); - - q->screenCoordinates( bCoord, viewport, x, y, globeHidesPoint ); - - if( !globeHidesPoint ) { - *polygons.last() << QPointF( x, y ); - } - else { - if ( !polygons.last()->isEmpty() ) { - QPolygonF *path =3D new QPolygonF; - polygons.append( path ); - } - } -} - -bool GnomonicProjectionPrivate::lineStringToPolygon( const GeoDataLineStri= ng &lineString, - const ViewportParams *viewpo= rt, - QVector &polygo= ns ) const -{ - Q_Q( const GnomonicProjection ); - - const TessellationFlags f =3D lineString.tessellationFlags(); - - qreal x =3D 0; - qreal y =3D 0; - bool globeHidesPoint =3D false; - - qreal previousX =3D -1.0; - qreal previousY =3D -1.0; - bool previousGlobeHidesPoint =3D false; - - qreal horizonX =3D -1.0; - qreal horizonY =3D -1.0; - - polygons.append( new QPolygonF ); - - GeoDataLineString::ConstIterator itCoords =3D lineString.constBegin(); - GeoDataLineString::ConstIterator itPreviousCoords =3D lineString.const= Begin(); - - // Some projections display the earth in a way so that there is a - // foreside and a backside. - // The horizon is the line (usually a circle) which separates both - // sides from each other and resembles the map shape. - GeoDataCoordinates horizonCoords; - - // A horizon pair is a pair of two subsequent horizon crossings: - // The first one describes the point where a line string disappears be= hind the horizon. - // and where horizonPair is set to true. - // The second one describes the point where the line string reappears. - // In this case the two points are connected and horizonPair is set to= false again. - bool horizonPair =3D false; - GeoDataCoordinates horizonDisappearCoords; - - // If the first horizon crossing in a line string describes the appear= ance of - // a line string then we call it a "horizon orphan" and horizonOrphan = is set to true. - // In this case once the last horizon crossing in the line string is r= eached - // it needs to be connected to the orphan. - bool horizonOrphan =3D false; - GeoDataCoordinates horizonOrphanCoords; - - GeoDataLineString::ConstIterator itBegin =3D lineString.constBegin(); - GeoDataLineString::ConstIterator itEnd =3D lineString.constEnd(); - - bool processingLastNode =3D false; - - // We use a while loop to be able to cover linestrings as well as line= ar rings: - // Linear rings require to tessellate the path from the last node to t= he first node - // which isn't really convenient to achieve with a for loop ... - - const bool isLong =3D lineString.size() > 50; - const int maximumDetail =3D ( viewport->radius() > 5000 ) ? 5 : - ( viewport->radius() > 2500 ) ? 4 : - ( viewport->radius() > 1000 ) ? 3 : - ( viewport->radius() > 600 ) ? 2 : - ( viewport->radius() > 50 ) ? 1 : - 0; - - while ( itCoords !=3D itEnd ) - { - - // Optimization for line strings with a big amount of nodes - bool skipNode =3D itCoords !=3D itBegin && isLong && !processingLa= stNode && - ( (*itCoords).detail() > maximumDetail - || viewport->resolves( *itPreviousCoords, *itCoords ) ); - - if ( !skipNode ) { - - q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoi= nt ); - - // Initializing variables that store the values of the previou= s iteration - if ( !processingLastNode && itCoords =3D=3D itBegin ) { - previousGlobeHidesPoint =3D globeHidesPoint; - itPreviousCoords =3D itCoords; - previousX =3D x; - previousY =3D y; - } - - // Check for the "horizon case" (which is present e.g. for the= spherical projection - const bool isAtHorizon =3D ( globeHidesPoint || previousGlobeH= idesPoint ) && - ( globeHidesPoint !=3D previousGlobe= HidesPoint ); - - if ( isAtHorizon ) { - // Handle the "horizon case" - horizonCoords =3D findHorizon( *itPreviousCoords, *itCoord= s, viewport, f ); - - if ( lineString.isClosed() ) { - if ( horizonPair ) { - horizonToPolygon( viewport, horizonDisappearCoords= , horizonCoords, polygons.last() ); - horizonPair =3D false; - } - else { - if ( globeHidesPoint ) { - horizonDisappearCoords =3D horizonCoords; - horizonPair =3D true; - } - else { - horizonOrphanCoords =3D horizonCoords; - horizonOrphan =3D true; - } - } - } - - q->screenCoordinates( horizonCoords, viewport, horizonX, h= orizonY ); - - // If the line appears on the visible half we need - // to add an interpolated point at the horizon as the prev= ious point. - if ( previousGlobeHidesPoint ) { - *polygons.last() << QPointF( horizonX, horizonY ); - } - } - - // This if-clause contains the section that tessellates the li= ne - // segments of a linestring. If you are about to learn how the= code of - // this class works you can safely ignore this section for a s= tart. - - if ( lineString.tessellate() /* && ( isVisible || previousIsVi= sible ) */ ) { - - if ( !isAtHorizon ) { - - tessellateLineSegment( *itPreviousCoords, previousX, p= reviousY, - *itCoords, x, y, - polygons, viewport, - f ); - - } - else { - // Connect the interpolated point at the horizon with= the - // current or previous point in the line. - if ( previousGlobeHidesPoint ) { - tessellateLineSegment( horizonCoords, horizonX, ho= rizonY, - *itCoords, x, y, - polygons, viewport, - f ); - } - else { - tessellateLineSegment( *itPreviousCoords, previous= X, previousY, - horizonCoords, horizonX, ho= rizonY, - polygons, viewport, - f ); - } - } - } - else { - if ( !globeHidesPoint ) { - *polygons.last() << QPointF( x, y ); - } - else { - if ( !previousGlobeHidesPoint && isAtHorizon ) { - *polygons.last() << QPointF( horizonX, horizonY ); - } - } - } - - if ( globeHidesPoint ) { - if ( !previousGlobeHidesPoint - && !lineString.isClosed() - ) { - polygons.append( new QPolygonF ); - } - } - - previousGlobeHidesPoint =3D globeHidesPoint; - itPreviousCoords =3D itCoords; - previousX =3D x; - previousY =3D y; - } - - // Here we modify the condition to be able to process the - // first node after the last node in a LinearRing. - - if ( processingLastNode ) { - break; - } - ++itCoords; - - if ( itCoords =3D=3D itEnd && lineString.isClosed() ) { - itCoords =3D itBegin; - processingLastNode =3D true; - } - } - - // In case of horizon crossings, make sure that we always get a - // polygon closed correctly. - if ( horizonOrphan && lineString.isClosed() ) { - horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, po= lygons.last() ); - } - - if ( polygons.last()->size() <=3D 1 ){ - polygons.pop_back(); // Clean up "unused" empty polygon instances - } - - return polygons.isEmpty(); -} - -void GnomonicProjectionPrivate::horizonToPolygon( const ViewportParams *vi= ewport, - const GeoDataCoordinates & disa= ppearCoords, - const GeoDataCoordinates & reap= pearCoords, - QPolygonF * polygon ) const -{ - qreal x, y; - - const qreal imageHalfWidth =3D viewport->width() / 2; - const qreal imageHalfHeight =3D viewport->height() / 2; - - bool dummyGlobeHidesPoint =3D false; - - Q_Q( const GnomonicProjection ); - // Calculate the angle of the position vectors of both coordinates - q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHides= Point ); - qreal alpha =3D atan2( y - imageHalfHeight, - x - imageHalfWidth ); - - q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesP= oint ); - qreal beta =3D atan2( y - imageHalfHeight, - x - imageHalfWidth ); - - // Calculate the difference between both - qreal diff =3D GeoDataCoordinates::normalizeLon( beta - alpha ); - - qreal sgndiff =3D diff < 0 ? -1 : 1; - - const qreal arcradius =3D 10 * viewport->radius(); - const int itEnd =3D fabs(diff * RAD2DEG); - - // Create a polygon that resembles an arc between the two position vec= tors - for ( int it =3D 1; it <=3D itEnd; ++it ) { - const qreal angle =3D alpha + DEG2RAD * sgndiff * it; - const qreal itx =3D imageHalfWidth + arcradius * cos( angle ); - const qreal ity =3D imageHalfHeight + arcradius * sin( angle ); - *polygon << QPointF( itx, ity ); - } -} - - -GeoDataCoordinates GnomonicProjectionPrivate::findHorizon( const GeoDataCo= ordinates & previousCoords, - const GeoDataCoordinat= es & currentCoords, - const ViewportParams *= viewport, - TessellationFlags f, - int recursionCounter )= const -{ - bool currentHide =3D globeHidesPoint( currentCoords, viewport ) ; - - if ( recursionCounter > 20 ) { - return currentHide ? previousCoords : currentCoords; - } - ++recursionCounter; - - bool followLatitudeCircle =3D false; - - // Calculate steps for tessellation: lonDiff and altDiff - qreal lonDiff =3D 0.0; - qreal previousLongitude =3D 0.0; - qreal previousLatitude =3D 0.0; - - if ( f.testFlag( RespectLatitudeCircle ) ) { - previousCoords.geoCoordinates( previousLongitude, previousLatitude= ); - qreal previousSign =3D previousLongitude > 0 ? 1 : -1; - - qreal currentLongitude =3D 0.0; - qreal currentLatitude =3D 0.0; - currentCoords.geoCoordinates( currentLongitude, currentLatitude ); - qreal currentSign =3D currentLongitude > 0 ? 1 : -1; - - if ( previousLatitude =3D=3D currentLatitude ) { - followLatitudeCircle =3D true; - - lonDiff =3D currentLongitude - previousLongitude; - if ( previousSign !=3D currentSign - && fabs(previousLongitude) + fabs(currentLongitude) > M_P= I ) { - if ( previousSign > currentSign ) { - // going eastwards -> - lonDiff +=3D 2 * M_PI ; - } else { - // going westwards -> - lonDiff -=3D 2 * M_PI; - } - } - - } - else { -// mDebug() << "Don't FollowLatitudeCircle"; - } - } - - qreal lon =3D 0.0; - qreal lat =3D 0.0; - - qreal altDiff =3D currentCoords.altitude() - previousCoords.altitude(); - - if ( followLatitudeCircle ) { - // To tessellate along latitude circles use the - // linear interpolation of the longitude. - lon =3D lonDiff * 0.5 + previousLongitude; - lat =3D previousLatitude; - } - else { - // To tessellate along great circles use the - // normalized linear interpolation ("NLERP") for latitude and long= itude. - const Quaternion itpos =3D Quaternion::nlerp( previousCoords.quate= rnion(), currentCoords.quaternion(), 0.5 ); - itpos. getSpherical( lon, lat ); - } - - qreal altitude =3D previousCoords.altitude() + 0.5 * altDiff; - - GeoDataCoordinates horizonCoords( lon, lat, altitude ); - - bool horizonHide =3D globeHidesPoint( horizonCoords, viewport ); - - if ( horizonHide !=3D currentHide ) { - return findHorizon( horizonCoords, currentCoords, viewport, f, rec= ursionCounter ); - } - - return findHorizon( previousCoords, horizonCoords, viewport, f, recurs= ionCounter ); -} - - -bool GnomonicProjectionPrivate::globeHidesPoint( const GeoDataCoordinates = &coordinates, - const ViewportParams *viewport )= const -{ - bool globeHidesPoint; - qreal dummyX, dummyY; - - Q_Q( const GnomonicProjection ); - q->screenCoordinates(coordinates, viewport, dummyX, dummyY, globeHides= Point); - return globeHidesPoint; -} - -QPainterPath GnomonicProjection::mapShape( const ViewportParams *viewport = ) const -{ - // Convenience variables - int width =3D viewport->width(); - int height =3D viewport->height(); - - // Cover the whole screen - QPainterPath mapShape; - mapShape.addRect( - 0, - 0, - width, - height ); - - return mapShape; -} - } diff --git a/src/lib/marble/projections/GnomonicProjection.h b/src/lib/marb= le/projections/GnomonicProjection.h index 58768d6..c992362 100644 --- a/src/lib/marble/projections/GnomonicProjection.h +++ b/src/lib/marble/projections/GnomonicProjection.h @@ -37,6 +37,8 @@ class GnomonicProjection : public AzimuthalProjection = virtual ~GnomonicProjection(); = + virtual qreal clippingRadius() const; + /** * @brief Get the screen coordinates corresponding to geographical coo= rdinates in the map. * @param lon the lon coordinate of the requested pixel position @@ -56,10 +58,6 @@ class GnomonicProjection : public AzimuthalProjection const QSizeF& size, bool &globeHidesPoint ) const; = - virtual bool screenCoordinates( const GeoDataLineString &lineString, - const ViewportParams *viewport, - QVector &polygons ) const; - using AbstractProjection::screenCoordinates; = /** @@ -76,10 +74,6 @@ class GnomonicProjection : public AzimuthalProjection qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit =3D GeoDataCoordina= tes::Degree ) const; = - bool mapCoversViewport( const ViewportParams *viewport ) const; - - virtual QPainterPath mapShape( const ViewportParams *viewport ) const; - protected: GnomonicProjection(GnomonicProjectionPrivate *dd ); = diff --git a/src/lib/marble/projections/SphericalProjection.cpp b/src/lib/m= arble/projections/SphericalProjection.cpp index d523142..eda4706 100644 --- a/src/lib/marble/projections/SphericalProjection.cpp +++ b/src/lib/marble/projections/SphericalProjection.cpp @@ -6,7 +6,7 @@ // the source code. // // Copyright 2007 Inge Wallin -// Copyright 2007-2012 Torsten Rahn +// Copyright 2007-2014 Torsten Rahn // Copyright 2009 Patrick Spendrin // Copyright 2012 Cezar Mocan // @@ -33,59 +33,9 @@ namespace Marble class SphericalProjectionPrivate : public AzimuthalProjectionPrivate { public: - enum LineStringFlag { - PlainLineString =3D 0x00, - ClosedLineString =3D 0x01 - }; = explicit SphericalProjectionPrivate( SphericalProjection * parent ); = - // This method tessellates a line segment in a way that the line segme= nt - // follows great circles. The count parameter specifies the - // number of nodes generated for the polygon. If the - // clampToGround flag is added the polygon contains count + 2 - // nodes as the clamped down start and end node get added. - - void tessellateLineSegment( const GeoDataCoordinates &aCoords, - qreal ax, qreal ay, - const GeoDataCoordinates &bCoords, - qreal bx, qreal by, - LineStringFlag lineStringFlag, - QVector &polygons, - const ViewportParams *viewport, - TessellationFlags f =3D 0 ) const; - - void processTessellation( const GeoDataCoordinates &previousCoords, - const GeoDataCoordinates ¤tCoords, - int count, - LineStringFlag lineStringFlag, - QVector &polygons, - const ViewportParams *viewport, - TessellationFlags f =3D 0 ) const; - - void crossHorizon( const GeoDataCoordinates & bCoord, - LineStringFlag lineStringFlag, - QVector &polygons, - const ViewportParams *viewport ) const; - - virtual bool lineStringToPolygon( const GeoDataLineString &lineString, - const ViewportParams *viewport, - QVector &polygons ) const; - - void horizonToPolygon( const ViewportParams *viewport, - const GeoDataCoordinates & disappearCoords, - const GeoDataCoordinates & reappearCoords, - QPolygonF* ) const; - - GeoDataCoordinates findHorizon( const GeoDataCoordinates & previousCoo= rds, - const GeoDataCoordinates & currentCoor= ds, - const ViewportParams *viewport, - TessellationFlags f =3D 0, - int recursionCounter =3D 0 ) const; - - static bool globeHidesPoint( const GeoDataCoordinates &coordinates, - const ViewportParams *viewport ); - Q_DECLARE_PUBLIC( SphericalProjection ) }; = @@ -107,11 +57,9 @@ SphericalProjection::~SphericalProjection() { } = - SphericalProjectionPrivate::SphericalProjectionPrivate( SphericalProjectio= n * parent ) : AzimuthalProjectionPrivate( parent ) { - } = bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coo= rdinates, = @@ -214,541 +162,4 @@ bool SphericalProjection::geoCoordinates( const int x= , const int y, return true; } = -bool SphericalProjection::mapCoversViewport( const ViewportParams *viewpor= t ) const -{ - qint64 radius =3D viewport->radius(); - qint64 width =3D viewport->width(); - qint64 height =3D viewport->height(); - - // This first test is a quick one that will catch all really big - // radii and prevent overflow in the real test. - if ( radius > width + height ) - return true; - - // This is the real test. The 4 is because we are really - // comparing to width/2 and height/2. - if ( 4 * radius * radius >=3D width * width + height * height ) - return true; - - return false; -} - -bool SphericalProjection::screenCoordinates( const GeoDataLineString &line= String, - const ViewportParams *vi= ewport, - QVector &po= lygons ) const -{ - - Q_D( const SphericalProjection ); - // Compare bounding box size of the line string with the angularResolu= tion - // Immediately return if the latLonAltBox is smaller. - if ( !viewport->resolves( lineString.latLonAltBox() ) ) { -// mDebug() << "Object too small to be resolved"; - return false; - } - - d->lineStringToPolygon( lineString, viewport, polygons ); - return true; -} - -void SphericalProjectionPrivate::tessellateLineSegment( const GeoDataCoord= inates &aCoords, - qreal ax, qreal ay, - const GeoDataCoordinates &= bCoords, - qreal bx, qreal by, - LineStringFlag lineStringF= lag, - QVector &polyg= ons, - const ViewportParams *view= port, - TessellationFlags f) const -{ - // We take the manhattan length as a distance approximation - // that can be too big by a factor of sqrt(2) - qreal distance =3D fabs((bx - ax)) + fabs((by - ay)); -#ifdef SAFE_DISTANCE - // Interpolate additional nodes if the line segment that connects the - // current or previous nodes might cross the viewport. - // The latter can pretty safely be excluded for most projections if bo= th points - // are located on the same side relative to the viewport boundaries an= d if they are - // located more than half the line segment distance away from the view= port. - const qreal safeDistance =3D - 0.5 * distance; - if ( !( bx < safeDistance && ax < safeDistance ) - || !( by < safeDistance && ay < safeDistance ) - || !( bx + safeDistance > viewport->width() - && ax + safeDistance > viewport->width() ) - || !( by + safeDistance > viewport->height() - && ay + safeDistance > viewport->height() ) - ) - { -#endif - bool const smallScreen =3D MarbleGlobal::getInstance()->profiles()= & MarbleGlobal::SmallScreen; - int const finalTessellationPrecision =3D smallScreen ? 3 * tessell= ationPrecision : tessellationPrecision; - - // Let the line segment follow the spherical surface - // if the distance between the previous point and the current point - // on screen is too big - - if ( distance > finalTessellationPrecision ) { - const int tessellatedNodes =3D qMin( distance / finalTess= ellationPrecision, maxTessellationNodes ); - - processTessellation( aCoords, bCoords, - tessellatedNodes, - lineStringFlag, - polygons, - viewport, - f ); - } - else { - crossHorizon( bCoords, lineStringFlag, polygons, viewport ); - } -#ifdef SAFE_DISTANCE - } -#endif -} - - -void SphericalProjectionPrivate::processTessellation( const GeoDataCoordin= ates &previousCoords, - const GeoDataCoordinat= es ¤tCoords, - int tessellatedNodes, - LineStringFlag lineStr= ingFlag, - QVector &p= olygons, - const ViewportParams *= viewport, - TessellationFlags f) c= onst -{ - - const bool clampToGround =3D f.testFlag( FollowGround ); - const bool followLatitudeCircle =3D f.testFlag( RespectLatitudeCircle ) - && previousCoords.latitude() =3D=3D = currentCoords.latitude(); - - // Calculate steps for tessellation: lonDiff and altDiff - qreal lonDiff =3D 0.0; - if ( followLatitudeCircle ) { - const int previousSign =3D previousCoords.longitude() > 0 ? 1 : -1; - const int currentSign =3D currentCoords.longitude() > 0 ? 1 : -1; - - lonDiff =3D currentCoords.longitude() - previousCoords.longitude(); - if ( previousSign !=3D currentSign - && fabs(previousCoords.longitude()) + fabs(currentCoords.long= itude()) > M_PI ) { - if ( previousSign > currentSign ) { - // going eastwards -> - lonDiff +=3D 2 * M_PI ; - } else { - // going westwards -> - lonDiff -=3D 2 * M_PI; - } - } - } - - const qreal altDiff =3D currentCoords.altitude() - previousCoords.alti= tude(); - - // Create the tessellation nodes. - GeoDataCoordinates previousTessellatedCoords =3D previousCoords; - for ( int i =3D 1; i <=3D tessellatedNodes; ++i ) { - const qreal t =3D (qreal)(i) / (qreal)( tessellatedNodes + 1 ); - - // interpolate the altitude, too - const qreal altitude =3D clampToGround ? 0 : altDiff * t + previou= sCoords.altitude(); - - qreal lon =3D 0.0; - qreal lat =3D 0.0; - if ( followLatitudeCircle ) { - // To tessellate along latitude circles use the - // linear interpolation of the longitude. - lon =3D lonDiff * t + previousCoords.longitude(); - lat =3D previousTessellatedCoords.latitude(); - } - else { - // To tessellate along great circles use the - // normalized linear interpolation ("NLERP") for latitude and = longitude. - const Quaternion itpos =3D Quaternion::nlerp( previousCoords.q= uaternion(), currentCoords.quaternion(), t ); - itpos. getSpherical( lon, lat ); - } - - const GeoDataCoordinates currentTessellatedCoords( lon, lat, altit= ude ); - crossHorizon( currentTessellatedCoords, lineStringFlag, polygons, = viewport ); - previousTessellatedCoords =3D currentTessellatedCoords; - } - - // For the clampToGround case add the "current" coordinate after addin= g all other nodes. - GeoDataCoordinates currentModifiedCoords( currentCoords ); - if ( clampToGround ) { - currentModifiedCoords.setAltitude( 0.0 ); - } - crossHorizon( currentModifiedCoords, lineStringFlag, polygons, viewpor= t ); -} - -void SphericalProjectionPrivate::crossHorizon( const GeoDataCoordinates & = bCoord, - LineStringFlag lineStringFla= g, - QVector &polygon= s, - const ViewportParams *viewpo= rt ) const -{ - qreal x, y; - bool globeHidesPoint; - - Q_Q( const AbstractProjection ); - - q->screenCoordinates( bCoord, viewport, x, y, globeHidesPoint ); - - if( !globeHidesPoint ) { - *polygons.last() << QPointF( x, y ); - } - else { - if ( !polygons.last()->isEmpty() && lineStringFlag !=3D ClosedLine= String ) { - QPolygonF *path =3D new QPolygonF; - polygons.append( path ); - } - } -} - -bool SphericalProjectionPrivate::lineStringToPolygon( const GeoDataLineStr= ing &lineString, - const ViewportParams *viewpo= rt, - QVector &polygo= ns ) const -{ - Q_Q( const SphericalProjection ); - - const TessellationFlags f =3D lineString.tessellationFlags(); - - qreal x =3D 0; - qreal y =3D 0; - bool globeHidesPoint =3D false; - - qreal previousX =3D -1.0; - qreal previousY =3D -1.0; - bool previousGlobeHidesPoint =3D false; - - qreal horizonX =3D -1.0; - qreal horizonY =3D -1.0; - - polygons.append( new QPolygonF ); - - GeoDataLineString::ConstIterator itCoords =3D lineString.constBegin(); - GeoDataLineString::ConstIterator itPreviousCoords =3D lineString.const= Begin(); - - // Some projections display the earth in a way so that there is a - // foreside and a backside. - // The horizon is the line (usually a circle) which separates both - // sides from each other and resembles the map shape. - GeoDataCoordinates horizonCoords; - - // A horizon pair is a pair of two subsequent horizon crossings: - // The first one describes the point where a line string disappears be= hind the horizon. - // and where horizonPair is set to true. - // The second one describes the point where the line string reappears. - // In this case the two points are connected and horizonPair is set to= false again. - bool horizonPair =3D false; - GeoDataCoordinates horizonDisappearCoords; - - // If the first horizon crossing in a line string describes the appear= ance of - // a line string then we call it a "horizon orphan" and horizonOrphan = is set to true. - // In this case once the last horizon crossing in the line string is r= eached - // it needs to be connected to the orphan. - bool horizonOrphan =3D false; - GeoDataCoordinates horizonOrphanCoords; - - GeoDataLineString::ConstIterator itBegin =3D lineString.constBegin(); - GeoDataLineString::ConstIterator itEnd =3D lineString.constEnd(); - - bool processingLastNode =3D false; - - // We use a while loop to be able to cover linestrings as well as line= ar rings: - // Linear rings require to tessellate the path from the last node to t= he first node - // which isn't really convenient to achieve with a for loop ... - - const bool isLong =3D lineString.size() > 50; - const int maximumDetail =3D ( viewport->radius() > 5000 ) ? 5 : - ( viewport->radius() > 2500 ) ? 4 : - ( viewport->radius() > 1000 ) ? 3 : - ( viewport->radius() > 600 ) ? 2 : - ( viewport->radius() > 50 ) ? 1 : - 0; - - while ( itCoords !=3D itEnd ) - { - - // Optimization for line strings with a big amount of nodes - bool skipNode =3D itCoords !=3D itBegin && isLong && !processingLa= stNode && - ( (*itCoords).detail() > maximumDetail - || viewport->resolves( *itPreviousCoords, *itCoords ) ); - - if ( !skipNode ) { - - q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoi= nt ); - - // Initializing variables that store the values of the previou= s iteration - if ( !processingLastNode && itCoords =3D=3D itBegin ) { - previousGlobeHidesPoint =3D globeHidesPoint; - itPreviousCoords =3D itCoords; - previousX =3D x; - previousY =3D y; - } - - // Check for the "horizon case" (which is present e.g. for the= spherical projection - const bool isAtHorizon =3D ( globeHidesPoint || previousGlobeH= idesPoint ) && - ( globeHidesPoint !=3D previousGlobe= HidesPoint ); - = - if ( isAtHorizon ) { - // Handle the "horizon case" - horizonCoords =3D findHorizon( *itPreviousCoords, *itCoord= s, viewport, f ); - - if ( lineString.isClosed() ) { - if ( horizonPair ) { - horizonToPolygon( viewport, horizonDisappearCoords= , horizonCoords, polygons.last() ); - horizonPair =3D false; - } - else { - if ( globeHidesPoint ) { - horizonDisappearCoords =3D horizonCoords; - horizonPair =3D true; - } - else { - horizonOrphanCoords =3D horizonCoords; - horizonOrphan =3D true; - } - } - } - - q->screenCoordinates( horizonCoords, viewport, horizonX, h= orizonY ); - - // If the line appears on the visible half we need - // to add an interpolated point at the horizon as the prev= ious point. - if ( previousGlobeHidesPoint ) { - *polygons.last() << QPointF( horizonX, horizonY ); - } - } - - // This if-clause contains the section that tessellates the li= ne - // segments of a linestring. If you are about to learn how the= code of - // this class works you can safely ignore this section for a s= tart. - - if ( lineString.tessellate() ) { - LineStringFlag lineStringFlag =3D lineString.isClosed() ? = ClosedLineString : PlainLineString; - if ( !isAtHorizon ) { - tessellateLineSegment( *itPreviousCoords, previousX, p= reviousY, - *itCoords, x, y, - lineStringFlag, polygons, viewp= ort, - f ); - } - else { - // Connect the interpolated point at the horizon with= the - // current or previous point in the line. = - if ( previousGlobeHidesPoint ) { - tessellateLineSegment( horizonCoords, horizonX, ho= rizonY, - *itCoords, x, y, - lineStringFlag, polygons, v= iewport, - f ); - } - else { - tessellateLineSegment( *itPreviousCoords, previous= X, previousY, - horizonCoords, horizonX, ho= rizonY, - lineStringFlag, polygons, v= iewport, - f ); - } - } - } - else { - if ( !globeHidesPoint ) { - *polygons.last() << QPointF( x, y ); - } - else { - if ( !previousGlobeHidesPoint && isAtHorizon ) { - *polygons.last() << QPointF( horizonX, horizonY ); - } - } - } - - if ( globeHidesPoint ) { - if ( !previousGlobeHidesPoint - && !lineString.isClosed() - ) { - polygons.append( new QPolygonF ); - } - } - - previousGlobeHidesPoint =3D globeHidesPoint; - itPreviousCoords =3D itCoords; - previousX =3D x; - previousY =3D y; - } - - // Here we modify the condition to be able to process the - // first node after the last node in a LinearRing. - - if ( processingLastNode ) { - break; - } - ++itCoords; - - if ( itCoords =3D=3D itEnd && lineString.isClosed() ) { - itCoords =3D itBegin; - processingLastNode =3D true; - } - } - - // In case of horizon crossings, make sure that we always get a - // polygon closed correctly. - if ( horizonOrphan && lineString.isClosed() ) { - horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, po= lygons.last() ); - } - - if ( polygons.last()->size() <=3D 1 ){ - polygons.pop_back(); // Clean up "unused" empty polygon instances - } - - return polygons.isEmpty(); -} - -void SphericalProjectionPrivate::horizonToPolygon( const ViewportParams *v= iewport, - const GeoDataCoordinates & disa= ppearCoords, - const GeoDataCoordinates & reap= pearCoords, - QPolygonF * polygon ) const -{ - qreal x, y; - - const qreal imageHalfWidth =3D viewport->width() / 2; - const qreal imageHalfHeight =3D viewport->height() / 2; - - bool dummyGlobeHidesPoint =3D false; - - Q_Q( const SphericalProjection ); - // Calculate the angle of the position vectors of both coordinates - q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHides= Point ); - qreal alpha =3D atan2( y - imageHalfHeight, - x - imageHalfWidth ); - - q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesP= oint ); - qreal beta =3D atan2( y - imageHalfHeight, - x - imageHalfWidth ); - - // Calculate the difference between both - qreal diff =3D GeoDataCoordinates::normalizeLon( beta - alpha ); - - qreal sgndiff =3D diff < 0 ? -1 : 1; - - const qreal arcradius =3D viewport->radius(); - const int itEnd =3D fabs(diff * RAD2DEG); - - // Create a polygon that resembles an arc between the two position vec= tors - for ( int it =3D 1; it <=3D itEnd; ++it ) { - const qreal angle =3D alpha + DEG2RAD * sgndiff * it; - const qreal itx =3D imageHalfWidth + arcradius * cos( angle ); - const qreal ity =3D imageHalfHeight + arcradius * sin( angle ); - *polygon << QPointF( itx, ity ); - } -} - - -GeoDataCoordinates SphericalProjectionPrivate::findHorizon( const GeoDataC= oordinates & previousCoords, - const GeoDataCoordinat= es & currentCoords, - const ViewportParams *= viewport, - TessellationFlags f, - int recursionCounter )= const -{ - bool currentHide =3D globeHidesPoint( currentCoords, viewport ) ; - - if ( recursionCounter > 20 ) { - return currentHide ? previousCoords : currentCoords; - } - ++recursionCounter; - - bool followLatitudeCircle =3D false; - - // Calculate steps for tessellation: lonDiff and altDiff - qreal lonDiff =3D 0.0; - qreal previousLongitude =3D 0.0; - qreal previousLatitude =3D 0.0; - - if ( f.testFlag( RespectLatitudeCircle ) ) { - previousCoords.geoCoordinates( previousLongitude, previousLatitude= ); - qreal previousSign =3D previousLongitude > 0 ? 1 : -1; - - qreal currentLongitude =3D 0.0; - qreal currentLatitude =3D 0.0; - currentCoords.geoCoordinates( currentLongitude, currentLatitude ); - qreal currentSign =3D currentLongitude > 0 ? 1 : -1; - - if ( previousLatitude =3D=3D currentLatitude ) { - followLatitudeCircle =3D true; - - lonDiff =3D currentLongitude - previousLongitude; - if ( previousSign !=3D currentSign - && fabs(previousLongitude) + fabs(currentLongitude) > M_P= I ) { - if ( previousSign > currentSign ) { - // going eastwards -> - lonDiff +=3D 2 * M_PI ; - } else { - // going westwards -> - lonDiff -=3D 2 * M_PI; - } - } - - } - else { -// mDebug() << "Don't FollowLatitudeCircle"; - } - } - - qreal lon =3D 0.0; - qreal lat =3D 0.0; - - qreal altDiff =3D currentCoords.altitude() - previousCoords.altitude(); - - if ( followLatitudeCircle ) { - // To tessellate along latitude circles use the - // linear interpolation of the longitude. - lon =3D lonDiff * 0.5 + previousLongitude; - lat =3D previousLatitude; - } - else { - // To tessellate along great circles use the - // normalized linear interpolation ("NLERP") for latitude and long= itude. - const Quaternion itpos =3D Quaternion::nlerp( previousCoords.quate= rnion(), currentCoords.quaternion(), 0.5 ); - itpos. getSpherical( lon, lat ); - } - - qreal altitude =3D previousCoords.altitude() + 0.5 * altDiff; - - GeoDataCoordinates horizonCoords( lon, lat, altitude ); - - bool horizonHide =3D globeHidesPoint( horizonCoords, viewport ); - - if ( horizonHide !=3D currentHide ) { - return findHorizon( horizonCoords, currentCoords, viewport, f, rec= ursionCounter ); - } - - return findHorizon( previousCoords, horizonCoords, viewport, f, recurs= ionCounter ); -} - - -bool SphericalProjectionPrivate::globeHidesPoint( const GeoDataCoordinates= &coordinates, - const ViewportParams *viewport ) -{ - qreal absoluteAltitude =3D coordinates.altitude() + EARTH_RADIUS; - Quaternion qpos =3D coordinates.quaternion(); - - qpos.rotateAroundAxis( viewport->planetAxisMatrix() ); - - qreal pixelAltitude =3D ( ( viewport->radius() ) - / EARTH_RADIUS * absoluteAltitude ); - if ( coordinates.altitude() < 10000 ) { - // Skip placemarks at the other side of the earth. - if ( qpos.v[Q_Z] < 0 ) { - return true; - } - } - else { - qreal earthCenteredX =3D pixelAltitude * qpos.v[Q_X]; - qreal earthCenteredY =3D pixelAltitude * qpos.v[Q_Y]; - qreal radius =3D viewport->radius(); - - // Don't draw high placemarks (e.g. satellites) that aren't visibl= e. - if ( qpos.v[Q_Z] < 0 - && ( ( earthCenteredX * earthCenteredX - + earthCenteredY * earthCenteredY ) - < radius * radius ) ) { - return true; - } - } - - return false; -} - - - } diff --git a/src/lib/marble/projections/SphericalProjection.h b/src/lib/mar= ble/projections/SphericalProjection.h index 84cc6f3..f50daf9 100644 --- a/src/lib/marble/projections/SphericalProjection.h +++ b/src/lib/marble/projections/SphericalProjection.h @@ -65,10 +65,6 @@ class SphericalProjection : public AzimuthalProjection const QSizeF& size, bool &globeHidesPoint ) const; = - virtual bool screenCoordinates( const GeoDataLineString &lineString, - const ViewportParams *viewport, - QVector &polygons ) const; - using AbstractProjection::screenCoordinates; = /** @@ -85,8 +81,6 @@ class SphericalProjection : public AzimuthalProjection qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit =3D GeoDataCoordina= tes::Degree ) const; = - bool mapCoversViewport( const ViewportParams *viewport ) const; - protected: SphericalProjection(SphericalProjectionPrivate *dd ); =20