[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [marble] src/lib/marble: - Cleaned up and refactored the AzimuthalProjection and GnomonicProjection.
From: Torsten Rahn <torsten.rahn () lge ! com>
Date: 2014-08-31 23:13:31
Message-ID: E1XOEJP-0004G7-W7 () scm ! kde ! org
[Download RAW message or body]
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->projection.
- 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/marble/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 <carlos _licea@hotmail.com>
+// Copyright 2008 Inge Wallin <inge@lysator.liu.se>
+// Copyright 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
+//
+
+
+// local
+#include "GenericScanlineTextureMapper.h"
+
+// Qt
+#include <QtCore/qmath.h>
+#include <QtCore/QRunnable>
+#include <QtGui/QImage>
+
+// 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 *canvasImage, \
const ViewportParams *viewport, MapQuality mapQuality, int yTop, int 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 *tileLoader, \
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( StackedTileLoader \
*tileLoader ) + : TextureMapperInterface()
+ , m_tileLoader( tileLoader )
+ , m_radius( 0 )
+ , m_threadPool()
+{
+}
+
+void GenericScanlineTextureMapper::mapTexture( GeoPainter *painter,
+ const ViewportParams *viewport,
+ int tileZoomLevel,
+ const QRect &dirtyRect,
+ TextureColorizer *texColorizer )
+{
+ if ( m_canvasImage.size() != viewport->size() || m_radius != viewport->radius() \
) { + const QImage::Format optimalFormat = \
ScanlineTextureMapperContext::optimalCanvasImageFormat( viewport ); +
+ if ( m_canvasImage.size() != viewport->size() || m_canvasImage.format() != \
optimalFormat ) { + m_canvasImage = QImage( viewport->size(), \
optimalFormat ); + }
+
+ if ( !viewport->mapCoversViewport() ) {
+ m_canvasImage.fill( 0 );
+ }
+
+ m_radius = viewport->radius();
+ m_repaintNeeded = true;
+ }
+
+ if ( m_repaintNeeded ) {
+ mapTexture( viewport, tileZoomLevel, painter->mapQuality() );
+
+ if ( texColorizer ) {
+ texColorizer->colorize( &m_canvasImage, viewport, painter->mapQuality() \
); + }
+
+ m_repaintNeeded = false;
+ }
+
+ const int radius = viewport->radius() * \
viewport->currentProjection()->clippingRadius(); +
+ QRect rect( viewport->width() / 2 - radius, viewport->height() / 2 - radius,
+ 2 * radius, 2 * radius);
+#if QT_VERSION < 0x050000
+ rect = rect.intersect( dirtyRect );
+#else
+ rect = rect.intersected( dirtyRect );
+#endif
+ painter->drawImage( rect, m_canvasImage, rect );
+}
+
+void GenericScanlineTextureMapper::mapTexture( const ViewportParams *viewport, int \
tileZoomLevel, MapQuality mapQuality ) +{
+ // Reset backend
+ m_tileLoader->resetTilehash();
+
+ const int imageHeight = viewport->height();
+ const qint64 radius = viewport->radius() * \
viewport->currentProjection()->clippingRadius(); +
+ // Calculate the actual y-range of the map on the screen
+ const int skip = ( mapQuality == LowQuality ) ? 1
+ : 0;
+ const int yTop = ( imageHeight / 2 - radius >= 0 ) ? imageHeight / 2 - radius
+ : 0;
+ const int yBottom = ( yTop == 0 ) ? imageHeight - skip
+ : yTop + radius + radius - skip;
+
+ const int numThreads = m_threadPool.maxThreadCount();
+ const int yStep = qCeil(qreal( yBottom - yTop ) / qreal(numThreads));
+ for ( int i = 0; i < numThreads; ++i ) {
+ const int yStart = yTop + i * yStep;
+ const int yEnd = qMin(yBottom, yTop + (i + 1) * yStep);
+ QRunnable *const job = new RenderJob( m_tileLoader, tileZoomLevel, \
&m_canvasImage, viewport, mapQuality, yStart, yEnd ); + m_threadPool.start( \
job ); + }
+
+ m_threadPool.waitForDone();
+
+ m_tileLoader->cleanupTilehash();
+}
+
+void GenericScanlineTextureMapper::RenderJob::run()
+{
+ const int imageWidth = m_canvasImage->width();
+ const int imageHeight = m_canvasImage->height();
+ const qint64 radius = m_viewport->radius();
+ // Calculate how many degrees are being represented per pixel.
+ const qreal rad2Pixel = ( 2 * radius ) / M_PI;
+ const qreal pixel2Rad = 1.0/rad2Pixel;
+
+ const bool interlaced = ( m_mapQuality == LowQuality );
+ const bool highQuality = ( m_mapQuality == HighQuality
+ || m_mapQuality == PrintQuality );
+ const bool printQuality = ( m_mapQuality == PrintQuality );
+
+ // Evaluate the degree of interpolation
+ const int n = ScanlineTextureMapperContext::interpolationStep( m_viewport, \
m_mapQuality ); +
+ // Calculate north pole position to decrease pole distortion later on
+ qreal northPoleX, northPoleY;
+ bool globeHidesNorthPole;
+ GeoDataCoordinates northPole(0, m_viewport->currentProjection()->maxLat(), 0);
+ m_viewport->screenCoordinates(northPole, northPoleX, northPoleY, \
globeHidesNorthPole ); +
+ // initialize needed variables that are modified during texture mapping:
+
+ ScanlineTextureMapperContext context( m_tileLoader, m_tileLevel );
+
+ qreal clipRadius = radius * m_viewport->currentProjection()->clippingRadius();
+
+
+ // Paint the map.
+ for ( int y = m_yTop; y < m_yBottom; ++y ) {
+
+ // rx is the radius component in x direction
+ const int rx = (int)sqrt( (qreal)( clipRadius * clipRadius
+ - ( ( y - imageHeight / 2 )
+ * ( y - imageHeight / 2 ) ) ) );
+
+ // Calculate the actual x-range of the map within the current scanline.
+ //
+ // If the circular border of the earth disk is still visible then xLeft
+ // equals the scanline position of the most left pixel that gets covered
+ // 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 ("rx").
+ //
+ // If the zoom factor is high enough then the whole screen gets covered
+ // by the earth and the border of the earth disk isn't visible anymore.
+ // In that situation xLeft equals zero.
+ // For xRight the situation is similar.
+
+ const int xLeft = ( imageWidth / 2 - rx > 0 ) ? imageWidth / 2 - rx
+ : 0;
+ const int xRight = ( imageWidth / 2 - rx > 0 ) ? xLeft + rx + rx
+ : imageWidth;
+
+ QRgb * scanLine = (QRgb*)( m_canvasImage->scanLine( y ) ) + xLeft;
+
+ const int xIpLeft = ( imageWidth / 2 - rx > 0 ) ? n * (int)( xLeft / n + 1 \
) + : 1;
+ const int xIpRight = ( imageWidth / 2 - rx > 0 ) ? n * (int)( xRight / n - 1 \
) + : n * (int)( xRight / n - \
1 ) + 1; +
+ // Decrease pole distortion due to linear approximation ( y-axis )
+ bool crossingPoleArea = false;
+ if ( !globeHidesNorthPole
+ && northPoleY - ( n * 0.75 ) <= y
+ && northPoleY + ( n * 0.75 ) >= y )
+ {
+ crossingPoleArea = true;
+ }
+
+ int ncount = 0;
+
+
+ for ( int x = xLeft; x < xRight; ++x ) {
+
+ // Prepare for interpolation
+ const int leftInterval = xIpLeft + ncount * n;
+
+ bool interpolate = false;
+
+ if ( x >= xIpLeft && x <= xIpRight ) {
+
+ // Decrease pole distortion due to linear approximation ( x-axis )
+ if ( crossingPoleArea
+ && northPoleX >= leftInterval + n
+ && northPoleX < leftInterval + 2 * n
+ && x < leftInterval + 3 * n )
+ {
+ interpolate = false;
+ }
+ else {
+ x += n - 1;
+ interpolate = !printQuality;
+ ++ncount;
+ }
+ }
+ else
+ interpolate = 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 += ( n - 1 );
+ }
+
+ if ( x < imageWidth ) {
+ if ( highQuality )
+ context.pixelValueF( lon, lat, scanLine );
+ else
+ context.pixelValue( lon, lat, scanLine );
+ }
+
+ ++scanLine;
+ lon += pixel2Rad;
+ }
+
+ // copy scanline to improve performance
+ if ( interlaced && y + 1 < m_yBottom ) {
+
+ const int pixelByteSize = m_canvasImage->bytesPerLine() / imageWidth;
+
+ memcpy( m_canvasImage->scanLine( y + 1 ) + xLeft * pixelByteSize,
+ m_canvasImage->scanLine( y ) + xLeft * pixelByteSize,
+ ( xRight - xLeft ) * pixelByteSize );
+ ++y;
+ }
+ }
+}
diff --git a/src/lib/marble/GnomonicScanlineTextureMapper.h \
b/src/lib/marble/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 <bbeschow@cs.tu-berlin.de>
//
-#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 TextureMapperInterface
}
-#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::createLinearRingFromGeoRect( const GeoDataC
bool GeoPainterPrivate::doClip( const ViewportParams *viewport )
{
- if ( viewport->projection() != Spherical )
+ if ( !viewport->currentProjection()->isClippedToSphere() )
return true;
- return ( viewport->radius() > viewport->width() / 2 || viewport->radius() > \
viewport->height() / 2 ); + const qint64 radius = viewport->radius() * \
viewport->currentProjection()->clippingRadius(); +
+ return ( radius > viewport->width() / 2 || radius > viewport->height() / 2 );
}
// -------------------------------------------------------------------------------------------------
diff --git a/src/lib/marble/GnomonicScanlineTextureMapper.cpp \
b/src/lib/marble/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 <carlos _licea@hotmail.com>
-// Copyright 2008 Inge Wallin <inge@lysator.liu.se>
-// Copyright 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
-//
-
-
-// local
-#include "GnomonicScanlineTextureMapper.h"
-
-// Qt
-#include <QtCore/qmath.h>
-#include <QtCore/QRunnable>
-#include <QtGui/QImage>
-
-// 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 *canvasImage, \
const ViewportParams *viewport, MapQuality mapQuality, int yTop, int \
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 *tileLoader, \
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_yPaintedTop( yTop ),
- m_yPaintedBottom( yBottom )
-{
-}
-
-
-GnomonicScanlineTextureMapper::GnomonicScanlineTextureMapper( StackedTileLoader \
*tileLoader )
- : TextureMapperInterface()
- , m_tileLoader( tileLoader )
- , m_radius( 0 )
-{
-}
-
-
-void GnomonicScanlineTextureMapper::mapTexture( GeoPainter *painter,
- const ViewportParams *viewport,
- int tileZoomLevel,
- const QRect &dirtyRect,
- TextureColorizer *texColorizer )
-{
- if ( m_canvasImage.size() != viewport->size() || m_radius != viewport->radius() \
) {
- const QImage::Format optimalFormat = \
ScanlineTextureMapperContext::optimalCanvasImageFormat( viewport );
-
- if ( m_canvasImage.size() != viewport->size() || m_canvasImage.format() != \
optimalFormat ) {
- m_canvasImage = QImage( viewport->size(), optimalFormat );
- }
-
- if ( !viewport->mapCoversViewport() ) {
- m_canvasImage.fill( 0 );
- }
-
- m_radius = viewport->radius();
- m_repaintNeeded = true;
- }
-
- if ( m_repaintNeeded ) {
- mapTexture( viewport, tileZoomLevel, painter->mapQuality() );
-
- if ( texColorizer ) {
- texColorizer->colorize( &m_canvasImage, viewport, painter->mapQuality() \
);
- }
-
- m_repaintNeeded = false;
- }
-
- painter->drawImage( dirtyRect, m_canvasImage, dirtyRect );
-}
-
-void GnomonicScanlineTextureMapper::mapTexture( const ViewportParams *viewport, int \
tileZoomLevel, MapQuality mapQuality )
-{
- // Reset backend
- m_tileLoader->resetTilehash();
-
- const int imageHeight = viewport->height();
-
- const int numThreads = m_threadPool.maxThreadCount();
- const int yStep = imageHeight / numThreads;
- for ( int i = 0; i < numThreads; ++i ) {
- const int yStart = i * yStep;
- const int yEnd = (i + 1) * yStep;
- QRunnable *const job = new RenderJob( m_tileLoader, tileZoomLevel, \
&m_canvasImage, viewport, mapQuality, yStart, yEnd );
- m_threadPool.start( job );
- }
-
- m_threadPool.waitForDone();
-
- m_tileLoader->cleanupTilehash();
-}
-
-void GnomonicScanlineTextureMapper::RenderJob::run()
-{
- const int imageWidth = m_canvasImage->width();
- const qint64 radius = m_viewport->radius();
- // Calculate how many degrees are being represented per pixel.
- const qreal rad2Pixel = ( 2 * radius ) / M_PI;
- const qreal pixel2Rad = 1.0/rad2Pixel;
-
- const bool interlaced = ( m_mapQuality == LowQuality );
- const bool highQuality = ( m_mapQuality == HighQuality
- || m_mapQuality == PrintQuality );
- const bool printQuality = ( m_mapQuality == PrintQuality );
-
- // Evaluate the degree of interpolation
- const int n = ScanlineTextureMapperContext::interpolationStep( m_viewport, \
m_mapQuality );
- const int maxInterpolationPointX = n * (int)( imageWidth / n - 1 ) + 1;
-
- // initialize needed variables that are modified during texture mapping:
-
- ScanlineTextureMapperContext context( m_tileLoader, m_tileLevel );
-
-
- // Paint the map.
- for ( int y = m_yPaintedTop; y < m_yPaintedBottom; ++y ) {
-
- QRgb * scanLine = (QRgb*)m_canvasImage->scanLine( y );
-
- for ( int x = 0; x < imageWidth; ++x ) {
-
- // Prepare for interpolation
- bool interpolate = false;
- if ( x >= 1 && x <= maxInterpolationPointX ) {
- x += n - 1;
- interpolate = !printQuality;
- }
- else {
- interpolate = 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 += ( n - 1 );
- }
-
- if ( x < imageWidth ) {
- if ( highQuality )
- context.pixelValueF( lon, lat, scanLine );
- else
- context.pixelValue( lon, lat, scanLine );
- }
-
- ++scanLine;
- lon += pixel2Rad;
- }
-
- // copy scanline to improve performance
- if ( interlaced && y + 1 < m_yPaintedBottom ) {
-
- const int pixelByteSize = m_canvasImage->bytesPerLine() / imageWidth;
-
- 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/MapViewWidget.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 = new QMenu;
m_globeViewAction = new QAction( QIcon(":/icons/map-globe.png"),
tr( "Spherical view" ),
- m_popupMenuSpherical );
+ m_globeViewButton );
m_globeViewAction->setCheckable( true );
m_globeViewAction->setChecked( false );
- m_gnomonicViewAction = 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 = 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 = 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/marble/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 = m_canvasImage.height();
- const qint64 radius = viewport->radius();
- // Calculate how many degrees are being represented per pixel.
- const float rad2Pixel = (float)( 2 * radius ) / M_PI;
-
- //mDebug() << "m_maxGlobalX: " << m_maxGlobalX;
- //mDebug() << "radius : " << radius << endl;
-
- // Calculate translation of center point
- const qreal centerLat = viewport->centerLatitude();
-
- const int yCenterOffset = (int)( asinh( tan( centerLat ) ) * rad2Pixel );
// Calculate y-range the represented by the center point, yTop and
// what actually can be painted
- const int yTop = imageHeight / 2 - 2 * radius + yCenterOffset;
- int yPaintedTop = imageHeight / 2 - 2 * radius + yCenterOffset;
- int yPaintedBottom = 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 = qBound(0.0, realYTop, (qreal)(imageHeight));
+ int yPaintedTop = yTop;
+ int yPaintedBottom = qBound(0.0, realYBottom, (qreal)(imageHeight));
- if (yPaintedTop < 0) yPaintedTop = 0;
- if (yPaintedTop > imageHeight) yPaintedTop = imageHeight;
- if (yPaintedBottom < 0) yPaintedBottom = 0;
- if (yPaintedBottom > imageHeight) yPaintedBottom = imageHeight;
+ yPaintedTop = qBound(0, yPaintedTop, imageHeight);
+ yPaintedBottom = qBound(0, yPaintedBottom, imageHeight);
const int numThreads = m_threadPool.maxThreadCount();
const int yStep = ( yPaintedBottom - yPaintedTop ) / numThreads;
diff --git a/src/lib/marble/TextureColorizer.cpp \
b/src/lib/marble/TextureColorizer.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 = viewport->radius();
+ const qint64 radius = viewport->radius() * \
viewport->currentProjection()->clippingRadius();
const int imgheight = origimg->height();
const int imgwidth = origimg->width();
@@ -253,31 +254,20 @@ void TextureColorizer::colorize( QImage *origimg, const \
ViewportParams *viewport int bump = 8;
if ( radius * radius > imgradius
- || viewport->projection() != Spherical )
+ || !viewport->currentProjection()->isClippedToSphere() )
{
int yTop = 0;
int yBottom = imgheight;
- if( viewport->projection() != Spherical )
+ if( !viewport->currentProjection()->isClippedToSphere() && \
!viewport->currentProjection()->traversablePoles() ) {
- // Calculate translation of center point
- const qreal centerLat = viewport->centerLatitude();
-
- const float rad2Pixel = (qreal)( 2 * radius ) / M_PI;
- if ( viewport->projection() == Equirectangular ) {
- int yCenterOffset = (int)( centerLat * rad2Pixel );
- yTop = ( imgry - radius + yCenterOffset < 0)? 0 : imgry - radius + \
yCenterOffset;
- yBottom = ( imgry + yCenterOffset + radius > imgheight )? imgheight \
: imgry + yCenterOffset + radius;
- }
- else if ( viewport->projection() == Mercator ) {
- int yCenterOffset = (int)( asinh( tan( centerLat ) ) * rad2Pixel );
- yTop = ( imgry - 2 * radius + yCenterOffset < 0 ) ? 0 : imgry - 2 * \
radius + yCenterOffset;
- yBottom = ( imgry + 2 * radius + yCenterOffset > imgheight )? \
imgheight : imgry + 2 * radius + yCenterOffset;
- }
- else {
- yTop = 0;
- yBottom = imgheight;
- }
+ 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 ); + yTop = \
qBound(0.0, realYTop, (qreal)(imgheight)); + yBottom = qBound(0.0, \
realYBottom, (qreal)(imgheight)); }
const int itEnd = 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 = new GnomonicScanlineTextureMapper( &d->m_tileLoader );
+ d->m_texmapper = new GenericScanlineTextureMapper( &d->m_tileLoader );
break;
default:
d->m_texmapper = 0;
diff --git a/src/lib/marble/projections/AbstractProjection.cpp \
b/src/lib/marble/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 lat,
const ViewportParams *viewport,
qreal &x, qreal &y ) const
diff --git a/src/lib/marble/projections/AbstractProjection.h \
b/src/lib/marble/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 coordinates \
in the map.
diff --git a/src/lib/marble/projections/AzimuthalProjection.cpp \
b/src/lib/marble/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 &lineString,
+ const ViewportParams *viewport,
+ QVector<QPolygonF *> &polygons ) \
const +{
+
+ Q_D( const AzimuthalProjection );
+ // Compare bounding box size of the line string with the angularResolution
+ // Immediately return if the latLonAltBox is smaller.
+ if ( !viewport->resolves( lineString.latLonAltBox() ) ) {
+// mDebug() << "Object too small to be resolved";
+ return false;
+ }
+
+ d->lineStringToPolygon( lineString, viewport, polygons );
+ return true;
+}
+
+bool AzimuthalProjection::mapCoversViewport( const ViewportParams *viewport ) const
+{
+ qint64 radius = viewport->radius() * \
viewport->currentProjection()->clippingRadius(); + qint64 width = \
viewport->width(); + qint64 height = 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 >= width * width + height * height )
+ return true;
+
+ return false;
+}
+
GeoDataLatLonAltBox AzimuthalProjection::latLonAltBox( const QRect& screenRect,
const ViewportParams \
*viewport ) const {
@@ -93,7 +139,7 @@ GeoDataLatLonAltBox AzimuthalProjection::latLonAltBox( const \
QRect& screenRect,
QPainterPath AzimuthalProjection::mapShape( const ViewportParams *viewport ) const
{
- int radius = viewport->radius();
+ int radius = viewport->radius() * \
viewport->currentProjection()->clippingRadius(); int imgWidth = viewport->width();
int imgHeight = viewport->height();
@@ -126,6 +172,482 @@ AzimuthalProjection::~AzimuthalProjection()
{
}
+void AzimuthalProjectionPrivate::tessellateLineSegment( const GeoDataCoordinates \
&aCoords, + qreal ax, qreal ay,
+ const GeoDataCoordinates &bCoords,
+ qreal bx, qreal by,
+ QVector<QPolygonF*> &polygons,
+ const ViewportParams *viewport,
+ 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 = 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 both points
+ // are located on the same side relative to the viewport boundaries and if they \
are + // located more than half the line segment distance away from the viewport.
+ const qreal safeDistance = - 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 = MarbleGlobal::getInstance()->profiles() & \
MarbleGlobal::SmallScreen; + int const finalTessellationPrecision = \
smallScreen ? 3 * tessellationPrecision : 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 = qMin<int>( distance / \
finalTessellationPrecision, maxTessellationNodes ); +
+ processTessellation( aCoords, bCoords,
+ tessellatedNodes,
+ polygons,
+ viewport,
+ f );
+ }
+ else {
+ crossHorizon( bCoords, polygons, viewport );
+ }
+#ifdef SAFE_DISTANCE
+ }
+#endif
+}
+
+
+void AzimuthalProjectionPrivate::processTessellation( const GeoDataCoordinates \
&previousCoords, + const \
GeoDataCoordinates ¤tCoords, + \
int tessellatedNodes, + \
QVector<QPolygonF*> &polygons, + \
const ViewportParams *viewport, + \
TessellationFlags f) const +{
+
+ const bool clampToGround = f.testFlag( FollowGround );
+ const bool followLatitudeCircle = f.testFlag( RespectLatitudeCircle )
+ && previousCoords.latitude() == \
currentCoords.latitude(); +
+ // Calculate steps for tessellation: lonDiff and altDiff
+ qreal lonDiff = 0.0;
+ if ( followLatitudeCircle ) {
+ const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
+ const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
+
+ lonDiff = currentCoords.longitude() - previousCoords.longitude();
+ if ( previousSign != currentSign
+ && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > \
M_PI ) { + if ( previousSign > currentSign ) {
+ // going eastwards ->
+ lonDiff += 2 * M_PI ;
+ } else {
+ // going westwards ->
+ lonDiff -= 2 * M_PI;
+ }
+ }
+ }
+
+ const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
+
+ // Create the tessellation nodes.
+ GeoDataCoordinates previousTessellatedCoords = previousCoords;
+ for ( int i = 1; i <= tessellatedNodes; ++i ) {
+ const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 );
+
+ // interpolate the altitude, too
+ const qreal altitude = clampToGround ? 0 : altDiff * t + \
previousCoords.altitude(); +
+ qreal lon = 0.0;
+ qreal lat = 0.0;
+ if ( followLatitudeCircle ) {
+ // To tessellate along latitude circles use the
+ // linear interpolation of the longitude.
+ lon = lonDiff * t + previousCoords.longitude();
+ lat = previousTessellatedCoords.latitude();
+ }
+ else {
+ // To tessellate along great circles use the
+ // normalized linear interpolation ("NLERP") for latitude and longitude.
+ const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), \
currentCoords.quaternion(), t ); + itpos. getSpherical( lon, lat );
+ }
+
+ const GeoDataCoordinates currentTessellatedCoords( lon, lat, altitude );
+ crossHorizon( currentTessellatedCoords, polygons, viewport );
+ previousTessellatedCoords = currentTessellatedCoords;
+ }
+
+ // For the clampToGround case add the "current" coordinate after adding all \
other nodes. + GeoDataCoordinates currentModifiedCoords( currentCoords );
+ if ( clampToGround ) {
+ currentModifiedCoords.setAltitude( 0.0 );
+ }
+ crossHorizon( currentModifiedCoords, polygons, viewport );
+}
+
+void AzimuthalProjectionPrivate::crossHorizon( const GeoDataCoordinates & bCoord,
+ QVector<QPolygonF*> &polygons,
+ const ViewportParams *viewport ) 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 = new QPolygonF;
+ polygons.append( path );
+ }
+ }
+}
+
+bool AzimuthalProjectionPrivate::lineStringToPolygon( const GeoDataLineString \
&lineString, + const ViewportParams \
*viewport, + QVector<QPolygonF *> \
&polygons ) const +{
+ Q_Q( const AzimuthalProjection );
+
+ const TessellationFlags f = lineString.tessellationFlags();
+
+ qreal x = 0;
+ qreal y = 0;
+ bool globeHidesPoint = false;
+
+ qreal previousX = -1.0;
+ qreal previousY = -1.0;
+ bool previousGlobeHidesPoint = false;
+
+ qreal horizonX = -1.0;
+ qreal horizonY = -1.0;
+
+ polygons.append( new QPolygonF );
+
+ GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
+ GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
+
+ // 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 behind 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 = false;
+ GeoDataCoordinates horizonDisappearCoords;
+
+ // If the first horizon crossing in a line string describes the appearance 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 \
reached + // it needs to be connected to the orphan.
+ bool horizonOrphan = false;
+ GeoDataCoordinates horizonOrphanCoords;
+
+ GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
+ GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
+
+ bool processingLastNode = false;
+
+ // We use a while loop to be able to cover linestrings as well as linear rings:
+ // Linear rings require to tessellate the path from the last node to the first \
node + // which isn't really convenient to achieve with a for loop ...
+
+ const bool isLong = lineString.size() > 50;
+ const int maximumDetail = ( viewport->radius() > 5000 ) ? 5 :
+ ( viewport->radius() > 2500 ) ? 4 :
+ ( viewport->radius() > 1000 ) ? 3 :
+ ( viewport->radius() > 600 ) ? 2 :
+ ( viewport->radius() > 50 ) ? 1 :
+ 0;
+
+ while ( itCoords != itEnd )
+ {
+
+ // Optimization for line strings with a big amount of nodes
+ bool skipNode = itCoords != itBegin && isLong && !processingLastNode &&
+ ( (*itCoords).detail() > maximumDetail
+ || viewport->resolves( *itPreviousCoords, *itCoords ) );
+
+ if ( !skipNode ) {
+
+ q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoint );
+
+ // Initializing variables that store the values of the previous \
iteration + if ( !processingLastNode && itCoords == itBegin ) {
+ previousGlobeHidesPoint = globeHidesPoint;
+ itPreviousCoords = itCoords;
+ previousX = x;
+ previousY = y;
+ }
+
+ // Check for the "horizon case" (which is present e.g. for the spherical \
projection + const bool isAtHorizon = ( globeHidesPoint || \
previousGlobeHidesPoint ) && + ( globeHidesPoint \
!= previousGlobeHidesPoint ); +
+ if ( isAtHorizon ) {
+ // Handle the "horizon case"
+ horizonCoords = findHorizon( *itPreviousCoords, *itCoords, viewport, \
f ); +
+ if ( lineString.isClosed() ) {
+ if ( horizonPair ) {
+ horizonToPolygon( viewport, horizonDisappearCoords, \
horizonCoords, polygons.last() ); + horizonPair = false;
+ }
+ else {
+ if ( globeHidesPoint ) {
+ horizonDisappearCoords = horizonCoords;
+ horizonPair = true;
+ }
+ else {
+ horizonOrphanCoords = horizonCoords;
+ horizonOrphan = true;
+ }
+ }
+ }
+
+ q->screenCoordinates( horizonCoords, viewport, horizonX, horizonY );
+
+ // If the line appears on the visible half we need
+ // to add an interpolated point at the horizon as the previous \
point. + if ( previousGlobeHidesPoint ) {
+ *polygons.last() << QPointF( horizonX, horizonY );
+ }
+ }
+
+ // This if-clause contains the section that tessellates the line
+ // segments of a linestring. If you are about to learn how the code of
+ // this class works you can safely ignore this section for a start.
+
+ if ( lineString.tessellate() /* && ( isVisible || previousIsVisible ) */ \
) { +
+ if ( !isAtHorizon ) {
+
+ tessellateLineSegment( *itPreviousCoords, previousX, previousY,
+ *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, horizonY,
+ *itCoords, x, y,
+ polygons, viewport,
+ f );
+ }
+ else {
+ tessellateLineSegment( *itPreviousCoords, previousX, \
previousY, + horizonCoords, horizonX, \
horizonY, + 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 = globeHidesPoint;
+ itPreviousCoords = itCoords;
+ previousX = x;
+ previousY = y;
+ }
+
+ // Here we modify the condition to be able to process the
+ // first node after the last node in a LinearRing.
+
+ if ( processingLastNode ) {
+ break;
+ }
+ ++itCoords;
+
+ if ( itCoords == itEnd && lineString.isClosed() ) {
+ itCoords = itBegin;
+ processingLastNode = true;
+ }
+ }
+
+ // In case of horizon crossings, make sure that we always get a
+ // polygon closed correctly.
+ if ( horizonOrphan && lineString.isClosed() ) {
+ horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, \
polygons.last() ); + }
+
+ if ( polygons.last()->size() <= 1 ){
+ polygons.pop_back(); // Clean up "unused" empty polygon instances
+ }
+
+ return polygons.isEmpty();
+}
+
+void AzimuthalProjectionPrivate::horizonToPolygon( const ViewportParams *viewport,
+ const GeoDataCoordinates & \
disappearCoords, + const GeoDataCoordinates \
& reappearCoords, + QPolygonF * polygon ) \
const +{
+ qreal x, y;
+
+ const qreal imageHalfWidth = viewport->width() / 2;
+ const qreal imageHalfHeight = viewport->height() / 2;
+
+ bool dummyGlobeHidesPoint = false;
+
+ Q_Q( const AzimuthalProjection );
+ // Calculate the angle of the position vectors of both coordinates
+ q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHidesPoint );
+ qreal alpha = atan2( y - imageHalfHeight,
+ x - imageHalfWidth );
+
+ q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesPoint );
+ qreal beta = atan2( y - imageHalfHeight,
+ x - imageHalfWidth );
+
+ // Calculate the difference between both
+ qreal diff = GeoDataCoordinates::normalizeLon( beta - alpha );
+
+ qreal sgndiff = diff < 0 ? -1 : 1;
+
+ const qreal arcradius = q->clippingRadius() * viewport->radius();
+ const int itEnd = fabs(diff * RAD2DEG);
+
+ // Create a polygon that resembles an arc between the two position vectors
+ for ( int it = 1; it <= itEnd; ++it ) {
+ const qreal angle = alpha + DEG2RAD * sgndiff * it;
+ const qreal itx = imageHalfWidth + arcradius * cos( angle );
+ const qreal ity = imageHalfHeight + arcradius * sin( angle );
+ *polygon << QPointF( itx, ity );
+ }
+}
+
+
+GeoDataCoordinates AzimuthalProjectionPrivate::findHorizon( const GeoDataCoordinates \
& previousCoords, + const \
GeoDataCoordinates & currentCoords, + \
const ViewportParams *viewport, + \
TessellationFlags f, + int \
recursionCounter ) const +{
+ bool currentHide = globeHidesPoint( currentCoords, viewport ) ;
+
+ if ( recursionCounter > 20 ) {
+ return currentHide ? previousCoords : currentCoords;
+ }
+ ++recursionCounter;
+
+ bool followLatitudeCircle = false;
+
+ // Calculate steps for tessellation: lonDiff and altDiff
+ qreal lonDiff = 0.0;
+ qreal previousLongitude = 0.0;
+ qreal previousLatitude = 0.0;
+
+ if ( f.testFlag( RespectLatitudeCircle ) ) {
+ previousCoords.geoCoordinates( previousLongitude, previousLatitude );
+ qreal previousSign = previousLongitude > 0 ? 1 : -1;
+
+ qreal currentLongitude = 0.0;
+ qreal currentLatitude = 0.0;
+ currentCoords.geoCoordinates( currentLongitude, currentLatitude );
+ qreal currentSign = currentLongitude > 0 ? 1 : -1;
+
+ if ( previousLatitude == currentLatitude ) {
+ followLatitudeCircle = true;
+
+ lonDiff = currentLongitude - previousLongitude;
+ if ( previousSign != currentSign
+ && fabs(previousLongitude) + fabs(currentLongitude) > M_PI ) {
+ if ( previousSign > currentSign ) {
+ // going eastwards ->
+ lonDiff += 2 * M_PI ;
+ } else {
+ // going westwards ->
+ lonDiff -= 2 * M_PI;
+ }
+ }
+
+ }
+ else {
+// mDebug() << "Don't FollowLatitudeCircle";
+ }
+ }
+
+ qreal lon = 0.0;
+ qreal lat = 0.0;
+
+ qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
+
+ if ( followLatitudeCircle ) {
+ // To tessellate along latitude circles use the
+ // linear interpolation of the longitude.
+ lon = lonDiff * 0.5 + previousLongitude;
+ lat = previousLatitude;
+ }
+ else {
+ // To tessellate along great circles use the
+ // normalized linear interpolation ("NLERP") for latitude and longitude.
+ const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), \
currentCoords.quaternion(), 0.5 ); + itpos. getSpherical( lon, lat );
+ }
+
+ qreal altitude = previousCoords.altitude() + 0.5 * altDiff;
+
+ GeoDataCoordinates horizonCoords( lon, lat, altitude );
+
+ bool horizonHide = globeHidesPoint( horizonCoords, viewport );
+
+ if ( horizonHide != currentHide ) {
+ return findHorizon( horizonCoords, currentCoords, viewport, f, \
recursionCounter ); + }
+
+ return findHorizon( previousCoords, horizonCoords, viewport, f, recursionCounter \
); +}
+
+
+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, globeHidesPoint);
+ return globeHidesPoint;
+}
+
+
}
diff --git a/src/lib/marble/projections/AzimuthalProjection.h \
b/src/lib/marble/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 NoPreservation; }
+ virtual bool isClippedToSphere() const;
+
+ virtual qreal clippingRadius() const;
+
+ bool mapCoversViewport( const ViewportParams *viewport ) const;
+
+ virtual bool screenCoordinates( const GeoDataLineString &lineString,
+ const ViewportParams *viewport,
+ QVector<QPolygonF*> &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/marble/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 segment
+ // 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<QPolygonF*> &polygons,
+ const ViewportParams *viewport,
+ TessellationFlags f = 0 ) const;
+
+ void processTessellation( const GeoDataCoordinates &previousCoords,
+ const GeoDataCoordinates ¤tCoords,
+ int count,
+ QVector<QPolygonF*> &polygons,
+ const ViewportParams *viewport,
+ TessellationFlags f = 0 ) const;
+
+ void crossHorizon( const GeoDataCoordinates & bCoord,
+ QVector<QPolygonF*> &polygons,
+ const ViewportParams *viewport ) const;
+
+ virtual bool lineStringToPolygon( const GeoDataLineString &lineString,
+ const ViewportParams *viewport,
+ QVector<QPolygonF*> &polygons ) const;
+
+ void horizonToPolygon( const ViewportParams *viewport,
+ const GeoDataCoordinates & disappearCoords,
+ const GeoDataCoordinates & reappearCoords,
+ QPolygonF* ) const;
+
+ GeoDataCoordinates findHorizon( const GeoDataCoordinates & previousCoords,
+ const GeoDataCoordinates & currentCoords,
+ const ViewportParams *viewport,
+ TessellationFlags f = 0,
+ int recursionCounter = 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/marble/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 \
AzimuthalProjectionPrivate public:
explicit GnomonicProjectionPrivate( GnomonicProjection * parent );
- // This method tessellates a line segment in a way that the line segment
- // 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<QPolygonF*> &polygons,
- const ViewportParams *viewport,
- TessellationFlags f = 0 ) const;
-
- void processTessellation( const GeoDataCoordinates &previousCoords,
- const GeoDataCoordinates ¤tCoords,
- int count,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport,
- TessellationFlags f = 0 ) const;
-
- void crossHorizon( const GeoDataCoordinates & bCoord,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport ) const;
-
- virtual bool lineStringToPolygon( const GeoDataLineString &lineString,
- const ViewportParams *viewport,
- QVector<QPolygonF*> &polygons ) const;
-
- void horizonToPolygon( const ViewportParams *viewport,
- const GeoDataCoordinates & disappearCoords,
- const GeoDataCoordinates & reappearCoords,
- QPolygonF* ) const;
-
- GeoDataCoordinates findHorizon( const GeoDataCoordinates & previousCoords,
- const GeoDataCoordinates & currentCoords,
- const ViewportParams *viewport,
- TessellationFlags f = 0,
- int recursionCounter = 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 &coordinates,
@@ -126,10 +87,9 @@ bool GnomonicProjection::screenCoordinates( const \
GeoDataCoordinates &coordinate x *= 2 * viewport->radius() / M_PI;
y *= 2 * viewport->radius() / M_PI;
- const qint64 radius = viewport->radius();
+ const qint64 radius = 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 = true;
return false;
}
@@ -185,7 +145,7 @@ bool GnomonicProjection::geoCoordinates( const int x, const int \
y, const qreal centerLat = viewport->centerLatitude();
const qreal rx = ( - viewport->width() / 2 + x ) / rad2Pixel;
const qreal ry = ( viewport->height() / 2 - y ) / rad2Pixel;
- const qreal p = qSqrt( rx*rx + ry*ry );
+ const qreal p = qMax( qSqrt( rx*rx + ry*ry ), 0.0001 ); // ensure we don't \
divide by zero const qreal c = qAtan( p );
const qreal sinc = 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 &lineString,
- const ViewportParams *viewport,
- QVector<QPolygonF *> &polygons ) \
const
-{
-
- Q_D( const GnomonicProjection );
- // Compare bounding box size of the line string with the angularResolution
- // Immediately return if the latLonAltBox is smaller.
- if ( !viewport->resolves( lineString.latLonAltBox() ) ) {
-// mDebug() << "Object too small to be resolved";
- return false;
- }
-
- d->lineStringToPolygon( lineString, viewport, polygons );
- return true;
-}
-
-void GnomonicProjectionPrivate::tessellateLineSegment( const GeoDataCoordinates \
&aCoords,
- qreal ax, qreal ay,
- const GeoDataCoordinates &bCoords,
- qreal bx, qreal by,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport,
- 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 = 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 both points
- // are located on the same side relative to the viewport boundaries and if they \
are
- // located more than half the line segment distance away from the viewport.
- const qreal safeDistance = - 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 = MarbleGlobal::getInstance()->profiles() & \
MarbleGlobal::SmallScreen;
- int const finalTessellationPrecision = smallScreen ? 3 * \
tessellationPrecision : 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 = qMin<int>( distance / \
finalTessellationPrecision, maxTessellationNodes );
-
- processTessellation( aCoords, bCoords,
- tessellatedNodes,
- polygons,
- viewport,
- f );
- }
- else {
- crossHorizon( bCoords, polygons, viewport );
- }
-#ifdef SAFE_DISTANCE
- }
-#endif
-}
-
-
-void GnomonicProjectionPrivate::processTessellation( const GeoDataCoordinates \
&previousCoords,
- const GeoDataCoordinates \
¤tCoords,
- int tessellatedNodes,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport,
- TessellationFlags f) const
-{
-
- const bool clampToGround = f.testFlag( FollowGround );
- const bool followLatitudeCircle = f.testFlag( RespectLatitudeCircle )
- && previousCoords.latitude() == \
currentCoords.latitude();
-
- // Calculate steps for tessellation: lonDiff and altDiff
- qreal lonDiff = 0.0;
- if ( followLatitudeCircle ) {
- const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
- const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
-
- lonDiff = currentCoords.longitude() - previousCoords.longitude();
- if ( previousSign != currentSign
- && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > \
M_PI ) {
- if ( previousSign > currentSign ) {
- // going eastwards ->
- lonDiff += 2 * M_PI ;
- } else {
- // going westwards ->
- lonDiff -= 2 * M_PI;
- }
- }
- }
-
- const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
-
- // Create the tessellation nodes.
- GeoDataCoordinates previousTessellatedCoords = previousCoords;
- for ( int i = 1; i <= tessellatedNodes; ++i ) {
- const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 );
-
- // interpolate the altitude, too
- const qreal altitude = clampToGround ? 0 : altDiff * t + \
previousCoords.altitude();
-
- qreal lon = 0.0;
- qreal lat = 0.0;
- if ( followLatitudeCircle ) {
- // To tessellate along latitude circles use the
- // linear interpolation of the longitude.
- lon = lonDiff * t + previousCoords.longitude();
- lat = previousTessellatedCoords.latitude();
- }
- else {
- // To tessellate along great circles use the
- // normalized linear interpolation ("NLERP") for latitude and longitude.
- const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), \
currentCoords.quaternion(), t );
- itpos. getSpherical( lon, lat );
- }
-
- const GeoDataCoordinates currentTessellatedCoords( lon, lat, altitude );
- crossHorizon( currentTessellatedCoords, polygons, viewport );
- previousTessellatedCoords = currentTessellatedCoords;
- }
-
- // For the clampToGround case add the "current" coordinate after adding all \
other nodes.
- GeoDataCoordinates currentModifiedCoords( currentCoords );
- if ( clampToGround ) {
- currentModifiedCoords.setAltitude( 0.0 );
- }
- crossHorizon( currentModifiedCoords, polygons, viewport );
-}
-
-void GnomonicProjectionPrivate::crossHorizon( const GeoDataCoordinates & bCoord,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport ) 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 = new QPolygonF;
- polygons.append( path );
- }
- }
-}
-
-bool GnomonicProjectionPrivate::lineStringToPolygon( const GeoDataLineString \
&lineString,
- const ViewportParams *viewport,
- QVector<QPolygonF *> &polygons ) const
-{
- Q_Q( const GnomonicProjection );
-
- const TessellationFlags f = lineString.tessellationFlags();
-
- qreal x = 0;
- qreal y = 0;
- bool globeHidesPoint = false;
-
- qreal previousX = -1.0;
- qreal previousY = -1.0;
- bool previousGlobeHidesPoint = false;
-
- qreal horizonX = -1.0;
- qreal horizonY = -1.0;
-
- polygons.append( new QPolygonF );
-
- GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
- GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
-
- // 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 behind 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 = false;
- GeoDataCoordinates horizonDisappearCoords;
-
- // If the first horizon crossing in a line string describes the appearance 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 reached
- // it needs to be connected to the orphan.
- bool horizonOrphan = false;
- GeoDataCoordinates horizonOrphanCoords;
-
- GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
- GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
-
- bool processingLastNode = false;
-
- // We use a while loop to be able to cover linestrings as well as linear rings:
- // Linear rings require to tessellate the path from the last node to the first \
node
- // which isn't really convenient to achieve with a for loop ...
-
- const bool isLong = lineString.size() > 50;
- const int maximumDetail = ( viewport->radius() > 5000 ) ? 5 :
- ( viewport->radius() > 2500 ) ? 4 :
- ( viewport->radius() > 1000 ) ? 3 :
- ( viewport->radius() > 600 ) ? 2 :
- ( viewport->radius() > 50 ) ? 1 :
- 0;
-
- while ( itCoords != itEnd )
- {
-
- // Optimization for line strings with a big amount of nodes
- bool skipNode = itCoords != itBegin && isLong && !processingLastNode &&
- ( (*itCoords).detail() > maximumDetail
- || viewport->resolves( *itPreviousCoords, *itCoords ) );
-
- if ( !skipNode ) {
-
- q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoint );
-
- // Initializing variables that store the values of the previous \
iteration
- if ( !processingLastNode && itCoords == itBegin ) {
- previousGlobeHidesPoint = globeHidesPoint;
- itPreviousCoords = itCoords;
- previousX = x;
- previousY = y;
- }
-
- // Check for the "horizon case" (which is present e.g. for the spherical \
projection
- const bool isAtHorizon = ( globeHidesPoint || previousGlobeHidesPoint ) \
&&
- ( globeHidesPoint != previousGlobeHidesPoint \
);
-
- if ( isAtHorizon ) {
- // Handle the "horizon case"
- horizonCoords = findHorizon( *itPreviousCoords, *itCoords, viewport, \
f );
-
- if ( lineString.isClosed() ) {
- if ( horizonPair ) {
- horizonToPolygon( viewport, horizonDisappearCoords, \
horizonCoords, polygons.last() );
- horizonPair = false;
- }
- else {
- if ( globeHidesPoint ) {
- horizonDisappearCoords = horizonCoords;
- horizonPair = true;
- }
- else {
- horizonOrphanCoords = horizonCoords;
- horizonOrphan = true;
- }
- }
- }
-
- q->screenCoordinates( horizonCoords, viewport, horizonX, horizonY );
-
- // If the line appears on the visible half we need
- // to add an interpolated point at the horizon as the previous \
point.
- if ( previousGlobeHidesPoint ) {
- *polygons.last() << QPointF( horizonX, horizonY );
- }
- }
-
- // This if-clause contains the section that tessellates the line
- // segments of a linestring. If you are about to learn how the code of
- // this class works you can safely ignore this section for a start.
-
- if ( lineString.tessellate() /* && ( isVisible || previousIsVisible ) */ \
) {
-
- if ( !isAtHorizon ) {
-
- tessellateLineSegment( *itPreviousCoords, previousX, previousY,
- *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, horizonY,
- *itCoords, x, y,
- polygons, viewport,
- f );
- }
- else {
- tessellateLineSegment( *itPreviousCoords, previousX, \
previousY,
- horizonCoords, horizonX, horizonY,
- 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 = globeHidesPoint;
- itPreviousCoords = itCoords;
- previousX = x;
- previousY = y;
- }
-
- // Here we modify the condition to be able to process the
- // first node after the last node in a LinearRing.
-
- if ( processingLastNode ) {
- break;
- }
- ++itCoords;
-
- if ( itCoords == itEnd && lineString.isClosed() ) {
- itCoords = itBegin;
- processingLastNode = true;
- }
- }
-
- // In case of horizon crossings, make sure that we always get a
- // polygon closed correctly.
- if ( horizonOrphan && lineString.isClosed() ) {
- horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, \
polygons.last() );
- }
-
- if ( polygons.last()->size() <= 1 ){
- polygons.pop_back(); // Clean up "unused" empty polygon instances
- }
-
- return polygons.isEmpty();
-}
-
-void GnomonicProjectionPrivate::horizonToPolygon( const ViewportParams *viewport,
- const GeoDataCoordinates & \
disappearCoords,
- const GeoDataCoordinates & \
reappearCoords,
- QPolygonF * polygon ) const
-{
- qreal x, y;
-
- const qreal imageHalfWidth = viewport->width() / 2;
- const qreal imageHalfHeight = viewport->height() / 2;
-
- bool dummyGlobeHidesPoint = false;
-
- Q_Q( const GnomonicProjection );
- // Calculate the angle of the position vectors of both coordinates
- q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHidesPoint );
- qreal alpha = atan2( y - imageHalfHeight,
- x - imageHalfWidth );
-
- q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesPoint );
- qreal beta = atan2( y - imageHalfHeight,
- x - imageHalfWidth );
-
- // Calculate the difference between both
- qreal diff = GeoDataCoordinates::normalizeLon( beta - alpha );
-
- qreal sgndiff = diff < 0 ? -1 : 1;
-
- const qreal arcradius = 10 * viewport->radius();
- const int itEnd = fabs(diff * RAD2DEG);
-
- // Create a polygon that resembles an arc between the two position vectors
- for ( int it = 1; it <= itEnd; ++it ) {
- const qreal angle = alpha + DEG2RAD * sgndiff * it;
- const qreal itx = imageHalfWidth + arcradius * cos( angle );
- const qreal ity = imageHalfHeight + arcradius * sin( angle );
- *polygon << QPointF( itx, ity );
- }
-}
-
-
-GeoDataCoordinates GnomonicProjectionPrivate::findHorizon( const GeoDataCoordinates \
& previousCoords,
- const GeoDataCoordinates & \
currentCoords,
- const ViewportParams *viewport,
- TessellationFlags f,
- int recursionCounter ) const
-{
- bool currentHide = globeHidesPoint( currentCoords, viewport ) ;
-
- if ( recursionCounter > 20 ) {
- return currentHide ? previousCoords : currentCoords;
- }
- ++recursionCounter;
-
- bool followLatitudeCircle = false;
-
- // Calculate steps for tessellation: lonDiff and altDiff
- qreal lonDiff = 0.0;
- qreal previousLongitude = 0.0;
- qreal previousLatitude = 0.0;
-
- if ( f.testFlag( RespectLatitudeCircle ) ) {
- previousCoords.geoCoordinates( previousLongitude, previousLatitude );
- qreal previousSign = previousLongitude > 0 ? 1 : -1;
-
- qreal currentLongitude = 0.0;
- qreal currentLatitude = 0.0;
- currentCoords.geoCoordinates( currentLongitude, currentLatitude );
- qreal currentSign = currentLongitude > 0 ? 1 : -1;
-
- if ( previousLatitude == currentLatitude ) {
- followLatitudeCircle = true;
-
- lonDiff = currentLongitude - previousLongitude;
- if ( previousSign != currentSign
- && fabs(previousLongitude) + fabs(currentLongitude) > M_PI ) {
- if ( previousSign > currentSign ) {
- // going eastwards ->
- lonDiff += 2 * M_PI ;
- } else {
- // going westwards ->
- lonDiff -= 2 * M_PI;
- }
- }
-
- }
- else {
-// mDebug() << "Don't FollowLatitudeCircle";
- }
- }
-
- qreal lon = 0.0;
- qreal lat = 0.0;
-
- qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
-
- if ( followLatitudeCircle ) {
- // To tessellate along latitude circles use the
- // linear interpolation of the longitude.
- lon = lonDiff * 0.5 + previousLongitude;
- lat = previousLatitude;
- }
- else {
- // To tessellate along great circles use the
- // normalized linear interpolation ("NLERP") for latitude and longitude.
- const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), \
currentCoords.quaternion(), 0.5 );
- itpos. getSpherical( lon, lat );
- }
-
- qreal altitude = previousCoords.altitude() + 0.5 * altDiff;
-
- GeoDataCoordinates horizonCoords( lon, lat, altitude );
-
- bool horizonHide = globeHidesPoint( horizonCoords, viewport );
-
- if ( horizonHide != currentHide ) {
- return findHorizon( horizonCoords, currentCoords, viewport, f, \
recursionCounter );
- }
-
- return findHorizon( previousCoords, horizonCoords, viewport, f, recursionCounter \
);
-}
-
-
-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, globeHidesPoint);
- return globeHidesPoint;
-}
-
-QPainterPath GnomonicProjection::mapShape( const ViewportParams *viewport ) const
-{
- // Convenience variables
- int width = viewport->width();
- int height = 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/marble/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 coordinates \
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<QPolygonF*> &polygons ) const;
-
using AbstractProjection::screenCoordinates;
/**
@@ -76,10 +74,6 @@ class GnomonicProjection : public AzimuthalProjection
qreal& lon, qreal& lat,
GeoDataCoordinates::Unit unit = GeoDataCoordinates::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/marble/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 <ingwa@kde.org>
-// Copyright 2007-2012 Torsten Rahn <rahn@kde.org>
+// Copyright 2007-2014 Torsten Rahn <rahn@kde.org>
// Copyright 2009 Patrick Spendrin <ps_ml@gmx.de>
// Copyright 2012 Cezar Mocan <mocancezar@gmail.com>
//
@@ -33,59 +33,9 @@ namespace Marble
class SphericalProjectionPrivate : public AzimuthalProjectionPrivate
{
public:
- enum LineStringFlag {
- PlainLineString = 0x00,
- ClosedLineString = 0x01
- };
explicit SphericalProjectionPrivate( SphericalProjection * parent );
- // This method tessellates a line segment in a way that the line segment
- // 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<QPolygonF*> &polygons,
- const ViewportParams *viewport,
- TessellationFlags f = 0 ) const;
-
- void processTessellation( const GeoDataCoordinates &previousCoords,
- const GeoDataCoordinates ¤tCoords,
- int count,
- LineStringFlag lineStringFlag,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport,
- TessellationFlags f = 0 ) const;
-
- void crossHorizon( const GeoDataCoordinates & bCoord,
- LineStringFlag lineStringFlag,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport ) const;
-
- virtual bool lineStringToPolygon( const GeoDataLineString &lineString,
- const ViewportParams *viewport,
- QVector<QPolygonF*> &polygons ) const;
-
- void horizonToPolygon( const ViewportParams *viewport,
- const GeoDataCoordinates & disappearCoords,
- const GeoDataCoordinates & reappearCoords,
- QPolygonF* ) const;
-
- GeoDataCoordinates findHorizon( const GeoDataCoordinates & previousCoords,
- const GeoDataCoordinates & currentCoords,
- const ViewportParams *viewport,
- TessellationFlags f = 0,
- int recursionCounter = 0 ) const;
-
- static bool globeHidesPoint( const GeoDataCoordinates &coordinates,
- const ViewportParams *viewport );
-
Q_DECLARE_PUBLIC( SphericalProjection )
};
@@ -107,11 +57,9 @@ SphericalProjection::~SphericalProjection()
{
}
-
SphericalProjectionPrivate::SphericalProjectionPrivate( SphericalProjection * parent \
) : AzimuthalProjectionPrivate( parent )
{
-
}
bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates,
@@ -214,541 +162,4 @@ bool SphericalProjection::geoCoordinates( const int x, const \
int y, return true;
}
-bool SphericalProjection::mapCoversViewport( const ViewportParams *viewport ) const
-{
- qint64 radius = viewport->radius();
- qint64 width = viewport->width();
- qint64 height = 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 >= width * width + height * height )
- return true;
-
- return false;
-}
-
-bool SphericalProjection::screenCoordinates( const GeoDataLineString &lineString,
- const ViewportParams *viewport,
- QVector<QPolygonF *> &polygons ) \
const
-{
-
- Q_D( const SphericalProjection );
- // Compare bounding box size of the line string with the angularResolution
- // Immediately return if the latLonAltBox is smaller.
- if ( !viewport->resolves( lineString.latLonAltBox() ) ) {
-// mDebug() << "Object too small to be resolved";
- return false;
- }
-
- d->lineStringToPolygon( lineString, viewport, polygons );
- return true;
-}
-
-void SphericalProjectionPrivate::tessellateLineSegment( const GeoDataCoordinates \
&aCoords,
- qreal ax, qreal ay,
- const GeoDataCoordinates &bCoords,
- qreal bx, qreal by,
- LineStringFlag lineStringFlag,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport,
- 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 = 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 both points
- // are located on the same side relative to the viewport boundaries and if they \
are
- // located more than half the line segment distance away from the viewport.
- const qreal safeDistance = - 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 = MarbleGlobal::getInstance()->profiles() & \
MarbleGlobal::SmallScreen;
- int const finalTessellationPrecision = smallScreen ? 3 * \
tessellationPrecision : 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 = qMin<int>( distance / \
finalTessellationPrecision, maxTessellationNodes );
-
- processTessellation( aCoords, bCoords,
- tessellatedNodes,
- lineStringFlag,
- polygons,
- viewport,
- f );
- }
- else {
- crossHorizon( bCoords, lineStringFlag, polygons, viewport );
- }
-#ifdef SAFE_DISTANCE
- }
-#endif
-}
-
-
-void SphericalProjectionPrivate::processTessellation( const GeoDataCoordinates \
&previousCoords,
- const GeoDataCoordinates \
¤tCoords,
- int tessellatedNodes,
- LineStringFlag lineStringFlag,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport,
- TessellationFlags f) const
-{
-
- const bool clampToGround = f.testFlag( FollowGround );
- const bool followLatitudeCircle = f.testFlag( RespectLatitudeCircle )
- && previousCoords.latitude() == \
currentCoords.latitude();
-
- // Calculate steps for tessellation: lonDiff and altDiff
- qreal lonDiff = 0.0;
- if ( followLatitudeCircle ) {
- const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
- const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
-
- lonDiff = currentCoords.longitude() - previousCoords.longitude();
- if ( previousSign != currentSign
- && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > \
M_PI ) {
- if ( previousSign > currentSign ) {
- // going eastwards ->
- lonDiff += 2 * M_PI ;
- } else {
- // going westwards ->
- lonDiff -= 2 * M_PI;
- }
- }
- }
-
- const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
-
- // Create the tessellation nodes.
- GeoDataCoordinates previousTessellatedCoords = previousCoords;
- for ( int i = 1; i <= tessellatedNodes; ++i ) {
- const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 );
-
- // interpolate the altitude, too
- const qreal altitude = clampToGround ? 0 : altDiff * t + \
previousCoords.altitude();
-
- qreal lon = 0.0;
- qreal lat = 0.0;
- if ( followLatitudeCircle ) {
- // To tessellate along latitude circles use the
- // linear interpolation of the longitude.
- lon = lonDiff * t + previousCoords.longitude();
- lat = previousTessellatedCoords.latitude();
- }
- else {
- // To tessellate along great circles use the
- // normalized linear interpolation ("NLERP") for latitude and longitude.
- const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), \
currentCoords.quaternion(), t );
- itpos. getSpherical( lon, lat );
- }
-
- const GeoDataCoordinates currentTessellatedCoords( lon, lat, altitude );
- crossHorizon( currentTessellatedCoords, lineStringFlag, polygons, viewport \
);
- previousTessellatedCoords = currentTessellatedCoords;
- }
-
- // For the clampToGround case add the "current" coordinate after adding all \
other nodes.
- GeoDataCoordinates currentModifiedCoords( currentCoords );
- if ( clampToGround ) {
- currentModifiedCoords.setAltitude( 0.0 );
- }
- crossHorizon( currentModifiedCoords, lineStringFlag, polygons, viewport );
-}
-
-void SphericalProjectionPrivate::crossHorizon( const GeoDataCoordinates & bCoord,
- LineStringFlag lineStringFlag,
- QVector<QPolygonF*> &polygons,
- const ViewportParams *viewport ) 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 != ClosedLineString ) {
- QPolygonF *path = new QPolygonF;
- polygons.append( path );
- }
- }
-}
-
-bool SphericalProjectionPrivate::lineStringToPolygon( const GeoDataLineString \
&lineString,
- const ViewportParams *viewport,
- QVector<QPolygonF *> &polygons ) const
-{
- Q_Q( const SphericalProjection );
-
- const TessellationFlags f = lineString.tessellationFlags();
-
- qreal x = 0;
- qreal y = 0;
- bool globeHidesPoint = false;
-
- qreal previousX = -1.0;
- qreal previousY = -1.0;
- bool previousGlobeHidesPoint = false;
-
- qreal horizonX = -1.0;
- qreal horizonY = -1.0;
-
- polygons.append( new QPolygonF );
-
- GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
- GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
-
- // 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 behind 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 = false;
- GeoDataCoordinates horizonDisappearCoords;
-
- // If the first horizon crossing in a line string describes the appearance 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 reached
- // it needs to be connected to the orphan.
- bool horizonOrphan = false;
- GeoDataCoordinates horizonOrphanCoords;
-
- GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
- GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
-
- bool processingLastNode = false;
-
- // We use a while loop to be able to cover linestrings as well as linear rings:
- // Linear rings require to tessellate the path from the last node to the first \
node
- // which isn't really convenient to achieve with a for loop ...
-
- const bool isLong = lineString.size() > 50;
- const int maximumDetail = ( viewport->radius() > 5000 ) ? 5 :
- ( viewport->radius() > 2500 ) ? 4 :
- ( viewport->radius() > 1000 ) ? 3 :
- ( viewport->radius() > 600 ) ? 2 :
- ( viewport->radius() > 50 ) ? 1 :
- 0;
-
- while ( itCoords != itEnd )
- {
-
- // Optimization for line strings with a big amount of nodes
- bool skipNode = itCoords != itBegin && isLong && !processingLastNode &&
- ( (*itCoords).detail() > maximumDetail
- || viewport->resolves( *itPreviousCoords, *itCoords ) );
-
- if ( !skipNode ) {
-
- q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoint );
-
- // Initializing variables that store the values of the previous \
iteration
- if ( !processingLastNode && itCoords == itBegin ) {
- previousGlobeHidesPoint = globeHidesPoint;
- itPreviousCoords = itCoords;
- previousX = x;
- previousY = y;
- }
-
- // Check for the "horizon case" (which is present e.g. for the spherical \
projection
- const bool isAtHorizon = ( globeHidesPoint || previousGlobeHidesPoint ) \
&&
- ( globeHidesPoint != previousGlobeHidesPoint \
);
-
- if ( isAtHorizon ) {
- // Handle the "horizon case"
- horizonCoords = findHorizon( *itPreviousCoords, *itCoords, viewport, \
f );
-
- if ( lineString.isClosed() ) {
- if ( horizonPair ) {
- horizonToPolygon( viewport, horizonDisappearCoords, \
horizonCoords, polygons.last() );
- horizonPair = false;
- }
- else {
- if ( globeHidesPoint ) {
- horizonDisappearCoords = horizonCoords;
- horizonPair = true;
- }
- else {
- horizonOrphanCoords = horizonCoords;
- horizonOrphan = true;
- }
- }
- }
-
- q->screenCoordinates( horizonCoords, viewport, horizonX, horizonY );
-
- // If the line appears on the visible half we need
- // to add an interpolated point at the horizon as the previous \
point.
- if ( previousGlobeHidesPoint ) {
- *polygons.last() << QPointF( horizonX, horizonY );
- }
- }
-
- // This if-clause contains the section that tessellates the line
- // segments of a linestring. If you are about to learn how the code of
- // this class works you can safely ignore this section for a start.
-
- if ( lineString.tessellate() ) {
- LineStringFlag lineStringFlag = lineString.isClosed() ? \
ClosedLineString : PlainLineString;
- if ( !isAtHorizon ) {
- tessellateLineSegment( *itPreviousCoords, previousX, previousY,
- *itCoords, x, y,
- lineStringFlag, 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, horizonY,
- *itCoords, x, y,
- lineStringFlag, polygons, viewport,
- f );
- }
- else {
- tessellateLineSegment( *itPreviousCoords, previousX, \
previousY,
- horizonCoords, horizonX, horizonY,
- lineStringFlag, 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 = globeHidesPoint;
- itPreviousCoords = itCoords;
- previousX = x;
- previousY = y;
- }
-
- // Here we modify the condition to be able to process the
- // first node after the last node in a LinearRing.
-
- if ( processingLastNode ) {
- break;
- }
- ++itCoords;
-
- if ( itCoords == itEnd && lineString.isClosed() ) {
- itCoords = itBegin;
- processingLastNode = true;
- }
- }
-
- // In case of horizon crossings, make sure that we always get a
- // polygon closed correctly.
- if ( horizonOrphan && lineString.isClosed() ) {
- horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, \
polygons.last() );
- }
-
- if ( polygons.last()->size() <= 1 ){
- polygons.pop_back(); // Clean up "unused" empty polygon instances
- }
-
- return polygons.isEmpty();
-}
-
-void SphericalProjectionPrivate::horizonToPolygon( const ViewportParams *viewport,
- const GeoDataCoordinates & \
disappearCoords,
- const GeoDataCoordinates & \
reappearCoords,
- QPolygonF * polygon ) const
-{
- qreal x, y;
-
- const qreal imageHalfWidth = viewport->width() / 2;
- const qreal imageHalfHeight = viewport->height() / 2;
-
- bool dummyGlobeHidesPoint = false;
-
- Q_Q( const SphericalProjection );
- // Calculate the angle of the position vectors of both coordinates
- q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHidesPoint );
- qreal alpha = atan2( y - imageHalfHeight,
- x - imageHalfWidth );
-
- q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesPoint );
- qreal beta = atan2( y - imageHalfHeight,
- x - imageHalfWidth );
-
- // Calculate the difference between both
- qreal diff = GeoDataCoordinates::normalizeLon( beta - alpha );
-
- qreal sgndiff = diff < 0 ? -1 : 1;
-
- const qreal arcradius = viewport->radius();
- const int itEnd = fabs(diff * RAD2DEG);
-
- // Create a polygon that resembles an arc between the two position vectors
- for ( int it = 1; it <= itEnd; ++it ) {
- const qreal angle = alpha + DEG2RAD * sgndiff * it;
- const qreal itx = imageHalfWidth + arcradius * cos( angle );
- const qreal ity = imageHalfHeight + arcradius * sin( angle );
- *polygon << QPointF( itx, ity );
- }
-}
-
-
-GeoDataCoordinates SphericalProjectionPrivate::findHorizon( const GeoDataCoordinates \
& previousCoords,
- const GeoDataCoordinates & \
currentCoords,
- const ViewportParams *viewport,
- TessellationFlags f,
- int recursionCounter ) const
-{
- bool currentHide = globeHidesPoint( currentCoords, viewport ) ;
-
- if ( recursionCounter > 20 ) {
- return currentHide ? previousCoords : currentCoords;
- }
- ++recursionCounter;
-
- bool followLatitudeCircle = false;
-
- // Calculate steps for tessellation: lonDiff and altDiff
- qreal lonDiff = 0.0;
- qreal previousLongitude = 0.0;
- qreal previousLatitude = 0.0;
-
- if ( f.testFlag( RespectLatitudeCircle ) ) {
- previousCoords.geoCoordinates( previousLongitude, previousLatitude );
- qreal previousSign = previousLongitude > 0 ? 1 : -1;
-
- qreal currentLongitude = 0.0;
- qreal currentLatitude = 0.0;
- currentCoords.geoCoordinates( currentLongitude, currentLatitude );
- qreal currentSign = currentLongitude > 0 ? 1 : -1;
-
- if ( previousLatitude == currentLatitude ) {
- followLatitudeCircle = true;
-
- lonDiff = currentLongitude - previousLongitude;
- if ( previousSign != currentSign
- && fabs(previousLongitude) + fabs(currentLongitude) > M_PI ) {
- if ( previousSign > currentSign ) {
- // going eastwards ->
- lonDiff += 2 * M_PI ;
- } else {
- // going westwards ->
- lonDiff -= 2 * M_PI;
- }
- }
-
- }
- else {
-// mDebug() << "Don't FollowLatitudeCircle";
- }
- }
-
- qreal lon = 0.0;
- qreal lat = 0.0;
-
- qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
-
- if ( followLatitudeCircle ) {
- // To tessellate along latitude circles use the
- // linear interpolation of the longitude.
- lon = lonDiff * 0.5 + previousLongitude;
- lat = previousLatitude;
- }
- else {
- // To tessellate along great circles use the
- // normalized linear interpolation ("NLERP") for latitude and longitude.
- const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), \
currentCoords.quaternion(), 0.5 );
- itpos. getSpherical( lon, lat );
- }
-
- qreal altitude = previousCoords.altitude() + 0.5 * altDiff;
-
- GeoDataCoordinates horizonCoords( lon, lat, altitude );
-
- bool horizonHide = globeHidesPoint( horizonCoords, viewport );
-
- if ( horizonHide != currentHide ) {
- return findHorizon( horizonCoords, currentCoords, viewport, f, \
recursionCounter );
- }
-
- return findHorizon( previousCoords, horizonCoords, viewport, f, recursionCounter \
);
-}
-
-
-bool SphericalProjectionPrivate::globeHidesPoint( const GeoDataCoordinates \
&coordinates,
- const ViewportParams *viewport )
-{
- qreal absoluteAltitude = coordinates.altitude() + EARTH_RADIUS;
- Quaternion qpos = coordinates.quaternion();
-
- qpos.rotateAroundAxis( viewport->planetAxisMatrix() );
-
- qreal pixelAltitude = ( ( 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 = pixelAltitude * qpos.v[Q_X];
- qreal earthCenteredY = pixelAltitude * qpos.v[Q_Y];
- qreal radius = viewport->radius();
-
- // Don't draw high placemarks (e.g. satellites) that aren't visible.
- 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/marble/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<QPolygonF*> &polygons ) const;
-
using AbstractProjection::screenCoordinates;
/**
@@ -85,8 +81,6 @@ class SphericalProjection : public AzimuthalProjection
qreal& lon, qreal& lat,
GeoDataCoordinates::Unit unit = GeoDataCoordinates::Degree \
) const;
- bool mapCoversViewport( const ViewportParams *viewport ) const;
-
protected:
SphericalProjection(SphericalProjectionPrivate *dd );
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic