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() ) {
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,
174  TessellationFlags f,
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,
227  TessellationFlags f,
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,
575  TessellationFlags f,
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 
A 3d point representation.
bool isEmpty() const const
A class that defines a 3D bounding box for geographic data.
T & last()
virtual GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const
Returns a GeoDataLatLonAltBox bounding box of the given screenrect inside the given viewport.
qreal east(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the eastern boundary of the bounding box.
void append(const T &value)
A base class for all projections in Marble.
bool mapCoversViewport(const ViewportParams *viewport) const override
Returns whether the projected data fully obstructs the current viewport.
void addEllipse(const QRectF &boundingRectangle)
void pop_back()
QPainterPath mapShape(const ViewportParams *viewport) const override
Returns the shape/outline of a map projection.
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
A public class that controls what is visible in the viewport of a Marble map.
qreal west(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the western boundary of the bounding box.
qreal minLat() const
Returns the arbitrarily chosen minimum (southern) latitude.
void addRect(const QRectF &rectangle)
Binds a QML item to a specific geodetic location in screen coordinates.
void reserve(int size)
QPainterPath intersected(const QPainterPath &p) const const
QString path(const QString &relativePath)
static qreal normalizeLat(qreal lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize latitude to always be in -M_PI / 2.
qreal maxLat() const
Returns the arbitrarily chosen maximum (northern) latitude.
bool isClippedToSphere() const override
Defines whether a projection is supposed to be clipped to a certain radius.
int size() 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.
static qreal normalizeLon(qreal lon, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Nov 29 2023 04:12:17 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.