8#include "AzimuthalProjection_p.h"
9#include "AbstractProjection_p.h"
12#include "GeoDataLinearRing.h"
13#include "GeoDataLineString.h"
14#include "GeoDataCoordinates.h"
15#include "GeoDataLatLonAltBox.h"
18#include <QPainterPath>
28qreal AzimuthalProjection::clippingRadius()
const
33bool AzimuthalProjection::screenCoordinates(
const GeoDataLineString &lineString,
34 const ViewportParams *viewport,
38 Q_D(
const AzimuthalProjection );
41 if ( !viewport->resolves( lineString.latLonAltBox() ) ) {
46 d->lineStringToPolygon( lineString, viewport, polygons );
52 qint64 radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
53 qint64 width = viewport->width();
54 qint64 height = viewport->height();
58 if ( radius > width + height )
63 if ( 4 * radius * radius >= width * width + height * height )
80 if ( 2.0 * viewport->radius() <= viewport->height()
81 && 2.0 * viewport->radius() <= viewport->width() )
85 if ( pitch > 0.0 && pitch < +M_PI ) {
88 latLonAltBox.setNorth( +fabs( M_PI / 2.0 - fabs( pitch ) ) );
91 if ( pitch < 0.0 && pitch > -M_PI ) {
95 latLonAltBox.setSouth( -fabs( M_PI / 2.0 - fabs( pitch ) ) );
101 if ( pitch == 0.0 || pitch == -M_PI || pitch == +M_PI ) {
102 qreal yaw = viewport->planetAxis().yaw();
121 qreal dummyX, dummyY;
124 if ( screenCoordinates( maxLatPoint, viewport, dummyX, dummyY, dummyVal ) ||
125 screenCoordinates( minLatPoint, viewport, dummyX, dummyY, dummyVal ) ) {
135 int radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
136 int imgWidth = viewport->width();
137 int imgHeight = viewport->height();
140 fullRect.
addRect( 0 , 0 , imgWidth, imgHeight );
146 if ( !viewport->mapCoversViewport() ) {
149 imgWidth / 2 - radius,
150 imgHeight / 2 - radius,
159AzimuthalProjection::AzimuthalProjection(AzimuthalProjectionPrivate * dd) :
164AzimuthalProjection::~AzimuthalProjection()
168void AzimuthalProjectionPrivate::tessellateLineSegment(
const GeoDataCoordinates &aCoords,
170 const GeoDataCoordinates &bCoords,
173 const ViewportParams *viewport,
175 bool allowLatePolygonCut )
const
179 qreal
distance = fabs((bx - ax)) + fabs((by - ay));
186 const qreal safeDistance = - 0.5 *
distance;
187 if ( !( bx < safeDistance && ax < safeDistance )
188 || !( by < safeDistance && ay < safeDistance )
189 || !( bx + safeDistance > viewport->width()
190 && ax + safeDistance > viewport->width() )
191 || !( by + safeDistance > viewport->height()
192 && ay + safeDistance > viewport->height() )
196 int maxTessellationFactor = viewport->radius() < 20000 ? 10 : 20;
197 int const finalTessellationPrecision = qBound(2, viewport->radius()/200, maxTessellationFactor) * tessellationPrecision;
203 if ( distance > finalTessellationPrecision ) {
204 const int tessellatedNodes = qMin<int>( distance / finalTessellationPrecision, maxTessellationNodes );
206 processTessellation( aCoords, bCoords,
211 allowLatePolygonCut);
214 crossHorizon( bCoords, polygons, viewport, allowLatePolygonCut );
222void AzimuthalProjectionPrivate::processTessellation(
const GeoDataCoordinates &previousCoords,
223 const GeoDataCoordinates ¤tCoords,
224 int tessellatedNodes,
226 const ViewportParams *viewport,
228 bool allowLatePolygonCut )
const
231 const bool clampToGround = f.testFlag( FollowGround );
232 const bool followLatitudeCircle = f.testFlag( RespectLatitudeCircle )
233 && previousCoords.latitude() == currentCoords.latitude();
237 if ( followLatitudeCircle ) {
238 const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
239 const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
241 lonDiff = currentCoords.longitude() - previousCoords.longitude();
242 if ( previousSign != currentSign
243 && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > M_PI ) {
244 if ( previousSign > currentSign ) {
246 lonDiff += 2 * M_PI ;
255 GeoDataCoordinates previousTessellatedCoords = previousCoords;
256 for (
int i = 1; i <= tessellatedNodes; ++i ) {
257 const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 );
259 GeoDataCoordinates currentTessellatedCoords;
261 if ( followLatitudeCircle ) {
264 const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
265 const qreal altitude = altDiff * t + previousCoords.altitude();
266 const qreal lon = lonDiff * t + previousCoords.longitude();
267 const qreal lat = previousTessellatedCoords.latitude();
269 currentTessellatedCoords = GeoDataCoordinates(lon, lat, altitude);
274 currentTessellatedCoords = previousCoords.nlerp(currentCoords, t);
278 currentTessellatedCoords.setAltitude(0);
281 crossHorizon( currentTessellatedCoords, polygons, viewport, allowLatePolygonCut );
282 previousTessellatedCoords = currentTessellatedCoords;
286 GeoDataCoordinates currentModifiedCoords( currentCoords );
287 if ( clampToGround ) {
288 currentModifiedCoords.setAltitude( 0.0 );
290 crossHorizon( currentModifiedCoords, polygons, viewport, allowLatePolygonCut );
293void AzimuthalProjectionPrivate::crossHorizon(
const GeoDataCoordinates & bCoord,
295 const ViewportParams *viewport,
296 bool allowLatePolygonCut
300 bool globeHidesPoint;
302 Q_Q(
const AbstractProjection );
304 q->screenCoordinates( bCoord, viewport, x, y, globeHidesPoint );
306 if( !globeHidesPoint ) {
310 if ( allowLatePolygonCut && !polygons.
last()->isEmpty() ) {
317bool AzimuthalProjectionPrivate::lineStringToPolygon(
const GeoDataLineString &lineString,
318 const ViewportParams *viewport,
321 Q_Q(
const AzimuthalProjection );
323 const TessellationFlags f = lineString.tessellationFlags();
324 bool const tessellate = lineString.tessellate();
325 const bool noFilter = f.testFlag(PreventNodeFiltering);
330 bool globeHidesPoint =
false;
332 qreal previousX = -1.0;
333 qreal previousY = -1.0;
334 bool previousGlobeHidesPoint =
false;
336 qreal horizonX = -1.0;
337 qreal horizonY = -1.0;
341 polygon->
reserve(lineString.size());
343 polygons.
append( polygon );
345 GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
346 GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
352 GeoDataCoordinates horizonCoords;
359 bool horizonPair =
false;
360 GeoDataCoordinates horizonDisappearCoords;
366 bool horizonOrphan =
false;
367 GeoDataCoordinates horizonOrphanCoords;
369 GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
370 GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
372 bool processingLastNode =
false;
378 const bool isLong = lineString.size() > 10;
379 const int maximumDetail = levelForResolution(viewport->angularResolution());
381 const bool hasDetail = itBegin->detail() != 0;
383 while ( itCoords != itEnd )
386 bool skipNode = (hasDetail ? itCoords->detail() > maximumDetail
387 : itCoords != itBegin && isLong && !processingLastNode &&
388 !viewport->resolves( *itPreviousCoords, *itCoords ) );
390 if ( !skipNode || noFilter) {
392 q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoint );
395 if ( !processingLastNode && itCoords == itBegin ) {
396 previousGlobeHidesPoint = globeHidesPoint;
397 itPreviousCoords = itCoords;
403 const bool isAtHorizon = ( globeHidesPoint || previousGlobeHidesPoint ) &&
404 ( globeHidesPoint != previousGlobeHidesPoint );
408 horizonCoords = findHorizon( *itPreviousCoords, *itCoords, viewport, f );
410 if ( lineString.isClosed() ) {
412 horizonToPolygon( viewport, horizonDisappearCoords, horizonCoords, polygons.
last() );
416 if ( globeHidesPoint ) {
417 horizonDisappearCoords = horizonCoords;
421 horizonOrphanCoords = horizonCoords;
422 horizonOrphan =
true;
427 q->screenCoordinates( horizonCoords, viewport, horizonX, horizonY );
431 if ( previousGlobeHidesPoint ) {
440 if ( lineString.tessellate() ) {
442 if ( !isAtHorizon ) {
444 tessellateLineSegment( *itPreviousCoords, previousX, previousY,
447 f, !lineString.isClosed() );
453 if ( previousGlobeHidesPoint ) {
454 tessellateLineSegment( horizonCoords, horizonX, horizonY,
457 f, !lineString.isClosed() );
460 tessellateLineSegment( *itPreviousCoords, previousX, previousY,
461 horizonCoords, horizonX, horizonY,
463 f, !lineString.isClosed() );
468 if ( !globeHidesPoint ) {
472 if ( !previousGlobeHidesPoint && isAtHorizon ) {
478 if ( globeHidesPoint ) {
479 if ( !previousGlobeHidesPoint
480 && !lineString.isClosed()
486 previousGlobeHidesPoint = globeHidesPoint;
487 itPreviousCoords = itCoords;
495 if ( processingLastNode ) {
500 if ( itCoords == itEnd && lineString.isClosed() ) {
502 processingLastNode =
true;
508 if ( horizonOrphan && lineString.isClosed() ) {
509 horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, polygons.
last() );
512 if ( polygons.
last()->size() <= 1 ){
513 delete polygons.
last();
520void AzimuthalProjectionPrivate::horizonToPolygon(
const ViewportParams *viewport,
521 const GeoDataCoordinates & disappearCoords,
522 const GeoDataCoordinates & reappearCoords,
527 const qreal imageHalfWidth = viewport->width() / 2;
528 const qreal imageHalfHeight = viewport->height() / 2;
530 bool dummyGlobeHidesPoint =
false;
532 Q_Q(
const AzimuthalProjection );
534 q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHidesPoint );
535 qreal alpha = atan2( y - imageHalfHeight,
536 x - imageHalfWidth );
538 q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesPoint );
539 qreal beta = atan2( y - imageHalfHeight,
540 x - imageHalfWidth );
545 qreal sgndiff = diff < 0 ? -1 : 1;
547 const qreal arcradius = q->clippingRadius() * viewport->radius();
548 const int itEnd = fabs(diff * RAD2DEG);
552 for (
int it = 1; it <= itEnd; ++it ) {
553 const qreal angle = alpha + DEG2RAD * sgndiff * it;
554 const qreal itx = imageHalfWidth + arcradius * cos( angle );
555 const qreal ity = imageHalfHeight + arcradius * sin( angle );
556 *polygon <<
QPointF( itx, ity );
561GeoDataCoordinates AzimuthalProjectionPrivate::findHorizon(
const GeoDataCoordinates & previousCoords,
562 const GeoDataCoordinates & currentCoords,
563 const ViewportParams *viewport,
564 TessellationFlags f)
const
566 bool currentHide = globeHidesPoint( currentCoords, viewport ) ;
568 return doFindHorizon(previousCoords, currentCoords, viewport, f, currentHide, 0);
572GeoDataCoordinates AzimuthalProjectionPrivate::doFindHorizon(
const GeoDataCoordinates & previousCoords,
573 const GeoDataCoordinates & currentCoords,
574 const ViewportParams *viewport,
577 int recursionCounter )
const
579 if ( recursionCounter > 20 ) {
580 return currentHide ? previousCoords : currentCoords;
584 bool followLatitudeCircle =
false;
588 qreal previousLongitude = 0.0;
589 qreal previousLatitude = 0.0;
591 if ( f.testFlag( RespectLatitudeCircle ) ) {
592 previousCoords.geoCoordinates( previousLongitude, previousLatitude );
593 qreal previousSign = previousLongitude > 0 ? 1 : -1;
595 qreal currentLongitude = 0.0;
596 qreal currentLatitude = 0.0;
597 currentCoords.geoCoordinates( currentLongitude, currentLatitude );
598 qreal currentSign = currentLongitude > 0 ? 1 : -1;
600 if ( previousLatitude == currentLatitude ) {
601 followLatitudeCircle =
true;
603 lonDiff = currentLongitude - previousLongitude;
604 if ( previousSign != currentSign
605 && fabs(previousLongitude) + fabs(currentLongitude) > M_PI ) {
606 if ( previousSign > currentSign ) {
608 lonDiff += 2 * M_PI ;
621 GeoDataCoordinates horizonCoords;
623 if ( followLatitudeCircle ) {
626 const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
627 const qreal altitude = previousCoords.altitude() + 0.5 * altDiff;
628 const qreal lon = lonDiff * 0.5 + previousLongitude;
629 const qreal lat = previousLatitude;
631 horizonCoords = GeoDataCoordinates(lon, lat, altitude);
636 horizonCoords = previousCoords.nlerp(currentCoords, 0.5);
639 bool horizonHide = globeHidesPoint( horizonCoords, viewport );
641 if ( horizonHide != currentHide ) {
642 return doFindHorizon(horizonCoords, currentCoords, viewport, f, currentHide, recursionCounter);
645 return doFindHorizon(previousCoords, horizonCoords, viewport, f, horizonHide, recursionCounter);
649bool AzimuthalProjectionPrivate::globeHidesPoint(
const GeoDataCoordinates &coordinates,
650 const ViewportParams *viewport )
const
652 bool globeHidesPoint;
653 qreal dummyX, dummyY;
655 Q_Q(
const AzimuthalProjection );
656 q->screenCoordinates(coordinates, viewport, dummyX, dummyY, globeHidesPoint);
657 return globeHidesPoint;
This file contains the headers for AzimuthalProjection.
This file contains the headers for ViewportParams.
A base class for all projections in Marble.
qreal minLat() const
Returns the arbitrarily chosen minimum (southern) latitude.
qreal maxLat() const
Returns the arbitrarily chosen maximum (northern) latitude.
virtual GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const
Returns a GeoDataLatLonAltBox bounding box of the given screenrect inside the given viewport.
bool isClippedToSphere() const override
Defines whether a projection is supposed to be clipped to a certain radius.
GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const override
Returns a GeoDataLatLonAltBox bounding box of the given screenrect inside the given viewport.
bool mapCoversViewport(const ViewportParams *viewport) const override
Returns whether the projected data fully obstructs the current viewport.
QPainterPath mapShape(const ViewportParams *viewport) const override
Returns the shape/outline of a map projection.
A 3d point representation.
static qreal normalizeLon(qreal lon, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
static qreal normalizeLat(qreal lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize latitude to always be in -M_PI / 2.
A class that defines a 3D bounding box for geographic data.
qreal east(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the eastern boundary of the bounding box.
qreal west(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the western boundary of the bounding box.
A public class that controls what is visible in the viewport of a Marble map.
QString path(const QString &relativePath)
Binds a QML item to a specific geodetic location in screen coordinates.
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void append(QList< T > &&value)
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
void addEllipse(const QPointF ¢er, qreal rx, qreal ry)
void addRect(const QRectF &rectangle)
QPainterPath intersected(const QPainterPath &p) const const