Marble

AzimuthalProjection.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2014 Gábor Péterffy <[email protected]>
4 //
5 
6 // Local
7 #include "AzimuthalProjection.h"
8 #include "AzimuthalProjection_p.h"
9 #include "AbstractProjection_p.h"
10 
11 // Marble
12 #include "GeoDataLinearRing.h"
13 #include "GeoDataLineString.h"
14 #include "GeoDataCoordinates.h"
15 #include "GeoDataLatLonAltBox.h"
16 #include "ViewportParams.h"
17 
18 #include <QPainterPath>
19 
20 
21 namespace Marble {
22 
24 {
25  return true;
26 }
27 
28 qreal AzimuthalProjection::clippingRadius() const
29 {
30  return 1;
31 }
32 
33 bool AzimuthalProjection::screenCoordinates( const GeoDataLineString &lineString,
34  const ViewportParams *viewport,
35  QVector<QPolygonF *> &polygons ) const
36 {
37 
38  Q_D( const AzimuthalProjection );
39  // Compare bounding box size of the line string with the angularResolution
40  // Immediately return if the latLonAltBox is smaller.
41  if ( !viewport->resolves( lineString.latLonAltBox() ) ) {
42 // mDebug() << "Object too small to be resolved";
43  return false;
44  }
45 
46  d->lineStringToPolygon( lineString, viewport, polygons );
47  return true;
48 }
49 
51 {
52  qint64 radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
53  qint64 width = viewport->width();
54  qint64 height = viewport->height();
55 
56  // This first test is a quick one that will catch all really big
57  // radii and prevent overflow in the real test.
58  if ( radius > width + height )
59  return true;
60 
61  // This is the real test. The 4 is because we are really
62  // comparing to width/2 and height/2.
63  if ( 4 * radius * radius >= width * width + height * height )
64  return true;
65 
66  return false;
67 }
68 
70  const ViewportParams *viewport ) const
71 {
72  // For the case where the whole viewport gets covered there is a
73  // pretty dirty and generic detection algorithm:
75 
76  // If the whole globe is visible we can easily calculate
77  // analytically the lon-/lat- range.
78  qreal pitch = GeoDataCoordinates::normalizeLat( viewport->planetAxis().pitch() );
79 
80  if ( 2.0 * viewport->radius() <= viewport->height()
81  && 2.0 * viewport->radius() <= viewport->width() )
82  {
83  // Unless the planetaxis is in the screen plane the allowed longitude range
84  // covers full -180 deg to +180 deg:
85  if ( pitch > 0.0 && pitch < +M_PI ) {
86  latLonAltBox.setWest( -M_PI );
87  latLonAltBox.setEast( +M_PI );
88  latLonAltBox.setNorth( +fabs( M_PI / 2.0 - fabs( pitch ) ) );
89  latLonAltBox.setSouth( -M_PI / 2.0 );
90  }
91  if ( pitch < 0.0 && pitch > -M_PI ) {
92  latLonAltBox.setWest( -M_PI );
93  latLonAltBox.setEast( +M_PI );
94  latLonAltBox.setNorth( +M_PI / 2.0 );
95  latLonAltBox.setSouth( -fabs( M_PI / 2.0 - fabs( pitch ) ) );
96  }
97 
98  // Last but not least we deal with the rare case where the
99  // globe is fully visible and pitch = 0.0 or pitch = -M_PI or
100  // pitch = +M_PI
101  if ( pitch == 0.0 || pitch == -M_PI || pitch == +M_PI ) {
102  qreal yaw = viewport->planetAxis().yaw();
103  latLonAltBox.setWest( GeoDataCoordinates::normalizeLon( yaw - M_PI / 2.0 ) );
104  latLonAltBox.setEast( GeoDataCoordinates::normalizeLon( yaw + M_PI / 2.0 ) );
105  latLonAltBox.setNorth( +M_PI / 2.0 );
106  latLonAltBox.setSouth( -M_PI / 2.0 );
107  }
108 
109  return latLonAltBox;
110  }
111 
112  // Now we check whether maxLat (e.g. the north pole) gets displayed
113  // inside the viewport to get more accurate values for east and west.
114 
115  // We need a point on the screen at maxLat that definitely gets displayed:
116  qreal averageLongitude = ( latLonAltBox.west() + latLonAltBox.east() ) / 2.0;
117 
118  GeoDataCoordinates maxLatPoint( averageLongitude, maxLat(), 0.0, GeoDataCoordinates::Radian );
119  GeoDataCoordinates minLatPoint( averageLongitude, minLat(), 0.0, GeoDataCoordinates::Radian );
120 
121  qreal dummyX, dummyY; // not needed
122  bool dummyVal;
123 
124  if ( screenCoordinates( maxLatPoint, viewport, dummyX, dummyY, dummyVal ) ||
125  screenCoordinates( minLatPoint, viewport, dummyX, dummyY, dummyVal ) ) {
126  latLonAltBox.setWest( -M_PI );
127  latLonAltBox.setEast( +M_PI );
128  }
129 
130  return latLonAltBox;
131 }
132 
134 {
135  int radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
136  int imgWidth = viewport->width();
137  int imgHeight = viewport->height();
138 
139  QPainterPath fullRect;
140  fullRect.addRect( 0 , 0 , imgWidth, imgHeight );
141 
142  // If the globe covers the whole image, then the projected region represents
143  // all of the image.
144  // Otherwise the active region has got the shape of the visible globe.
145 
146  if ( !viewport->mapCoversViewport() ) {
148  mapShape.addEllipse(
149  imgWidth / 2 - radius,
150  imgHeight / 2 - radius,
151  2 * radius,
152  2 * radius );
153  return mapShape.intersected( fullRect );
154  }
155 
156  return fullRect;
157 }
158 
159 AzimuthalProjection::AzimuthalProjection(AzimuthalProjectionPrivate * dd) :
160  AbstractProjection( dd )
161 {
162 }
163 
164 AzimuthalProjection::~AzimuthalProjection()
165 {
166 }
167 
168 void AzimuthalProjectionPrivate::tessellateLineSegment( const GeoDataCoordinates &aCoords,
169  qreal ax, qreal ay,
170  const GeoDataCoordinates &bCoords,
171  qreal bx, qreal by,
172  QVector<QPolygonF*> &polygons,
173  const ViewportParams *viewport,
175  bool allowLatePolygonCut ) const
176 {
177  // We take the manhattan length as a distance approximation
178  // that can be too big by a factor of sqrt(2)
179  qreal distance = fabs((bx - ax)) + fabs((by - ay));
180 #ifdef SAFE_DISTANCE
181  // Interpolate additional nodes if the line segment that connects the
182  // current or previous nodes might cross the viewport.
183  // The latter can pretty safely be excluded for most projections if both points
184  // are located on the same side relative to the viewport boundaries and if they are
185  // located more than half the line segment distance away from the viewport.
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() )
193  )
194  {
195 #endif
196  int maxTessellationFactor = viewport->radius() < 20000 ? 10 : 20;
197  int const finalTessellationPrecision = qBound(2, viewport->radius()/200, maxTessellationFactor) * tessellationPrecision;
198 
199  // Let the line segment follow the spherical surface
200  // if the distance between the previous point and the current point
201  // on screen is too big
202 
203  if ( distance > finalTessellationPrecision ) {
204  const int tessellatedNodes = qMin<int>( distance / finalTessellationPrecision, maxTessellationNodes );
205 
206  processTessellation( aCoords, bCoords,
207  tessellatedNodes,
208  polygons,
209  viewport,
210  f,
211  allowLatePolygonCut);
212  }
213  else {
214  crossHorizon( bCoords, polygons, viewport, allowLatePolygonCut );
215  }
216 #ifdef SAFE_DISTANCE
217  }
218 #endif
219 }
220 
221 
222 void AzimuthalProjectionPrivate::processTessellation( const GeoDataCoordinates &previousCoords,
223  const GeoDataCoordinates &currentCoords,
224  int tessellatedNodes,
225  QVector<QPolygonF*> &polygons,
226  const ViewportParams *viewport,
228  bool allowLatePolygonCut ) const
229 {
230 
231  const bool clampToGround = f.testFlag( FollowGround );
232  const bool followLatitudeCircle = f.testFlag( RespectLatitudeCircle )
233  && previousCoords.latitude() == currentCoords.latitude();
234 
235  // Calculate steps for tessellation: lonDiff and altDiff
236  qreal lonDiff = 0.0;
237  if ( followLatitudeCircle ) {
238  const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
239  const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
240 
241  lonDiff = currentCoords.longitude() - previousCoords.longitude();
242  if ( previousSign != currentSign
243  && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > M_PI ) {
244  if ( previousSign > currentSign ) {
245  // going eastwards ->
246  lonDiff += 2 * M_PI ;
247  } else {
248  // going westwards ->
249  lonDiff -= 2 * M_PI;
250  }
251  }
252  }
253 
254  // Create the tessellation nodes.
255  GeoDataCoordinates previousTessellatedCoords = previousCoords;
256  for ( int i = 1; i <= tessellatedNodes; ++i ) {
257  const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 );
258 
259  GeoDataCoordinates currentTessellatedCoords;
260 
261  if ( followLatitudeCircle ) {
262  // To tessellate along latitude circles use the
263  // linear interpolation of the longitude.
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();
268 
269  currentTessellatedCoords = GeoDataCoordinates(lon, lat, altitude);
270  }
271  else {
272  // To tessellate along great circles use the
273  // normalized linear interpolation ("NLERP") for latitude and longitude.
274  currentTessellatedCoords = previousCoords.nlerp(currentCoords, t);
275  }
276 
277  if (clampToGround) {
278  currentTessellatedCoords.setAltitude(0);
279  }
280 
281  crossHorizon( currentTessellatedCoords, polygons, viewport, allowLatePolygonCut );
282  previousTessellatedCoords = currentTessellatedCoords;
283  }
284 
285  // For the clampToGround case add the "current" coordinate after adding all other nodes.
286  GeoDataCoordinates currentModifiedCoords( currentCoords );
287  if ( clampToGround ) {
288  currentModifiedCoords.setAltitude( 0.0 );
289  }
290  crossHorizon( currentModifiedCoords, polygons, viewport, allowLatePolygonCut );
291 }
292 
293 void AzimuthalProjectionPrivate::crossHorizon( const GeoDataCoordinates & bCoord,
294  QVector<QPolygonF*> &polygons,
295  const ViewportParams *viewport,
296  bool allowLatePolygonCut
297  ) const
298 {
299  qreal x, y;
300  bool globeHidesPoint;
301 
302  Q_Q( const AbstractProjection );
303 
304  q->screenCoordinates( bCoord, viewport, x, y, globeHidesPoint );
305 
306  if( !globeHidesPoint ) {
307  *polygons.last() << QPointF( x, y );
308  }
309  else {
310  if ( allowLatePolygonCut && !polygons.last()->isEmpty() ) {
311  QPolygonF *path = new QPolygonF;
312  polygons.append( path );
313  }
314  }
315 }
316 
317 bool AzimuthalProjectionPrivate::lineStringToPolygon( const GeoDataLineString &lineString,
318  const ViewportParams *viewport,
319  QVector<QPolygonF *> &polygons ) const
320 {
321  Q_Q( const AzimuthalProjection );
322 
323  const TessellationFlags f = lineString.tessellationFlags();
324  bool const tessellate = lineString.tessellate();
325  const bool noFilter = f.testFlag(PreventNodeFiltering);
326 
327 
328  qreal x = 0;
329  qreal y = 0;
330  bool globeHidesPoint = false;
331 
332  qreal previousX = -1.0;
333  qreal previousY = -1.0;
334  bool previousGlobeHidesPoint = false;
335 
336  qreal horizonX = -1.0;
337  qreal horizonY = -1.0;
338 
339  QPolygonF * polygon = new QPolygonF;
340  if (!tessellate) {
341  polygon->reserve(lineString.size());
342  }
343  polygons.append( polygon );
344 
345  GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
346  GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
347 
348  // Some projections display the earth in a way so that there is a
349  // foreside and a backside.
350  // The horizon is the line (usually a circle) which separates both
351  // sides from each other and resembles the map shape.
352  GeoDataCoordinates horizonCoords;
353 
354  // A horizon pair is a pair of two subsequent horizon crossings:
355  // The first one describes the point where a line string disappears behind the horizon.
356  // and where horizonPair is set to true.
357  // The second one describes the point where the line string reappears.
358  // In this case the two points are connected and horizonPair is set to false again.
359  bool horizonPair = false;
360  GeoDataCoordinates horizonDisappearCoords;
361 
362  // If the first horizon crossing in a line string describes the appearance of
363  // a line string then we call it a "horizon orphan" and horizonOrphan is set to true.
364  // In this case once the last horizon crossing in the line string is reached
365  // it needs to be connected to the orphan.
366  bool horizonOrphan = false;
367  GeoDataCoordinates horizonOrphanCoords;
368 
369  GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
370  GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
371 
372  bool processingLastNode = false;
373 
374  // We use a while loop to be able to cover linestrings as well as linear rings:
375  // Linear rings require to tessellate the path from the last node to the first node
376  // which isn't really convenient to achieve with a for loop ...
377 
378  const bool isLong = lineString.size() > 10;
379  const int maximumDetail = levelForResolution(viewport->angularResolution());
380  // The first node of optimized linestrings has a non-zero detail value.
381  const bool hasDetail = itBegin->detail() != 0;
382 
383  while ( itCoords != itEnd )
384  {
385  // Optimization for line strings with a big amount of nodes
386  bool skipNode = (hasDetail ? itCoords->detail() > maximumDetail
387  : itCoords != itBegin && isLong && !processingLastNode &&
388  !viewport->resolves( *itPreviousCoords, *itCoords ) );
389 
390  if ( !skipNode || noFilter) {
391 
392  q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoint );
393 
394  // Initializing variables that store the values of the previous iteration
395  if ( !processingLastNode && itCoords == itBegin ) {
396  previousGlobeHidesPoint = globeHidesPoint;
397  itPreviousCoords = itCoords;
398  previousX = x;
399  previousY = y;
400  }
401 
402  // Check for the "horizon case" (which is present e.g. for the spherical projection
403  const bool isAtHorizon = ( globeHidesPoint || previousGlobeHidesPoint ) &&
404  ( globeHidesPoint != previousGlobeHidesPoint );
405 
406  if ( isAtHorizon ) {
407  // Handle the "horizon case"
408  horizonCoords = findHorizon( *itPreviousCoords, *itCoords, viewport, f );
409 
410  if ( lineString.isClosed() ) {
411  if ( horizonPair ) {
412  horizonToPolygon( viewport, horizonDisappearCoords, horizonCoords, polygons.last() );
413  horizonPair = false;
414  }
415  else {
416  if ( globeHidesPoint ) {
417  horizonDisappearCoords = horizonCoords;
418  horizonPair = true;
419  }
420  else {
421  horizonOrphanCoords = horizonCoords;
422  horizonOrphan = true;
423  }
424  }
425  }
426 
427  q->screenCoordinates( horizonCoords, viewport, horizonX, horizonY );
428 
429  // If the line appears on the visible half we need
430  // to add an interpolated point at the horizon as the previous point.
431  if ( previousGlobeHidesPoint ) {
432  *polygons.last() << QPointF( horizonX, horizonY );
433  }
434  }
435 
436  // This if-clause contains the section that tessellates the line
437  // segments of a linestring. If you are about to learn how the code of
438  // this class works you can safely ignore this section for a start.
439 
440  if ( lineString.tessellate() /* && ( isVisible || previousIsVisible ) */ ) {
441 
442  if ( !isAtHorizon ) {
443 
444  tessellateLineSegment( *itPreviousCoords, previousX, previousY,
445  *itCoords, x, y,
446  polygons, viewport,
447  f, !lineString.isClosed() );
448 
449  }
450  else {
451  // Connect the interpolated point at the horizon with the
452  // current or previous point in the line.
453  if ( previousGlobeHidesPoint ) {
454  tessellateLineSegment( horizonCoords, horizonX, horizonY,
455  *itCoords, x, y,
456  polygons, viewport,
457  f, !lineString.isClosed() );
458  }
459  else {
460  tessellateLineSegment( *itPreviousCoords, previousX, previousY,
461  horizonCoords, horizonX, horizonY,
462  polygons, viewport,
463  f, !lineString.isClosed() );
464  }
465  }
466  }
467  else {
468  if ( !globeHidesPoint ) {
469  *polygons.last() << QPointF( x, y );
470  }
471  else {
472  if ( !previousGlobeHidesPoint && isAtHorizon ) {
473  *polygons.last() << QPointF( horizonX, horizonY );
474  }
475  }
476  }
477 
478  if ( globeHidesPoint ) {
479  if ( !previousGlobeHidesPoint
480  && !lineString.isClosed()
481  ) {
482  polygons.append( new QPolygonF );
483  }
484  }
485 
486  previousGlobeHidesPoint = globeHidesPoint;
487  itPreviousCoords = itCoords;
488  previousX = x;
489  previousY = y;
490  }
491 
492  // Here we modify the condition to be able to process the
493  // first node after the last node in a LinearRing.
494 
495  if ( processingLastNode ) {
496  break;
497  }
498  ++itCoords;
499 
500  if ( itCoords == itEnd && lineString.isClosed() ) {
501  itCoords = itBegin;
502  processingLastNode = true;
503  }
504  }
505 
506  // In case of horizon crossings, make sure that we always get a
507  // polygon closed correctly.
508  if ( horizonOrphan && lineString.isClosed() ) {
509  horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, polygons.last() );
510  }
511 
512  if ( polygons.last()->size() <= 1 ){
513  delete polygons.last();
514  polygons.pop_back(); // Clean up "unused" empty polygon instances
515  }
516 
517  return polygons.isEmpty();
518 }
519 
520 void AzimuthalProjectionPrivate::horizonToPolygon( const ViewportParams *viewport,
521  const GeoDataCoordinates & disappearCoords,
522  const GeoDataCoordinates & reappearCoords,
523  QPolygonF * polygon ) const
524 {
525  qreal x, y;
526 
527  const qreal imageHalfWidth = viewport->width() / 2;
528  const qreal imageHalfHeight = viewport->height() / 2;
529 
530  bool dummyGlobeHidesPoint = false;
531 
532  Q_Q( const AzimuthalProjection );
533  // Calculate the angle of the position vectors of both coordinates
534  q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHidesPoint );
535  qreal alpha = atan2( y - imageHalfHeight,
536  x - imageHalfWidth );
537 
538  q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesPoint );
539  qreal beta = atan2( y - imageHalfHeight,
540  x - imageHalfWidth );
541 
542  // Calculate the difference between both
543  qreal diff = GeoDataCoordinates::normalizeLon( beta - alpha );
544 
545  qreal sgndiff = diff < 0 ? -1 : 1;
546 
547  const qreal arcradius = q->clippingRadius() * viewport->radius();
548  const int itEnd = fabs(diff * RAD2DEG);
549 
550  // Create a polygon that resembles an arc between the two position vectors
551  polygon->reserve(polygon->size() + itEnd);
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 );
557  }
558 }
559 
560 
561 GeoDataCoordinates AzimuthalProjectionPrivate::findHorizon( const GeoDataCoordinates & previousCoords,
562  const GeoDataCoordinates & currentCoords,
563  const ViewportParams *viewport,
564  TessellationFlags f) const
565 {
566  bool currentHide = globeHidesPoint( currentCoords, viewport ) ;
567 
568  return doFindHorizon(previousCoords, currentCoords, viewport, f, currentHide, 0);
569 }
570 
571 
572 GeoDataCoordinates AzimuthalProjectionPrivate::doFindHorizon( const GeoDataCoordinates & previousCoords,
573  const GeoDataCoordinates & currentCoords,
574  const ViewportParams *viewport,
576  bool currentHide,
577  int recursionCounter ) const
578 {
579  if ( recursionCounter > 20 ) {
580  return currentHide ? previousCoords : currentCoords;
581  }
582  ++recursionCounter;
583 
584  bool followLatitudeCircle = false;
585 
586  // Calculate steps for tessellation: lonDiff and altDiff
587  qreal lonDiff = 0.0;
588  qreal previousLongitude = 0.0;
589  qreal previousLatitude = 0.0;
590 
591  if ( f.testFlag( RespectLatitudeCircle ) ) {
592  previousCoords.geoCoordinates( previousLongitude, previousLatitude );
593  qreal previousSign = previousLongitude > 0 ? 1 : -1;
594 
595  qreal currentLongitude = 0.0;
596  qreal currentLatitude = 0.0;
597  currentCoords.geoCoordinates( currentLongitude, currentLatitude );
598  qreal currentSign = currentLongitude > 0 ? 1 : -1;
599 
600  if ( previousLatitude == currentLatitude ) {
601  followLatitudeCircle = true;
602 
603  lonDiff = currentLongitude - previousLongitude;
604  if ( previousSign != currentSign
605  && fabs(previousLongitude) + fabs(currentLongitude) > M_PI ) {
606  if ( previousSign > currentSign ) {
607  // going eastwards ->
608  lonDiff += 2 * M_PI ;
609  } else {
610  // going westwards ->
611  lonDiff -= 2 * M_PI;
612  }
613  }
614 
615  }
616  else {
617 // mDebug() << "Don't FollowLatitudeCircle";
618  }
619  }
620 
621  GeoDataCoordinates horizonCoords;
622 
623  if ( followLatitudeCircle ) {
624  // To tessellate along latitude circles use the
625  // linear interpolation of the longitude.
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;
630 
631  horizonCoords = GeoDataCoordinates(lon, lat, altitude);
632  }
633  else {
634  // To tessellate along great circles use the
635  // normalized linear interpolation ("NLERP") for latitude and longitude.
636  horizonCoords = previousCoords.nlerp(currentCoords, 0.5);
637  }
638 
639  bool horizonHide = globeHidesPoint( horizonCoords, viewport );
640 
641  if ( horizonHide != currentHide ) {
642  return doFindHorizon(horizonCoords, currentCoords, viewport, f, currentHide, recursionCounter);
643  }
644 
645  return doFindHorizon(previousCoords, horizonCoords, viewport, f, horizonHide, recursionCounter);
646 }
647 
648 
649 bool AzimuthalProjectionPrivate::globeHidesPoint( const GeoDataCoordinates &coordinates,
650  const ViewportParams *viewport ) const
651 {
652  bool globeHidesPoint;
653  qreal dummyX, dummyY;
654 
655  Q_Q( const AzimuthalProjection );
656  q->screenCoordinates(coordinates, viewport, dummyX, dummyY, globeHidesPoint);
657  return globeHidesPoint;
658 }
659 
660 
661 }
662 
663 
void addEllipse(const QRectF &boundingRectangle)
A 3d point representation.
QVector< GeoDataCoordinates >::ConstIterator constEnd() const
Returns a const iterator that points to the end of the LineString.
void setAltitude(const qreal altitude)
set the altitude of the Point in meters
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).
Binds a QML item to a specific geodetic location in screen coordinates.
int size() const
Returns the number of nodes in a LineString.
T & last()
A base class for the Gnomonic and Orthographic (Globe) projections in Marble.
const GeoDataLatLonAltBox & latLonAltBox() const override
Returns the smallest latLonAltBox that contains the LineString.
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...
TessellationFlags tessellationFlags() const
Returns the tessellation flags for a LineString.
virtual bool isClosed() const
Returns whether a LineString is a closed polygon.
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.
GeoDataCoordinates nlerp(const GeoDataCoordinates &target, double t) const
nlerp (normalized linear interpolation) between this coordinates and the given target coordinates ...
void addRect(const QRectF &rectangle)
void pop_back()
static qreal normalizeLat(qreal lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize latitude to always be in -M_PI / 2.
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.
QPainterPath mapShape(const ViewportParams *viewport) const override
Returns the shape/outline of a map projection.
void reserve(int size)
This file contains the headers for ViewportParams.
qreal west(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the western boundary of the bounding box.
AbstractProjection()
Construct a new AbstractProjection.
void geoCoordinates(qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit) const
use this function to get the longitude and latitude with one call - use the unit parameter to switch ...
QVector< GeoDataCoordinates >::ConstIterator constBegin() const
Returns a const iterator that points to the begin of the LineString.
bool isEmpty() const const
A base class for all projections in Marble.
This file contains the headers for AzimuthalProjection.
qreal longitude(GeoDataCoordinates::Unit unit) const
retrieves the longitude of the GeoDataCoordinates object use the unit parameter to switch between Rad...
qreal latitude(GeoDataCoordinates::Unit unit) const
retrieves the latitude of the GeoDataCoordinates object use the unit parameter to switch between Radi...
bool mapCoversViewport(const ViewportParams *viewport) const override
Returns whether the projected data fully obstructs the current viewport.
int size() const const
qreal minLat() const
Returns the arbitrarily chosen minimum (southern) latitude.
bool testFlag(Enum flag) const const
A class that defines a 3D bounding box for geographic data.
bool tessellate() const
Returns whether the LineString follows the earth&#39;s surface.
QPainterPath intersected(const QPainterPath &p) const const
GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const override
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.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Nov 29 2021 23:11:01 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.