35 class SphericalProjectionPrivate :
public AbstractProjectionPrivate
39 PlainLineString = 0x00,
40 ClosedLineString = 0x01
43 explicit SphericalProjectionPrivate( SphericalProjection * parent );
51 void tessellateLineSegment(
const GeoDataCoordinates &aCoords,
53 const GeoDataCoordinates &bCoords,
55 LineStringFlag lineStringFlag,
57 const ViewportParams *viewport,
58 TessellationFlags f = 0 )
const;
60 void processTessellation(
const GeoDataCoordinates &previousCoords,
61 const GeoDataCoordinates ¤tCoords,
63 LineStringFlag lineStringFlag,
65 const ViewportParams *viewport,
66 TessellationFlags f = 0 )
const;
68 void crossHorizon(
const GeoDataCoordinates & bCoord,
69 LineStringFlag lineStringFlag,
71 const ViewportParams *viewport )
const;
73 virtual bool lineStringToPolygon(
const GeoDataLineString &lineString,
74 const ViewportParams *viewport,
77 void horizonToPolygon(
const ViewportParams *viewport,
78 const GeoDataCoordinates & disappearCoords,
79 const GeoDataCoordinates & reappearCoords,
82 GeoDataCoordinates findHorizon(
const GeoDataCoordinates & previousCoords,
83 const GeoDataCoordinates & currentCoords,
84 const ViewportParams *viewport,
85 TessellationFlags f = 0,
86 int recursionCounter = 0 )
const;
88 static bool globeHidesPoint(
const GeoDataCoordinates &coordinates,
89 const ViewportParams *viewport );
91 Q_DECLARE_PUBLIC( SphericalProjection )
137 qreal &x, qreal &y,
bool &globeHidesPoint )
const
144 qreal pixelAltitude = ( ( viewport->
radius() )
146 if ( coordinates.
altitude() < 10000 ) {
148 if ( qpos.
v[
Q_Z] < 0 ) {
149 globeHidesPoint =
true;
154 qreal earthCenteredX = pixelAltitude * qpos.
v[
Q_X];
155 qreal earthCenteredY = pixelAltitude * qpos.
v[
Q_Y];
160 && ( ( earthCenteredX * earthCenteredX
161 + earthCenteredY * earthCenteredY )
162 < radius * radius ) ) {
163 globeHidesPoint =
true;
169 x = ((qreal)(viewport->
width()) / 2 + pixelAltitude * qpos.
v[
Q_X]);
170 y = ((qreal)(viewport->
height()) / 2 - pixelAltitude * qpos.
v[
Q_Y]);
173 if ( x < 0 || x >= viewport->
width() || y < 0 || y >= viewport->
height() ) {
174 globeHidesPoint =
false;
178 globeHidesPoint =
false;
187 bool &globeHidesPoint )
const
190 bool visible =
screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint );
193 if ( *x + size.
width() / 2.0 < 0.0 || *x >= viewport->
width() + size.
width() / 2.0
196 globeHidesPoint =
false;
209 qreal& lon, qreal& lat,
212 const qreal inverseRadius = 1.0 / (qreal)(viewport->
radius());
214 const qreal qx = +(qreal)( x - viewport->
width() / 2 ) * inverseRadius;
215 const qreal qy = -(qreal)( y - viewport->
height() / 2 ) * inverseRadius;
217 if ( 1 <= qx * qx + qy * qy ) {
221 const qreal qz = sqrt( 1 - qx * qx - qy * qy );
251 if ( pitch > 0.0 && pitch < +
M_PI ) {
252 latLonAltBox.
setNorth( +fabs(
M_PI / 2.0 - fabs( pitch ) ) );
255 if ( pitch < 0.0 && pitch > -
M_PI ) {
259 latLonAltBox.
setSouth( -fabs(
M_PI / 2.0 - fabs( pitch ) ) );
265 if ( pitch == 0.0 || pitch == -
M_PI || pitch == +
M_PI ) {
278 qreal averageLongitude = ( latLonAltBox.
west() + latLonAltBox.
east() ) / 2.0;
283 qreal dummyX, dummyY;
299 qint64 width = viewport->
width();
300 qint64 height = viewport->
height();
304 if ( radius > width + height )
309 if ( 4 * radius * radius >= width * width + height * height )
318 int imgWidth = viewport->
width();
319 int imgHeight = viewport->
height();
322 fullRect.
addRect( 0 , 0 , imgWidth, imgHeight );
331 imgWidth / 2 - radius,
332 imgHeight / 2 - radius,
354 d->lineStringToPolygon( lineString, viewport, polygons );
358 void SphericalProjectionPrivate::tessellateLineSegment(
const GeoDataCoordinates &aCoords,
362 LineStringFlag lineStringFlag,
365 TessellationFlags f)
const
369 qreal distance = fabs((bx - ax)) + fabs((by - ay));
376 const qreal safeDistance = - 0.5 * distance;
377 if ( !( bx < safeDistance && ax < safeDistance )
378 || !( by < safeDistance && ay < safeDistance )
379 || !( bx + safeDistance > viewport->
width()
380 && ax + safeDistance > viewport->
width() )
381 || !( by + safeDistance > viewport->
height()
382 && ay + safeDistance > viewport->
height() )
393 if ( distance > finalTessellationPrecision ) {
394 const int tessellatedNodes = qMin<int>( distance / finalTessellationPrecision,
maxTessellationNodes );
396 processTessellation( aCoords, bCoords,
404 crossHorizon( bCoords, lineStringFlag, polygons, viewport );
412 void SphericalProjectionPrivate::processTessellation(
const GeoDataCoordinates &previousCoords,
413 const GeoDataCoordinates ¤tCoords,
414 int tessellatedNodes,
415 LineStringFlag lineStringFlag,
417 const ViewportParams *viewport,
418 TessellationFlags f)
const
423 && previousCoords.latitude() == currentCoords.latitude();
427 if ( followLatitudeCircle ) {
428 const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
429 const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
431 lonDiff = currentCoords.longitude() - previousCoords.longitude();
432 if ( previousSign != currentSign
433 && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) >
M_PI ) {
434 if ( previousSign > currentSign ) {
436 lonDiff += 2 *
M_PI ;
444 const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
447 GeoDataCoordinates previousTessellatedCoords = previousCoords;
448 for (
int i = 1; i <= tessellatedNodes; ++i ) {
449 const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 );
452 const qreal altitude = clampToGround ? 0 : altDiff * t + previousCoords.altitude();
456 if ( followLatitudeCircle ) {
459 lon = lonDiff * t + previousCoords.longitude();
460 lat = previousTessellatedCoords.latitude();
465 const Quaternion itpos =
Quaternion::nlerp( previousCoords.quaternion(), currentCoords.quaternion(), t );
466 itpos. getSpherical( lon, lat );
469 const GeoDataCoordinates currentTessellatedCoords( lon, lat, altitude );
470 crossHorizon( currentTessellatedCoords, lineStringFlag, polygons, viewport );
471 previousTessellatedCoords = currentTessellatedCoords;
475 GeoDataCoordinates currentModifiedCoords( currentCoords );
476 if ( clampToGround ) {
477 currentModifiedCoords.setAltitude( 0.0 );
479 crossHorizon( currentModifiedCoords, lineStringFlag, polygons, viewport );
482 void SphericalProjectionPrivate::crossHorizon(
const GeoDataCoordinates & bCoord,
483 LineStringFlag lineStringFlag,
485 const ViewportParams *viewport )
const
488 bool globeHidesPoint;
490 Q_Q(
const AbstractProjection );
492 q->screenCoordinates( bCoord, viewport, x, y, globeHidesPoint );
494 if( !globeHidesPoint ) {
498 if ( !polygons.
last()->isEmpty() && lineStringFlag != ClosedLineString ) {
505 bool SphericalProjectionPrivate::lineStringToPolygon(
const GeoDataLineString &lineString,
506 const ViewportParams *viewport,
509 Q_Q(
const SphericalProjection );
511 const TessellationFlags f = lineString.tessellationFlags();
515 bool globeHidesPoint =
false;
517 qreal previousX = -1.0;
518 qreal previousY = -1.0;
519 bool previousGlobeHidesPoint =
false;
521 qreal horizonX = -1.0;
522 qreal horizonY = -1.0;
533 GeoDataCoordinates horizonCoords;
540 bool horizonPair =
false;
541 GeoDataCoordinates horizonDisappearCoords;
547 bool horizonOrphan =
false;
548 GeoDataCoordinates horizonOrphanCoords;
553 bool processingLastNode =
false;
559 const bool isLong = lineString.size() > 50;
560 const int maximumDetail = ( viewport->radius() > 5000 ) ? 5 :
561 ( viewport->radius() > 2500 ) ? 4 :
562 ( viewport->radius() > 1000 ) ? 3 :
563 ( viewport->radius() > 600 ) ? 2 :
564 ( viewport->radius() > 50 ) ? 1 :
567 while ( itCoords != itEnd )
571 bool skipNode = itCoords != itBegin && isLong && !processingLastNode &&
572 ( (*itCoords).detail() > maximumDetail
573 || viewport->resolves( *itPreviousCoords, *itCoords ) );
577 q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoint );
580 if ( !processingLastNode && itCoords == itBegin ) {
581 previousGlobeHidesPoint = globeHidesPoint;
582 itPreviousCoords = itCoords;
588 const bool isAtHorizon = ( globeHidesPoint || previousGlobeHidesPoint ) &&
589 ( globeHidesPoint != previousGlobeHidesPoint );
593 horizonCoords = findHorizon( *itPreviousCoords, *itCoords, viewport, f );
595 if ( lineString.isClosed() ) {
597 horizonToPolygon( viewport, horizonDisappearCoords, horizonCoords, polygons.
last() );
601 if ( globeHidesPoint ) {
602 horizonDisappearCoords = horizonCoords;
606 horizonOrphanCoords = horizonCoords;
607 horizonOrphan =
true;
612 q->screenCoordinates( horizonCoords, viewport, horizonX, horizonY );
616 if ( previousGlobeHidesPoint ) {
625 if ( lineString.tessellate() ) {
626 LineStringFlag lineStringFlag = lineString.isClosed() ? ClosedLineString : PlainLineString;
627 if ( !isAtHorizon ) {
628 tessellateLineSegment( *itPreviousCoords, previousX, previousY,
630 lineStringFlag, polygons, viewport,
636 if ( previousGlobeHidesPoint ) {
637 tessellateLineSegment( horizonCoords, horizonX, horizonY,
639 lineStringFlag, polygons, viewport,
643 tessellateLineSegment( *itPreviousCoords, previousX, previousY,
644 horizonCoords, horizonX, horizonY,
645 lineStringFlag, polygons, viewport,
651 if ( !globeHidesPoint ) {
655 if ( !previousGlobeHidesPoint && isAtHorizon ) {
661 if ( globeHidesPoint ) {
662 if ( !previousGlobeHidesPoint
663 && !lineString.isClosed()
669 previousGlobeHidesPoint = globeHidesPoint;
670 itPreviousCoords = itCoords;
678 if ( processingLastNode ) {
683 if ( itCoords == itEnd && lineString.isClosed() ) {
685 processingLastNode =
true;
691 if ( horizonOrphan && lineString.isClosed() ) {
692 horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, polygons.
last() );
695 if ( polygons.
last()->size() <= 1 ){
702 void SphericalProjectionPrivate::horizonToPolygon(
const ViewportParams *viewport,
703 const GeoDataCoordinates & disappearCoords,
704 const GeoDataCoordinates & reappearCoords,
709 const qreal imageHalfWidth = viewport->width() / 2;
710 const qreal imageHalfHeight = viewport->height() / 2;
712 bool dummyGlobeHidesPoint =
false;
714 Q_Q(
const SphericalProjection );
716 q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHidesPoint );
717 qreal alpha = atan2( y - imageHalfHeight,
718 x - imageHalfWidth );
720 q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesPoint );
721 qreal beta = atan2( y - imageHalfHeight,
722 x - imageHalfWidth );
727 qreal sgndiff = diff < 0 ? -1 : 1;
729 const qreal arcradius = viewport->radius();
730 const int itEnd = fabs(diff *
RAD2DEG);
733 for (
int it = 1; it <= itEnd; ++it ) {
735 const qreal itx = imageHalfWidth + arcradius * cos( angle );
736 const qreal ity = imageHalfHeight + arcradius * sin( angle );
737 *polygon <<
QPointF( itx, ity );
742 GeoDataCoordinates SphericalProjectionPrivate::findHorizon(
const GeoDataCoordinates & previousCoords,
743 const GeoDataCoordinates & currentCoords,
744 const ViewportParams *viewport,
746 int recursionCounter )
const
748 bool currentHide = globeHidesPoint( currentCoords, viewport ) ;
750 if ( recursionCounter > 20 ) {
751 return currentHide ? previousCoords : currentCoords;
755 bool followLatitudeCircle =
false;
759 qreal previousLongitude = 0.0;
760 qreal previousLatitude = 0.0;
763 previousCoords.geoCoordinates( previousLongitude, previousLatitude );
764 qreal previousSign = previousLongitude > 0 ? 1 : -1;
766 qreal currentLongitude = 0.0;
767 qreal currentLatitude = 0.0;
768 currentCoords.geoCoordinates( currentLongitude, currentLatitude );
769 qreal currentSign = currentLongitude > 0 ? 1 : -1;
771 if ( previousLatitude == currentLatitude ) {
772 followLatitudeCircle =
true;
774 lonDiff = currentLongitude - previousLongitude;
775 if ( previousSign != currentSign
776 && fabs(previousLongitude) + fabs(currentLongitude) >
M_PI ) {
777 if ( previousSign > currentSign ) {
779 lonDiff += 2 *
M_PI ;
795 qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
797 if ( followLatitudeCircle ) {
800 lon = lonDiff * 0.5 + previousLongitude;
801 lat = previousLatitude;
806 const Quaternion itpos =
Quaternion::nlerp( previousCoords.quaternion(), currentCoords.quaternion(), 0.5 );
807 itpos. getSpherical( lon, lat );
810 qreal altitude = previousCoords.altitude() + 0.5 * altDiff;
812 GeoDataCoordinates horizonCoords( lon, lat, altitude );
814 bool horizonHide = globeHidesPoint( horizonCoords, viewport );
816 if ( horizonHide != currentHide ) {
817 return findHorizon( horizonCoords, currentCoords, viewport, f, recursionCounter );
820 return findHorizon( previousCoords, horizonCoords, viewport, f, recursionCounter );
824 bool SphericalProjectionPrivate::globeHidesPoint(
const GeoDataCoordinates &coordinates,
825 const ViewportParams *viewport )
827 qreal absoluteAltitude = coordinates.altitude() +
EARTH_RADIUS;
828 Quaternion qpos = coordinates.quaternion();
830 qpos.rotateAroundAxis( viewport->planetAxisMatrix() );
832 qreal pixelAltitude = ( ( viewport->radius() )
834 if ( coordinates.altitude() < 10000 ) {
836 if ( qpos.v[
Q_Z] < 0 ) {
841 qreal earthCenteredX = pixelAltitude * qpos.v[
Q_X];
842 qreal earthCenteredY = pixelAltitude * qpos.v[
Q_Y];
843 qreal
radius = viewport->radius();
847 && ( ( earthCenteredX * earthCenteredX
848 + earthCenteredY * earthCenteredY )
849 < radius * radius ) ) {
Unit
enum used constructor to specify the units used
void addEllipse(const QRectF &boundingRectangle)
virtual bool screenCoordinates(const GeoDataCoordinates &coordinates, const ViewportParams *params, qreal &x, qreal &y, bool &globeHidesPoint) const
Get the screen coordinates corresponding to geographical coordinates in the map.
A 3d point representation.
void setNorth(const qreal north, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
double angle(double vec1[3], double vec2[3])
void append(const T &value)
static qreal normalizeLon(qreal lon, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
virtual qreal minValidLat() const
void setSouth(const qreal south, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
bool geoCoordinates(const int x, const int y, const ViewportParams *params, qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Degree) const
Get the earth coordinates corresponding to a pixel in the map.
virtual GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const
This file contains the headers for SphericalProjection.
void setWest(const qreal west, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
static Quaternion nlerp(const Quaternion &q1, const Quaternion &q2, qreal t)
QVector< GeoDataCoordinates >::ConstIterator ConstIterator
void rotateAroundAxis(const Quaternion &q)
qreal altitude() const
return the altitude of the Point in meters
qreal east(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the eastern boundary of the bounding box.
virtual ~SphericalProjection()
void setMaxLat(qreal maxLat)
static const int tessellationPrecision
virtual qreal maxValidLat() const
bool mapCoversViewport() const
SphericalProjection()
Construct a new SphericalProjection.
static qreal radius(qreal zoom)
void addRect(const QRectF &rectangle)
A class to implement the spherical projection used by the "Globe" view.
static qreal normalizeLat(qreal lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize latitude to always be in -M_PI / 2.
const matrix & planetAxisMatrix() const
A LineString that allows to store a contiguous set of line segments.
A public class that controls what is visible in the viewport of a Marble map.
virtual bool repeatableX() const
virtual QPainterPath mapShape(const ViewportParams *viewport) const
This file contains the headers for ViewportParams.
static MarbleGlobal * getInstance()
static const int maxTessellationNodes
qreal west(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the western boundary of the bounding box.
A base class for all projections in Marble.
bool resolves(const GeoDataLatLonBox &latLonBox) const
Profiles profiles() const
Quaternion planetAxis() const
bool mapCoversViewport(const ViewportParams *viewport) const
void getSpherical(qreal &lon, qreal &lat) const
A class that defines a 3D bounding box for geographic data.
virtual const GeoDataLatLonAltBox & latLonAltBox() const
Returns the smallest latLonAltBox that contains the LineString.
void setMinLat(qreal minLat)
GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const
QPainterPath intersected(const QPainterPath &p) const
const Quaternion & quaternion() const
return a Quaternion with the used coordinates
void setEast(const qreal east, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)