• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeedu API Reference
  • KDE Home
  • Contact Us
 

marble

  • sources
  • kde-4.14
  • kdeedu
  • marble
  • src
  • lib
  • marble
  • projections
SphericalProjection.cpp
Go to the documentation of this file.
1 //
2 // This file is part of the Marble Virtual Globe.
3 //
4 // This program is free software licensed under the GNU LGPL. You can
5 // find a copy of this license in LICENSE.txt in the top directory of
6 // the source code.
7 //
8 // Copyright 2007 Inge Wallin <ingwa@kde.org>
9 // Copyright 2007-2012 Torsten Rahn <rahn@kde.org>
10 // Copyright 2009 Patrick Spendrin <ps_ml@gmx.de>
11 // Copyright 2012 Cezar Mocan <mocancezar@gmail.com>
12 //
13 
14 // Local
15 #include "SphericalProjection.h"
16 #include "AbstractProjection_p.h"
17 
18 #include "MarbleDebug.h"
19 
20 // Marble
21 #include "ViewportParams.h"
22 #include "GeoDataPoint.h"
23 #include "GeoDataLineString.h"
24 #include "GeoDataCoordinates.h"
25 #include "MarbleGlobal.h"
26 
27 #define SAFE_DISTANCE
28 
29 // Maximum amount of nodes that are created automatically between actual nodes.
30 static const int maxTessellationNodes = 200;
31 
32 namespace Marble
33 {
34 
35 class SphericalProjectionPrivate : public AbstractProjectionPrivate
36 {
37  public:
38  enum LineStringFlag {
39  PlainLineString = 0x00,
40  ClosedLineString = 0x01
41  };
42 
43  explicit SphericalProjectionPrivate( SphericalProjection * parent );
44 
45  // This method tessellates a line segment in a way that the line segment
46  // follows great circles. The count parameter specifies the
47  // number of nodes generated for the polygon. If the
48  // clampToGround flag is added the polygon contains count + 2
49  // nodes as the clamped down start and end node get added.
50 
51  void tessellateLineSegment( const GeoDataCoordinates &aCoords,
52  qreal ax, qreal ay,
53  const GeoDataCoordinates &bCoords,
54  qreal bx, qreal by,
55  LineStringFlag lineStringFlag,
56  QVector<QPolygonF*> &polygons,
57  const ViewportParams *viewport,
58  TessellationFlags f = 0 ) const;
59 
60  void processTessellation( const GeoDataCoordinates &previousCoords,
61  const GeoDataCoordinates &currentCoords,
62  int count,
63  LineStringFlag lineStringFlag,
64  QVector<QPolygonF*> &polygons,
65  const ViewportParams *viewport,
66  TessellationFlags f = 0 ) const;
67 
68  void crossHorizon( const GeoDataCoordinates & bCoord,
69  LineStringFlag lineStringFlag,
70  QVector<QPolygonF*> &polygons,
71  const ViewportParams *viewport ) const;
72 
73  virtual bool lineStringToPolygon( const GeoDataLineString &lineString,
74  const ViewportParams *viewport,
75  QVector<QPolygonF*> &polygons ) const;
76 
77  void horizonToPolygon( const ViewportParams *viewport,
78  const GeoDataCoordinates & disappearCoords,
79  const GeoDataCoordinates & reappearCoords,
80  QPolygonF* ) const;
81 
82  GeoDataCoordinates findHorizon( const GeoDataCoordinates & previousCoords,
83  const GeoDataCoordinates & currentCoords,
84  const ViewportParams *viewport,
85  TessellationFlags f = 0,
86  int recursionCounter = 0 ) const;
87 
88  static bool globeHidesPoint( const GeoDataCoordinates &coordinates,
89  const ViewportParams *viewport );
90 
91  Q_DECLARE_PUBLIC( SphericalProjection )
92 };
93 
94 SphericalProjection::SphericalProjection()
95  : AbstractProjection( new SphericalProjectionPrivate( this ) )
96 {
97  setMinLat( minValidLat() );
98  setMaxLat( maxValidLat() );
99 }
100 
101 SphericalProjection::SphericalProjection( SphericalProjectionPrivate *dd )
102  : AbstractProjection( dd )
103 {
104  setMinLat( minValidLat() );
105  setMaxLat( maxValidLat() );
106 }
107 
108 SphericalProjection::~SphericalProjection()
109 {
110 }
111 
112 
113 SphericalProjectionPrivate::SphericalProjectionPrivate( SphericalProjection * parent )
114  : AbstractProjectionPrivate( parent )
115 {
116 
117 }
118 
119 
120 bool SphericalProjection::repeatableX() const
121 {
122  return false;
123 }
124 
125 qreal SphericalProjection::maxValidLat() const
126 {
127  return +90.0 * DEG2RAD;
128 }
129 
130 qreal SphericalProjection::minValidLat() const
131 {
132  return -90.0 * DEG2RAD;
133 }
134 
135 bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates,
136  const ViewportParams *viewport,
137  qreal &x, qreal &y, bool &globeHidesPoint ) const
138 {
139  qreal absoluteAltitude = coordinates.altitude() + EARTH_RADIUS;
140  Quaternion qpos = coordinates.quaternion();
141 
142  qpos.rotateAroundAxis( viewport->planetAxisMatrix() );
143 
144  qreal pixelAltitude = ( ( viewport->radius() )
145  / EARTH_RADIUS * absoluteAltitude );
146  if ( coordinates.altitude() < 10000 ) {
147  // Skip placemarks at the other side of the earth.
148  if ( qpos.v[Q_Z] < 0 ) {
149  globeHidesPoint = true;
150  return false;
151  }
152  }
153  else {
154  qreal earthCenteredX = pixelAltitude * qpos.v[Q_X];
155  qreal earthCenteredY = pixelAltitude * qpos.v[Q_Y];
156  qreal radius = viewport->radius();
157 
158  // Don't draw high placemarks (e.g. satellites) that aren't visible.
159  if ( qpos.v[Q_Z] < 0
160  && ( ( earthCenteredX * earthCenteredX
161  + earthCenteredY * earthCenteredY )
162  < radius * radius ) ) {
163  globeHidesPoint = true;
164  return false;
165  }
166  }
167 
168  // Let (x, y) be the position on the screen of the placemark..
169  x = ((qreal)(viewport->width()) / 2 + pixelAltitude * qpos.v[Q_X]);
170  y = ((qreal)(viewport->height()) / 2 - pixelAltitude * qpos.v[Q_Y]);
171 
172  // Skip placemarks that are outside the screen area
173  if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) {
174  globeHidesPoint = false;
175  return false;
176  }
177 
178  globeHidesPoint = false;
179  return true;
180 }
181 
182 bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates,
183  const ViewportParams *viewport,
184  qreal *x, qreal &y,
185  int &pointRepeatNum,
186  const QSizeF& size,
187  bool &globeHidesPoint ) const
188 {
189  pointRepeatNum = 0;
190  bool visible = screenCoordinates( coordinates, viewport, *x, y, globeHidesPoint );
191 
192  // Skip placemarks that are outside the screen area
193  if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0
194  || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 )
195  {
196  globeHidesPoint = false;
197  return false;
198  }
199 
200  // This projection doesn't have any repetitions,
201  // so the number of screen points referring to the geopoint is one.
202  pointRepeatNum = 1;
203  return visible;
204 }
205 
206 
207 bool SphericalProjection::geoCoordinates( const int x, const int y,
208  const ViewportParams *viewport,
209  qreal& lon, qreal& lat,
210  GeoDataCoordinates::Unit unit ) const
211 {
212  const qreal inverseRadius = 1.0 / (qreal)(viewport->radius());
213 
214  const qreal qx = +(qreal)( x - viewport->width() / 2 ) * inverseRadius;
215  const qreal qy = -(qreal)( y - viewport->height() / 2 ) * inverseRadius;
216 
217  if ( 1 <= qx * qx + qy * qy ) {
218  return false;
219  }
220 
221  const qreal qz = sqrt( 1 - qx * qx - qy * qy );
222 
223  Quaternion qpos( 0.0, qx, qy, qz );
224  qpos.rotateAroundAxis( viewport->planetAxis() );
225  qpos.getSpherical( lon, lat );
226 
227  if ( unit == GeoDataCoordinates::Degree ) {
228  lon *= RAD2DEG;
229  lat *= RAD2DEG;
230  }
231 
232  return true;
233 }
234 
235 GeoDataLatLonAltBox SphericalProjection::latLonAltBox( const QRect& screenRect,
236  const ViewportParams *viewport ) const
237 {
238  // For the case where the whole viewport gets covered there is a
239  // pretty dirty and generic detection algorithm:
240  GeoDataLatLonAltBox latLonAltBox = AbstractProjection::latLonAltBox( screenRect, viewport );
241 
242  // If the whole globe is visible we can easily calculate
243  // analytically the lon-/lat- range.
244  qreal pitch = GeoDataCoordinates::normalizeLat( viewport->planetAxis().pitch() );
245 
246  if ( 2.0 * viewport->radius() <= viewport->height()
247  && 2.0 * viewport->radius() <= viewport->width() )
248  {
249  // Unless the planetaxis is in the screen plane the allowed longitude range
250  // covers full -180 deg to +180 deg:
251  if ( pitch > 0.0 && pitch < +M_PI ) {
252  latLonAltBox.setNorth( +fabs( M_PI / 2.0 - fabs( pitch ) ) );
253  latLonAltBox.setSouth( -M_PI / 2.0 );
254  }
255  if ( pitch < 0.0 && pitch > -M_PI ) {
256  latLonAltBox.setWest( -M_PI );
257  latLonAltBox.setEast( +M_PI );
258  latLonAltBox.setNorth( +M_PI / 2.0 );
259  latLonAltBox.setSouth( -fabs( M_PI / 2.0 - fabs( pitch ) ) );
260  }
261 
262  // Last but not least we deal with the rare case where the
263  // globe is fully visible and pitch = 0.0 or pitch = -M_PI or
264  // pitch = +M_PI
265  if ( pitch == 0.0 || pitch == -M_PI || pitch == +M_PI ) {
266  qreal yaw = viewport->planetAxis().yaw();
267  latLonAltBox.setWest( GeoDataCoordinates::normalizeLon( yaw - M_PI / 2.0 ) );
268  latLonAltBox.setEast( GeoDataCoordinates::normalizeLon( yaw + M_PI / 2.0 ) );
269  latLonAltBox.setNorth( +M_PI / 2.0 );
270  latLonAltBox.setSouth( -M_PI / 2.0 );
271  }
272  }
273 
274  // Now we check whether maxLat (e.g. the north pole) gets displayed
275  // inside the viewport to get more accurate values for east and west.
276 
277  // We need a point on the screen at maxLat that definitely gets displayed:
278  qreal averageLongitude = ( latLonAltBox.west() + latLonAltBox.east() ) / 2.0;
279 
280  GeoDataCoordinates maxLatPoint( averageLongitude, maxLat(), 0.0, GeoDataCoordinates::Radian );
281  GeoDataCoordinates minLatPoint( averageLongitude, minLat(), 0.0, GeoDataCoordinates::Radian );
282 
283  qreal dummyX, dummyY; // not needed
284  bool dummyVal;
285 
286  if ( screenCoordinates( maxLatPoint, viewport, dummyX, dummyY, dummyVal ) ||
287  screenCoordinates( minLatPoint, viewport, dummyX, dummyY, dummyVal ) ) {
288  latLonAltBox.setWest( -M_PI );
289  latLonAltBox.setEast( +M_PI );
290  }
291 
292  return latLonAltBox;
293 }
294 
295 
296 bool SphericalProjection::mapCoversViewport( const ViewportParams *viewport ) const
297 {
298  qint64 radius = viewport->radius();
299  qint64 width = viewport->width();
300  qint64 height = viewport->height();
301 
302  // This first test is a quick one that will catch all really big
303  // radii and prevent overflow in the real test.
304  if ( radius > width + height )
305  return true;
306 
307  // This is the real test. The 4 is because we are really
308  // comparing to width/2 and height/2.
309  if ( 4 * radius * radius >= width * width + height * height )
310  return true;
311 
312  return false;
313 }
314 
315 QPainterPath SphericalProjection::mapShape( const ViewportParams *viewport ) const
316 {
317  int radius = viewport->radius();
318  int imgWidth = viewport->width();
319  int imgHeight = viewport->height();
320 
321  QPainterPath fullRect;
322  fullRect.addRect( 0 , 0 , imgWidth, imgHeight );
323 
324  // If the globe covers the whole image, then the projected region represents
325  // all of the image.
326  // Otherwise the active region has got the shape of the visible globe.
327 
328  if ( !viewport->mapCoversViewport() ) {
329  QPainterPath mapShape;
330  mapShape.addEllipse(
331  imgWidth / 2 - radius,
332  imgHeight / 2 - radius,
333  2 * radius,
334  2 * radius );
335  return mapShape.intersected( fullRect );
336  }
337 
338  return fullRect;
339 }
340 
341 bool SphericalProjection::screenCoordinates( const GeoDataLineString &lineString,
342  const ViewportParams *viewport,
343  QVector<QPolygonF *> &polygons ) const
344 {
345 
346  Q_D( const SphericalProjection );
347  // Compare bounding box size of the line string with the angularResolution
348  // Immediately return if the latLonAltBox is smaller.
349  if ( !viewport->resolves( lineString.latLonAltBox() ) ) {
350 // mDebug() << "Object too small to be resolved";
351  return false;
352  }
353 
354  d->lineStringToPolygon( lineString, viewport, polygons );
355  return true;
356 }
357 
358 void SphericalProjectionPrivate::tessellateLineSegment( const GeoDataCoordinates &aCoords,
359  qreal ax, qreal ay,
360  const GeoDataCoordinates &bCoords,
361  qreal bx, qreal by,
362  LineStringFlag lineStringFlag,
363  QVector<QPolygonF*> &polygons,
364  const ViewportParams *viewport,
365  TessellationFlags f) const
366 {
367  // We take the manhattan length as a distance approximation
368  // that can be too big by a factor of sqrt(2)
369  qreal distance = fabs((bx - ax)) + fabs((by - ay));
370 #ifdef SAFE_DISTANCE
371  // Interpolate additional nodes if the line segment that connects the
372  // current or previous nodes might cross the viewport.
373  // The latter can pretty safely be excluded for most projections if both points
374  // are located on the same side relative to the viewport boundaries and if they are
375  // located more than half the line segment distance away from the viewport.
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() )
383  )
384  {
385 #endif
386  bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
387  int const finalTessellationPrecision = smallScreen ? 3 * tessellationPrecision : tessellationPrecision;
388 
389  // Let the line segment follow the spherical surface
390  // if the distance between the previous point and the current point
391  // on screen is too big
392 
393  if ( distance > finalTessellationPrecision ) {
394  const int tessellatedNodes = qMin<int>( distance / finalTessellationPrecision, maxTessellationNodes );
395 
396  processTessellation( aCoords, bCoords,
397  tessellatedNodes,
398  lineStringFlag,
399  polygons,
400  viewport,
401  f );
402  }
403  else {
404  crossHorizon( bCoords, lineStringFlag, polygons, viewport );
405  }
406 #ifdef SAFE_DISTANCE
407  }
408 #endif
409 }
410 
411 
412 void SphericalProjectionPrivate::processTessellation( const GeoDataCoordinates &previousCoords,
413  const GeoDataCoordinates &currentCoords,
414  int tessellatedNodes,
415  LineStringFlag lineStringFlag,
416  QVector<QPolygonF*> &polygons,
417  const ViewportParams *viewport,
418  TessellationFlags f) const
419 {
420 
421  const bool clampToGround = f.testFlag( FollowGround );
422  const bool followLatitudeCircle = f.testFlag( RespectLatitudeCircle )
423  && previousCoords.latitude() == currentCoords.latitude();
424 
425  // Calculate steps for tessellation: lonDiff and altDiff
426  qreal lonDiff = 0.0;
427  if ( followLatitudeCircle ) {
428  const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
429  const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
430 
431  lonDiff = currentCoords.longitude() - previousCoords.longitude();
432  if ( previousSign != currentSign
433  && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > M_PI ) {
434  if ( previousSign > currentSign ) {
435  // going eastwards ->
436  lonDiff += 2 * M_PI ;
437  } else {
438  // going westwards ->
439  lonDiff -= 2 * M_PI;
440  }
441  }
442  }
443 
444  const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
445 
446  // Create the tessellation nodes.
447  GeoDataCoordinates previousTessellatedCoords = previousCoords;
448  for ( int i = 1; i <= tessellatedNodes; ++i ) {
449  const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 );
450 
451  // interpolate the altitude, too
452  const qreal altitude = clampToGround ? 0 : altDiff * t + previousCoords.altitude();
453 
454  qreal lon = 0.0;
455  qreal lat = 0.0;
456  if ( followLatitudeCircle ) {
457  // To tessellate along latitude circles use the
458  // linear interpolation of the longitude.
459  lon = lonDiff * t + previousCoords.longitude();
460  lat = previousTessellatedCoords.latitude();
461  }
462  else {
463  // To tessellate along great circles use the
464  // normalized linear interpolation ("NLERP") for latitude and longitude.
465  const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), currentCoords.quaternion(), t );
466  itpos. getSpherical( lon, lat );
467  }
468 
469  const GeoDataCoordinates currentTessellatedCoords( lon, lat, altitude );
470  crossHorizon( currentTessellatedCoords, lineStringFlag, polygons, viewport );
471  previousTessellatedCoords = currentTessellatedCoords;
472  }
473 
474  // For the clampToGround case add the "current" coordinate after adding all other nodes.
475  GeoDataCoordinates currentModifiedCoords( currentCoords );
476  if ( clampToGround ) {
477  currentModifiedCoords.setAltitude( 0.0 );
478  }
479  crossHorizon( currentModifiedCoords, lineStringFlag, polygons, viewport );
480 }
481 
482 void SphericalProjectionPrivate::crossHorizon( const GeoDataCoordinates & bCoord,
483  LineStringFlag lineStringFlag,
484  QVector<QPolygonF*> &polygons,
485  const ViewportParams *viewport ) const
486 {
487  qreal x, y;
488  bool globeHidesPoint;
489 
490  Q_Q( const AbstractProjection );
491 
492  q->screenCoordinates( bCoord, viewport, x, y, globeHidesPoint );
493 
494  if( !globeHidesPoint ) {
495  *polygons.last() << QPointF( x, y );
496  }
497  else {
498  if ( !polygons.last()->isEmpty() && lineStringFlag != ClosedLineString ) {
499  QPolygonF *path = new QPolygonF;
500  polygons.append( path );
501  }
502  }
503 }
504 
505 bool SphericalProjectionPrivate::lineStringToPolygon( const GeoDataLineString &lineString,
506  const ViewportParams *viewport,
507  QVector<QPolygonF *> &polygons ) const
508 {
509  Q_Q( const SphericalProjection );
510 
511  const TessellationFlags f = lineString.tessellationFlags();
512 
513  qreal x = 0;
514  qreal y = 0;
515  bool globeHidesPoint = false;
516 
517  qreal previousX = -1.0;
518  qreal previousY = -1.0;
519  bool previousGlobeHidesPoint = false;
520 
521  qreal horizonX = -1.0;
522  qreal horizonY = -1.0;
523 
524  polygons.append( new QPolygonF );
525 
526  GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
527  GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
528 
529  // Some projections display the earth in a way so that there is a
530  // foreside and a backside.
531  // The horizon is the line (usually a circle) which separates both
532  // sides from each other and resembles the map shape.
533  GeoDataCoordinates horizonCoords;
534 
535  // A horizon pair is a pair of two subsequent horizon crossings:
536  // The first one describes the point where a line string disappears behind the horizon.
537  // and where horizonPair is set to true.
538  // The second one describes the point where the line string reappears.
539  // In this case the two points are connected and horizonPair is set to false again.
540  bool horizonPair = false;
541  GeoDataCoordinates horizonDisappearCoords;
542 
543  // If the first horizon crossing in a line string describes the appearance of
544  // a line string then we call it a "horizon orphan" and horizonOrphan is set to true.
545  // In this case once the last horizon crossing in the line string is reached
546  // it needs to be connected to the orphan.
547  bool horizonOrphan = false;
548  GeoDataCoordinates horizonOrphanCoords;
549 
550  GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
551  GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
552 
553  bool processingLastNode = false;
554 
555  // We use a while loop to be able to cover linestrings as well as linear rings:
556  // Linear rings require to tessellate the path from the last node to the first node
557  // which isn't really convenient to achieve with a for loop ...
558 
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 :
565  0;
566 
567  while ( itCoords != itEnd )
568  {
569 
570  // Optimization for line strings with a big amount of nodes
571  bool skipNode = itCoords != itBegin && isLong && !processingLastNode &&
572  ( (*itCoords).detail() > maximumDetail
573  || viewport->resolves( *itPreviousCoords, *itCoords ) );
574 
575  if ( !skipNode ) {
576 
577  q->screenCoordinates( *itCoords, viewport, x, y, globeHidesPoint );
578 
579  // Initializing variables that store the values of the previous iteration
580  if ( !processingLastNode && itCoords == itBegin ) {
581  previousGlobeHidesPoint = globeHidesPoint;
582  itPreviousCoords = itCoords;
583  previousX = x;
584  previousY = y;
585  }
586 
587  // Check for the "horizon case" (which is present e.g. for the spherical projection
588  const bool isAtHorizon = ( globeHidesPoint || previousGlobeHidesPoint ) &&
589  ( globeHidesPoint != previousGlobeHidesPoint );
590 
591  if ( isAtHorizon ) {
592  // Handle the "horizon case"
593  horizonCoords = findHorizon( *itPreviousCoords, *itCoords, viewport, f );
594 
595  if ( lineString.isClosed() ) {
596  if ( horizonPair ) {
597  horizonToPolygon( viewport, horizonDisappearCoords, horizonCoords, polygons.last() );
598  horizonPair = false;
599  }
600  else {
601  if ( globeHidesPoint ) {
602  horizonDisappearCoords = horizonCoords;
603  horizonPair = true;
604  }
605  else {
606  horizonOrphanCoords = horizonCoords;
607  horizonOrphan = true;
608  }
609  }
610  }
611 
612  q->screenCoordinates( horizonCoords, viewport, horizonX, horizonY );
613 
614  // If the line appears on the visible half we need
615  // to add an interpolated point at the horizon as the previous point.
616  if ( previousGlobeHidesPoint ) {
617  *polygons.last() << QPointF( horizonX, horizonY );
618  }
619  }
620 
621  // This if-clause contains the section that tessellates the line
622  // segments of a linestring. If you are about to learn how the code of
623  // this class works you can safely ignore this section for a start.
624 
625  if ( lineString.tessellate() ) {
626  LineStringFlag lineStringFlag = lineString.isClosed() ? ClosedLineString : PlainLineString;
627  if ( !isAtHorizon ) {
628  tessellateLineSegment( *itPreviousCoords, previousX, previousY,
629  *itCoords, x, y,
630  lineStringFlag, polygons, viewport,
631  f );
632  }
633  else {
634  // Connect the interpolated point at the horizon with the
635  // current or previous point in the line.
636  if ( previousGlobeHidesPoint ) {
637  tessellateLineSegment( horizonCoords, horizonX, horizonY,
638  *itCoords, x, y,
639  lineStringFlag, polygons, viewport,
640  f );
641  }
642  else {
643  tessellateLineSegment( *itPreviousCoords, previousX, previousY,
644  horizonCoords, horizonX, horizonY,
645  lineStringFlag, polygons, viewport,
646  f );
647  }
648  }
649  }
650  else {
651  if ( !globeHidesPoint ) {
652  *polygons.last() << QPointF( x, y );
653  }
654  else {
655  if ( !previousGlobeHidesPoint && isAtHorizon ) {
656  *polygons.last() << QPointF( horizonX, horizonY );
657  }
658  }
659  }
660 
661  if ( globeHidesPoint ) {
662  if ( !previousGlobeHidesPoint
663  && !lineString.isClosed()
664  ) {
665  polygons.append( new QPolygonF );
666  }
667  }
668 
669  previousGlobeHidesPoint = globeHidesPoint;
670  itPreviousCoords = itCoords;
671  previousX = x;
672  previousY = y;
673  }
674 
675  // Here we modify the condition to be able to process the
676  // first node after the last node in a LinearRing.
677 
678  if ( processingLastNode ) {
679  break;
680  }
681  ++itCoords;
682 
683  if ( itCoords == itEnd && lineString.isClosed() ) {
684  itCoords = itBegin;
685  processingLastNode = true;
686  }
687  }
688 
689  // In case of horizon crossings, make sure that we always get a
690  // polygon closed correctly.
691  if ( horizonOrphan && lineString.isClosed() ) {
692  horizonToPolygon( viewport, horizonCoords, horizonOrphanCoords, polygons.last() );
693  }
694 
695  if ( polygons.last()->size() <= 1 ){
696  polygons.pop_back(); // Clean up "unused" empty polygon instances
697  }
698 
699  return polygons.isEmpty();
700 }
701 
702 void SphericalProjectionPrivate::horizonToPolygon( const ViewportParams *viewport,
703  const GeoDataCoordinates & disappearCoords,
704  const GeoDataCoordinates & reappearCoords,
705  QPolygonF * polygon ) const
706 {
707  qreal x, y;
708 
709  const qreal imageHalfWidth = viewport->width() / 2;
710  const qreal imageHalfHeight = viewport->height() / 2;
711 
712  bool dummyGlobeHidesPoint = false;
713 
714  Q_Q( const SphericalProjection );
715  // Calculate the angle of the position vectors of both coordinates
716  q->screenCoordinates( disappearCoords, viewport, x, y, dummyGlobeHidesPoint );
717  qreal alpha = atan2( y - imageHalfHeight,
718  x - imageHalfWidth );
719 
720  q->screenCoordinates( reappearCoords, viewport, x, y, dummyGlobeHidesPoint );
721  qreal beta = atan2( y - imageHalfHeight,
722  x - imageHalfWidth );
723 
724  // Calculate the difference between both
725  qreal diff = GeoDataCoordinates::normalizeLon( beta - alpha );
726 
727  qreal sgndiff = diff < 0 ? -1 : 1;
728 
729  const qreal arcradius = viewport->radius();
730  const int itEnd = fabs(diff * RAD2DEG);
731 
732  // Create a polygon that resembles an arc between the two position vectors
733  for ( int it = 1; it <= itEnd; ++it ) {
734  const qreal angle = alpha + DEG2RAD * sgndiff * it;
735  const qreal itx = imageHalfWidth + arcradius * cos( angle );
736  const qreal ity = imageHalfHeight + arcradius * sin( angle );
737  *polygon << QPointF( itx, ity );
738  }
739 }
740 
741 
742 GeoDataCoordinates SphericalProjectionPrivate::findHorizon( const GeoDataCoordinates & previousCoords,
743  const GeoDataCoordinates & currentCoords,
744  const ViewportParams *viewport,
745  TessellationFlags f,
746  int recursionCounter ) const
747 {
748  bool currentHide = globeHidesPoint( currentCoords, viewport ) ;
749 
750  if ( recursionCounter > 20 ) {
751  return currentHide ? previousCoords : currentCoords;
752  }
753  ++recursionCounter;
754 
755  bool followLatitudeCircle = false;
756 
757  // Calculate steps for tessellation: lonDiff and altDiff
758  qreal lonDiff = 0.0;
759  qreal previousLongitude = 0.0;
760  qreal previousLatitude = 0.0;
761 
762  if ( f.testFlag( RespectLatitudeCircle ) ) {
763  previousCoords.geoCoordinates( previousLongitude, previousLatitude );
764  qreal previousSign = previousLongitude > 0 ? 1 : -1;
765 
766  qreal currentLongitude = 0.0;
767  qreal currentLatitude = 0.0;
768  currentCoords.geoCoordinates( currentLongitude, currentLatitude );
769  qreal currentSign = currentLongitude > 0 ? 1 : -1;
770 
771  if ( previousLatitude == currentLatitude ) {
772  followLatitudeCircle = true;
773 
774  lonDiff = currentLongitude - previousLongitude;
775  if ( previousSign != currentSign
776  && fabs(previousLongitude) + fabs(currentLongitude) > M_PI ) {
777  if ( previousSign > currentSign ) {
778  // going eastwards ->
779  lonDiff += 2 * M_PI ;
780  } else {
781  // going westwards ->
782  lonDiff -= 2 * M_PI;
783  }
784  }
785 
786  }
787  else {
788 // mDebug() << "Don't FollowLatitudeCircle";
789  }
790  }
791 
792  qreal lon = 0.0;
793  qreal lat = 0.0;
794 
795  qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
796 
797  if ( followLatitudeCircle ) {
798  // To tessellate along latitude circles use the
799  // linear interpolation of the longitude.
800  lon = lonDiff * 0.5 + previousLongitude;
801  lat = previousLatitude;
802  }
803  else {
804  // To tessellate along great circles use the
805  // normalized linear interpolation ("NLERP") for latitude and longitude.
806  const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), currentCoords.quaternion(), 0.5 );
807  itpos. getSpherical( lon, lat );
808  }
809 
810  qreal altitude = previousCoords.altitude() + 0.5 * altDiff;
811 
812  GeoDataCoordinates horizonCoords( lon, lat, altitude );
813 
814  bool horizonHide = globeHidesPoint( horizonCoords, viewport );
815 
816  if ( horizonHide != currentHide ) {
817  return findHorizon( horizonCoords, currentCoords, viewport, f, recursionCounter );
818  }
819 
820  return findHorizon( previousCoords, horizonCoords, viewport, f, recursionCounter );
821 }
822 
823 
824 bool SphericalProjectionPrivate::globeHidesPoint( const GeoDataCoordinates &coordinates,
825  const ViewportParams *viewport )
826 {
827  qreal absoluteAltitude = coordinates.altitude() + EARTH_RADIUS;
828  Quaternion qpos = coordinates.quaternion();
829 
830  qpos.rotateAroundAxis( viewport->planetAxisMatrix() );
831 
832  qreal pixelAltitude = ( ( viewport->radius() )
833  / EARTH_RADIUS * absoluteAltitude );
834  if ( coordinates.altitude() < 10000 ) {
835  // Skip placemarks at the other side of the earth.
836  if ( qpos.v[Q_Z] < 0 ) {
837  return true;
838  }
839  }
840  else {
841  qreal earthCenteredX = pixelAltitude * qpos.v[Q_X];
842  qreal earthCenteredY = pixelAltitude * qpos.v[Q_Y];
843  qreal radius = viewport->radius();
844 
845  // Don't draw high placemarks (e.g. satellites) that aren't visible.
846  if ( qpos.v[Q_Z] < 0
847  && ( ( earthCenteredX * earthCenteredX
848  + earthCenteredY * earthCenteredY )
849  < radius * radius ) ) {
850  return true;
851  }
852  }
853 
854  return false;
855 }
856 
857 
858 
859 }
Marble::GeoDataCoordinates::Unit
Unit
enum used constructor to specify the units used
Definition: GeoDataCoordinates.h:64
QPainterPath::addEllipse
void addEllipse(const QRectF &boundingRectangle)
GeoDataCoordinates.h
Marble::RAD2DEG
const qreal RAD2DEG
Definition: MarbleGlobal.h:220
Marble::SphericalProjection::screenCoordinates
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.
Definition: SphericalProjection.cpp:135
Marble::AbstractProjectionPrivate
Definition: AbstractProjection_p.h:20
Marble::GeoDataCoordinates
A 3d point representation.
Definition: GeoDataCoordinates.h:52
Marble::GeoDataCoordinates::Radian
Definition: GeoDataCoordinates.h:65
Marble::GeoDataLatLonBox::setNorth
void setNorth(const qreal north, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
Definition: GeoDataLatLonBox.cpp:101
angle
double angle(double vec1[3], double vec2[3])
Definition: sgp4ext.cpp:164
AbstractProjection_p.h
QVector::append
void append(const T &value)
Marble::GeoDataCoordinates::normalizeLon
static qreal normalizeLon(qreal lon, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
Definition: GeoDataCoordinates.cpp:776
QVector::last
T & last()
Marble::SphericalProjection::minValidLat
virtual qreal minValidLat() const
Definition: SphericalProjection.cpp:130
Marble::AbstractProjection::maxLat
qreal maxLat() const
Definition: AbstractProjection.cpp:54
Marble::GeoDataLatLonBox::setSouth
void setSouth(const qreal south, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
Definition: GeoDataLatLonBox.cpp:122
Marble::SphericalProjection::geoCoordinates
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.
Definition: SphericalProjection.cpp:207
Marble::AbstractProjection::latLonAltBox
virtual GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const
Definition: AbstractProjection.cpp:136
SphericalProjection.h
This file contains the headers for SphericalProjection.
Marble::GeoDataLatLonBox::setWest
void setWest(const qreal west, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
Definition: GeoDataLatLonBox.cpp:164
Marble::Quaternion::nlerp
static Quaternion nlerp(const Quaternion &q1, const Quaternion &q2, qreal t)
Definition: Quaternion.cpp:231
Marble::GeoDataLineString::ConstIterator
QVector< GeoDataCoordinates >::ConstIterator ConstIterator
Definition: GeoDataLineString.h:80
MarbleDebug.h
Marble::GeoDataCoordinates::Degree
Definition: GeoDataCoordinates.h:66
Marble::ViewportParams::height
int height() const
Definition: ViewportParams.cpp:255
Marble::Quaternion::rotateAroundAxis
void rotateAroundAxis(const Quaternion &q)
Definition: Quaternion.cpp:191
QPointF
Marble::GeoDataCoordinates::altitude
qreal altitude() const
return the altitude of the Point in meters
Definition: GeoDataCoordinates.cpp:1197
Marble::GeoDataLatLonBox::east
qreal east(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the eastern boundary of the bounding box.
Definition: GeoDataLatLonBox.cpp:135
QRect
Marble::SphericalProjection::~SphericalProjection
virtual ~SphericalProjection()
Definition: SphericalProjection.cpp:108
Marble::AbstractProjection::setMaxLat
void setMaxLat(qreal maxLat)
Definition: AbstractProjection.cpp:60
Marble::ViewportParams::width
int width() const
Definition: ViewportParams.cpp:250
Marble::tessellationPrecision
static const int tessellationPrecision
Definition: AbstractProjection.h:37
Marble::EARTH_RADIUS
const qreal EARTH_RADIUS
Definition: MarbleGlobal.h:257
Marble::Quaternion::yaw
qreal yaw() const
Definition: Quaternion.cpp:129
Marble::SphericalProjection::maxValidLat
virtual qreal maxValidLat() const
Definition: SphericalProjection.cpp:125
Marble::ViewportParams::mapCoversViewport
bool mapCoversViewport() const
Definition: ViewportParams.cpp:398
Marble::SphericalProjection::SphericalProjection
SphericalProjection()
Construct a new SphericalProjection.
Definition: SphericalProjection.cpp:94
Marble::radius
static qreal radius(qreal zoom)
Definition: thumbnailer.cpp:99
QPainterPath::addRect
void addRect(const QRectF &rectangle)
Marble::SphericalProjection
A class to implement the spherical projection used by the "Globe" view.
Definition: SphericalProjection.h:36
GeoDataLineString.h
Marble::Q_Z
Definition: Quaternion.h:32
Marble::Quaternion::pitch
qreal pitch() const
Definition: Quaternion.cpp:123
QVector::pop_back
void pop_back()
Marble::GeoDataCoordinates::normalizeLat
static qreal normalizeLat(qreal lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize latitude to always be in -M_PI / 2.
Definition: GeoDataCoordinates.cpp:799
MarbleGlobal.h
Marble::DEG2RAD
const qreal DEG2RAD
Definition: MarbleGlobal.h:219
Marble::ViewportParams::planetAxisMatrix
const matrix & planetAxisMatrix() const
Definition: ViewportParams.cpp:245
Marble::GeoDataLineString
A LineString that allows to store a contiguous set of line segments.
Definition: GeoDataLineString.h:75
Marble::ViewportParams
A public class that controls what is visible in the viewport of a Marble map.
Definition: ViewportParams.h:44
Marble::SphericalProjection::repeatableX
virtual bool repeatableX() const
Definition: SphericalProjection.cpp:120
Marble::Q_Y
Definition: Quaternion.h:31
Marble::SphericalProjection::mapShape
virtual QPainterPath mapShape(const ViewportParams *viewport) const
Definition: SphericalProjection.cpp:315
Marble::Q_X
Definition: Quaternion.h:30
ViewportParams.h
This file contains the headers for ViewportParams.
Marble::MarbleGlobal::SmallScreen
Definition: MarbleGlobal.h:287
Marble::MarbleGlobal::getInstance
static MarbleGlobal * getInstance()
Definition: MarbleGlobal.cpp:37
Marble::RespectLatitudeCircle
Definition: MarbleGlobal.h:33
maxTessellationNodes
static const int maxTessellationNodes
Definition: SphericalProjection.cpp:30
Marble::GeoDataLatLonBox::west
qreal west(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the western boundary of the bounding box.
Definition: GeoDataLatLonBox.cpp:156
QPainterPath
GeoDataPoint.h
QVector
QSizeF
Marble::ViewportParams::radius
int radius() const
Definition: ViewportParams.cpp:195
QVector::isEmpty
bool isEmpty() const
Marble::Quaternion
Definition: Quaternion.h:41
Marble::AbstractProjection
A base class for all projections in Marble.
Definition: AbstractProjection.h:49
Marble::ViewportParams::resolves
bool resolves(const GeoDataLatLonBox &latLonBox) const
Definition: ViewportParams.cpp:330
Marble::MarbleGlobal::profiles
Profiles profiles() const
Definition: MarbleGlobal.cpp:48
Marble::ViewportParams::planetAxis
Quaternion planetAxis() const
Definition: ViewportParams.cpp:240
M_PI
#define M_PI
Definition: GeoDataCoordinates.h:26
Marble::Quaternion::v
xmmfloat v
Definition: Quaternion.h:87
Marble::SphericalProjection::mapCoversViewport
bool mapCoversViewport(const ViewportParams *viewport) const
Definition: SphericalProjection.cpp:296
Marble::FollowGround
Definition: MarbleGlobal.h:34
Marble::Quaternion::getSpherical
void getSpherical(qreal &lon, qreal &lat) const
Definition: Quaternion.cpp:48
QSizeF::height
qreal height() const
Marble::AbstractProjection::minLat
qreal minLat() const
Definition: AbstractProjection.cpp:76
Marble::GeoDataLatLonAltBox
A class that defines a 3D bounding box for geographic data.
Definition: GeoDataLatLonAltBox.h:49
Marble::GeoDataLineString::latLonAltBox
virtual const GeoDataLatLonAltBox & latLonAltBox() const
Returns the smallest latLonAltBox that contains the LineString.
Definition: GeoDataLineString.cpp:580
Marble::AbstractProjection::setMinLat
void setMinLat(qreal minLat)
Definition: AbstractProjection.cpp:82
QSizeF::width
qreal width() const
Marble::SphericalProjection::latLonAltBox
GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const
Definition: SphericalProjection.cpp:235
QPainterPath::intersected
QPainterPath intersected(const QPainterPath &p) const
Marble::GeoDataCoordinates::quaternion
const Quaternion & quaternion() const
return a Quaternion with the used coordinates
Definition: GeoDataCoordinates.cpp:1236
Marble::GeoDataLatLonBox::setEast
void setEast(const qreal east, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
Definition: GeoDataLatLonBox.cpp:143
QPolygonF
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:13:42 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

marble

Skip menu "marble"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdeedu API Reference

Skip menu "kdeedu API Reference"
  • Analitza
  •     lib
  • kalgebra
  • kalzium
  •   libscience
  • kanagram
  • kig
  •   lib
  • klettres
  • marble
  • parley
  • rocs
  •   App
  •   RocsCore
  •   VisualEditor
  •   stepcore

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal