Advertisement
Guest User

SphericalProjection.cpp

a guest
May 15th, 2012
56
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.44 KB | None | 0 0
  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-2009 Torsten Rahn <rahn@kde.org>
  10. //
  11.  
  12. // Local
  13. #include "SphericalProjection.h"
  14.  
  15. #include "MarbleDebug.h"
  16.  
  17. // Marble
  18. #include "ViewportParams.h"
  19. #include "GeoDataPoint.h"
  20. #include "global.h"
  21.  
  22. #define SAFE_DISTANCE
  23.  
  24. namespace Marble
  25. {
  26.  
  27. // Since SphericalProjection does not have members yet, the
  28. // private class for the members is empty.
  29. // For ABI reasons it is there however, but we do not yet need to
  30. // construct/destruct an object of this class
  31. class SphericalProjectionPrivate
  32. {
  33. };
  34.  
  35. SphericalProjection::SphericalProjection()
  36. : AbstractProjection(),
  37. d( 0 )
  38. {
  39. setRepeatX( repeatableX() );
  40. setMinLat( minValidLat() );
  41. setMaxLat( maxValidLat() );
  42. }
  43.  
  44. SphericalProjection::~SphericalProjection()
  45. {
  46. //For the future
  47. //delete d;
  48. }
  49.  
  50. bool SphericalProjection::repeatableX() const
  51. {
  52. return false;
  53. }
  54.  
  55. qreal SphericalProjection::maxValidLat() const
  56. {
  57. return +90.0 * DEG2RAD;
  58. }
  59.  
  60. qreal SphericalProjection::minValidLat() const
  61. {
  62. return -90.0 * DEG2RAD;
  63. }
  64.  
  65. bool SphericalProjection::screenCoordinates( const qreal lon, const qreal lat,
  66. const ViewportParams *viewport,
  67. qreal& x, qreal& y ) const
  68. {
  69. Quaternion p = Quaternion::fromSpherical( lon, lat );
  70. p.rotateAroundAxis( viewport->planetAxis().inverse() );
  71.  
  72. x = ( viewport->width() / 2 + (qreal)( viewport->radius() ) * p.v[Q_X] );
  73. y = ( viewport->height() / 2 - (qreal)( viewport->radius() ) * p.v[Q_Y] );
  74.  
  75. return ( ( 0 <= y && y < viewport->height() )
  76. && ( 0 <= x && x < viewport->width() )
  77. && p.v[Q_Z] > 0 );
  78. }
  79.  
  80. bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates,
  81. const ViewportParams *viewport,
  82. qreal &x, qreal &y, bool &globeHidesPoint ) const
  83. {
  84. qDebug() << "SphericalProjection::screenCoordinates() lat: " << coordinates.latitude() << " lon: " << coordinates.longitude() << " type: " << coordinates.getType();
  85. qreal absoluteAltitude = coordinates.altitude() + EARTH_RADIUS;
  86. Quaternion qpos = coordinates.quaternion();
  87.  
  88. if (coordinates.getType())
  89. viewport->setClock(coordinates.getClock());
  90. qpos.rotateAroundAxis( *( viewport->planetAxisMatrix(coordinates.getType() ) ));
  91.  
  92. qreal pixelAltitude = ( ( viewport->radius() )
  93. / EARTH_RADIUS * absoluteAltitude );
  94. if ( coordinates.altitude() < 10000 ) {
  95. // Skip placemarks at the other side of the earth.
  96. if ( qpos.v[Q_Z] < 0 ) {
  97. globeHidesPoint = true;
  98. return false;
  99. }
  100. }
  101. else {
  102. qreal earthCenteredX = pixelAltitude * qpos.v[Q_X];
  103. qreal earthCenteredY = pixelAltitude * qpos.v[Q_Y];
  104. qreal radius = viewport->radius();
  105.  
  106. // Don't draw high placemarks (e.g. satellites) that aren't visible.
  107. if ( qpos.v[Q_Z] < 0
  108. && ( ( earthCenteredX * earthCenteredX
  109. + earthCenteredY * earthCenteredY )
  110. < radius * radius ) ) {
  111. globeHidesPoint = true;
  112. return false;
  113. }
  114. }
  115.  
  116. // Let (x, y) be the position on the screen of the placemark..
  117. x = ((qreal)(viewport->width()) / 2 + pixelAltitude * qpos.v[Q_X]);
  118. y = ((qreal)(viewport->height()) / 2 - pixelAltitude * qpos.v[Q_Y]);
  119.  
  120. // Skip placemarks that are outside the screen area
  121. if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) {
  122. globeHidesPoint = false;
  123. return false;
  124. }
  125.  
  126. globeHidesPoint = false;
  127. return true;
  128. }
  129.  
  130. bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates,
  131. const ViewportParams *viewport,
  132. qreal *x, qreal &y,
  133. int &pointRepeatNum,
  134. const QSizeF& size,
  135. bool &globeHidesPoint ) const
  136. {
  137. qreal absoluteAltitude = coordinates.altitude() + EARTH_RADIUS;
  138. Quaternion qpos = coordinates.quaternion();
  139.  
  140. qpos.rotateAroundAxis( *( viewport->planetAxisMatrix() ) );
  141.  
  142. qreal pixelAltitude = ( ( viewport->radius() )
  143. / EARTH_RADIUS * absoluteAltitude );
  144. if ( coordinates.altitude() < 10000.0 ) {
  145. // Skip placemarks at the other side of the earth.
  146. if ( qpos.v[Q_Z] < 0.0 ) {
  147. globeHidesPoint = true;
  148. return false;
  149. }
  150. }
  151. else {
  152. qreal earthCenteredX = pixelAltitude * qpos.v[Q_X];
  153. qreal earthCenteredY = pixelAltitude * qpos.v[Q_Y];
  154. qreal radius = viewport->radius();
  155.  
  156. // Don't draw high placemarks (e.g. satellites) that aren't visible,
  157. // because they are "behind" the earth
  158. if ( qpos.v[Q_Z] < 0.0
  159. && ( ( earthCenteredX * earthCenteredX
  160. + earthCenteredY * earthCenteredY )
  161. < radius * radius ) ) {
  162. globeHidesPoint = true;
  163. return false;
  164. }
  165. }
  166.  
  167. // Let (x, y) be the position on the screen of the placemark..
  168. *x = ((qreal)(viewport->width()) / 2.0 + pixelAltitude * qpos.v[Q_X]);
  169. y = ((qreal)(viewport->height()) / 2.0 - pixelAltitude * qpos.v[Q_Y]);
  170.  
  171. // Skip placemarks that are outside the screen area
  172. if ( *x + size.width() / 2.0 < 0.0 || *x >= viewport->width() + size.width() / 2.0
  173. || y + size.height() / 2.0 < 0.0 || y >= viewport->height() + size.height() / 2.0 )
  174. {
  175. globeHidesPoint = false;
  176. return false;
  177. }
  178.  
  179. // This projection doesn't have any repetitions,
  180. // so the number of screen points referring to the geopoint is one.
  181. pointRepeatNum = 1;
  182. globeHidesPoint = false;
  183. return true;
  184. }
  185.  
  186.  
  187. bool SphericalProjection::geoCoordinates( const int x, const int y,
  188. const ViewportParams *viewport,
  189. qreal& lon, qreal& lat,
  190. GeoDataCoordinates::Unit unit ) const
  191. {
  192. const qreal inverseRadius = 1.0 / (qreal)(viewport->radius());
  193.  
  194. const qreal qx = +(qreal)( x - viewport->width() / 2 ) * inverseRadius;
  195. const qreal qy = -(qreal)( y - viewport->height() / 2 ) * inverseRadius;
  196.  
  197. if ( 1 <= qx * qx + qy * qy ) {
  198. return false;
  199. }
  200.  
  201. const qreal qz = sqrt( 1 - qx * qx - qy * qy );
  202.  
  203. Quaternion qpos( 0.0, qx, qy, qz );
  204. qpos.rotateAroundAxis( viewport->planetAxis() );
  205. qpos.getSpherical( lon, lat );
  206.  
  207. if ( unit == GeoDataCoordinates::Degree ) {
  208. lon *= RAD2DEG;
  209. lat *= RAD2DEG;
  210. }
  211.  
  212. return true;
  213. }
  214.  
  215. GeoDataLatLonAltBox SphericalProjection::latLonAltBox( const QRect& screenRect,
  216. const ViewportParams *viewport ) const
  217. {
  218. // For the case where the whole viewport gets covered there is a
  219. // pretty dirty and generic detection algorithm:
  220. GeoDataLatLonAltBox latLonAltBox = AbstractProjection::latLonAltBox( screenRect, viewport );
  221.  
  222. // If the whole globe is visible we can easily calculate
  223. // analytically the lon-/lat- range.
  224. qreal pitch = GeoDataPoint::normalizeLat( viewport->planetAxis().pitch() );
  225.  
  226. if ( 2.0 * viewport->radius() <= viewport->height()
  227. && 2.0 * viewport->radius() <= viewport->width() )
  228. {
  229. // Unless the planetaxis is in the screen plane the allowed longitude range
  230. // covers full -180 deg to +180 deg:
  231. if ( pitch > 0.0 && pitch < +M_PI ) {
  232. latLonAltBox.setWest( -M_PI );
  233. latLonAltBox.setEast( +M_PI );
  234. latLonAltBox.setNorth( +fabs( M_PI / 2.0 - fabs( pitch ) ) );
  235. latLonAltBox.setSouth( -M_PI / 2.0 );
  236. }
  237. if ( pitch < 0.0 && pitch > -M_PI ) {
  238. latLonAltBox.setWest( -M_PI );
  239. latLonAltBox.setEast( +M_PI );
  240. latLonAltBox.setNorth( +M_PI / 2.0 );
  241. latLonAltBox.setSouth( -fabs( M_PI / 2.0 - fabs( pitch ) ) );
  242. }
  243.  
  244. // Last but not least we deal with the rare case where the
  245. // globe is fully visible and pitch = 0.0 or pitch = -M_PI or
  246. // pitch = +M_PI
  247. if ( pitch == 0.0 || pitch == -M_PI || pitch == +M_PI ) {
  248. qreal yaw = viewport->planetAxis().yaw();
  249. latLonAltBox.setWest( GeoDataPoint::normalizeLon( yaw - M_PI / 2.0 ) );
  250. latLonAltBox.setEast( GeoDataPoint::normalizeLon( yaw + M_PI / 2.0 ) );
  251. latLonAltBox.setNorth( +M_PI / 2.0 );
  252. latLonAltBox.setSouth( -M_PI / 2.0 );
  253. }
  254. }
  255.  
  256. // Now we check whether maxLat (e.g. the north pole) gets displayed
  257. // inside the viewport to get more accurate values for east and west.
  258.  
  259. // We need a point on the screen at maxLat that definitely gets displayed:
  260. qreal averageLongitude = ( latLonAltBox.west() + latLonAltBox.east() ) / 2.0;
  261.  
  262. GeoDataCoordinates maxLatPoint( averageLongitude, maxLat(), 0.0, GeoDataCoordinates::Radian );
  263. GeoDataCoordinates minLatPoint( averageLongitude, minLat(), 0.0, GeoDataCoordinates::Radian );
  264.  
  265. qreal dummyX, dummyY; // not needed
  266. bool dummyVal;
  267.  
  268. if ( screenCoordinates( maxLatPoint, viewport, dummyX, dummyY, dummyVal ) ||
  269. screenCoordinates( minLatPoint, viewport, dummyX, dummyY, dummyVal ) ) {
  270. latLonAltBox.setWest( -M_PI );
  271. latLonAltBox.setEast( +M_PI );
  272. }
  273.  
  274. // mDebug() << latLonAltBox.text( GeoDataCoordinates::Degree );
  275.  
  276. return latLonAltBox;
  277. }
  278.  
  279.  
  280. bool SphericalProjection::mapCoversViewport( const ViewportParams *viewport ) const
  281. {
  282. qint64 radius = viewport->radius();
  283. qint64 width = viewport->width();
  284. qint64 height = viewport->height();
  285.  
  286. // This first test is a quick one that will catch all really big
  287. // radii and prevent overflow in the real test.
  288. if ( radius > width + height )
  289. return true;
  290.  
  291. // This is the real test. The 4 is because we are really
  292. // comparing to width/2 and height/2.
  293. if ( 4 * radius * radius >= width * width + height * height )
  294. return true;
  295.  
  296. return false;
  297. }
  298.  
  299. QPainterPath SphericalProjection::mapShape( const ViewportParams *viewport ) const
  300. {
  301. int radius = viewport->radius();
  302. int imgWidth = viewport->width();
  303. int imgHeight = viewport->height();
  304.  
  305. QPainterPath fullRect;
  306. fullRect.addRect( 0 , 0 , imgWidth, imgHeight );
  307.  
  308. // If the globe covers the whole image, then the projected region represents
  309. // all of the image.
  310. // Otherwise the active region has got the shape of the visible globe.
  311.  
  312. if ( !viewport->mapCoversViewport() ) {
  313. QPainterPath mapShape;
  314. mapShape.addEllipse(
  315. imgWidth / 2 - radius,
  316. imgHeight / 2 - radius,
  317. 2 * radius,
  318. 2 * radius );
  319. return mapShape.intersected( fullRect );
  320. }
  321.  
  322. return fullRect;
  323. }
  324.  
  325. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement