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>
28 qreal AzimuthalProjection::clippingRadius()
const
33 bool 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,
159 AzimuthalProjection::AzimuthalProjection(AzimuthalProjectionPrivate * dd) :
164 AzimuthalProjection::~AzimuthalProjection()
168 void 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 );
222 void 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 );
293 void 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() ) {
317 bool 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();
520 void 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 );
561 GeoDataCoordinates 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);
572 GeoDataCoordinates 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);
649 bool 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;