File indexing completed on 2025-01-05 03:59:33
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2007-2012 Torsten Rahn <rahn@kde.org> 0004 // SPDX-FileCopyrightText: 2007-2008 Inge Wallin <ingwa@kde.org> 0005 // 0006 0007 // Local 0008 #include "EquirectProjection.h" 0009 0010 #include <QIcon> 0011 0012 #include <klocalizedstring.h> 0013 0014 // Marble 0015 #include "ViewportParams.h" 0016 #include "GeoDataLatLonAltBox.h" 0017 0018 #include "digikam_debug.h" 0019 0020 using namespace Marble; 0021 0022 EquirectProjection::EquirectProjection() 0023 : CylindricalProjection() 0024 { 0025 setMinLat( minValidLat() ); 0026 setMaxLat( maxValidLat() ); 0027 } 0028 0029 EquirectProjection::~EquirectProjection() 0030 { 0031 } 0032 0033 QString EquirectProjection::name() const 0034 { 0035 return i18n( "Flat Map" ); 0036 } 0037 0038 QString EquirectProjection::description() const 0039 { 0040 return i18n( "<p><b>Equirectangular Projection</b> (\"Plate carrée\")</p><p>Applications: De facto standard for global texture data sets for computer software.</p>" ); 0041 } 0042 0043 QIcon EquirectProjection::icon() const 0044 { 0045 return QIcon::fromTheme(QStringLiteral("map-flat")); 0046 } 0047 0048 bool EquirectProjection::screenCoordinates( const GeoDataCoordinates &geopoint, 0049 const ViewportParams *viewport, 0050 qreal &x, qreal &y, bool &globeHidesPoint ) const 0051 { 0052 globeHidesPoint = false; 0053 0054 // Convenience variables 0055 int radius = viewport->radius(); 0056 int width = viewport->width(); 0057 int height = viewport->height(); 0058 0059 qreal lon; 0060 qreal lat; 0061 qreal rad2Pixel = 2.0 * viewport->radius() / M_PI; 0062 0063 const qreal centerLon = viewport->centerLongitude(); 0064 const qreal centerLat = viewport->centerLatitude(); 0065 0066 geopoint.geoCoordinates( lon, lat ); 0067 0068 // Let (x, y) be the position on the screen of the geopoint. 0069 x = ((qreal)(viewport->width()) / 2.0 + rad2Pixel * (lon - centerLon)); 0070 y = ((qreal)(viewport->height()) / 2.0 - rad2Pixel * (lat - centerLat)); 0071 0072 // Return true if the calculated point is inside the screen area, 0073 // otherwise return false. 0074 return ( ( 0 <= y && y < height ) 0075 && ( ( 0 <= x && x < width ) 0076 || ( 0 <= x - 4 * radius && x - 4 * radius < width ) 0077 || ( 0 <= x + 4 * radius && x + 4 * radius < width ) ) ); 0078 } 0079 0080 bool EquirectProjection::screenCoordinates( const GeoDataCoordinates &coordinates, 0081 const ViewportParams *viewport, 0082 qreal *x, qreal &y, 0083 int &pointRepeatNum, 0084 const QSizeF& size, 0085 bool &globeHidesPoint ) const 0086 { 0087 pointRepeatNum = 0; 0088 // On flat projections the observer's view onto the point won't be 0089 // obscured by the target planet itself. 0090 globeHidesPoint = false; 0091 0092 // Convenience variables 0093 int radius = viewport->radius(); 0094 qreal width = (qreal)(viewport->width()); 0095 qreal height = (qreal)(viewport->height()); 0096 0097 // Let (itX, y) be the first guess for one possible position on screen. 0098 qreal itX; 0099 screenCoordinates( coordinates, viewport, itX, y); 0100 0101 // Make sure that the requested point is within the visible y range: 0102 if ( 0 <= y + size.height() / 2.0 && y < height + size.height() / 2.0 ) { 0103 // For the repetition case the same geopoint gets displayed on 0104 // the map many times.across the longitude. 0105 0106 int xRepeatDistance = 4 * radius; 0107 0108 // Finding the leftmost positive x value 0109 if ( itX + size.width() > xRepeatDistance ) { 0110 const int repeatNum = (int)( ( itX + size.width() ) / xRepeatDistance ); 0111 itX = itX - repeatNum * xRepeatDistance; 0112 } 0113 if ( itX + size.width() / 2.0 < 0 ) { 0114 itX += xRepeatDistance; 0115 } 0116 // The requested point is out of the visible x range: 0117 if ( itX > width + size.width() / 2.0 ) { 0118 return false; 0119 } 0120 0121 // Now iterate through all visible x screen coordinates for the point 0122 // from left to right. 0123 int itNum = 0; 0124 while ( itX - size.width() / 2.0 < width ) { 0125 *x = itX; 0126 ++x; 0127 ++itNum; 0128 itX += xRepeatDistance; 0129 } 0130 0131 pointRepeatNum = itNum; 0132 0133 return true; 0134 } 0135 0136 // The requested point is out of the visible y range. 0137 return false; 0138 } 0139 0140 0141 bool EquirectProjection::geoCoordinates( const int x, const int y, 0142 const ViewportParams *viewport, 0143 qreal& lon, qreal& lat, 0144 GeoDataCoordinates::Unit unit ) const 0145 { 0146 const int radius = viewport->radius(); 0147 const qreal pixel2Rad = M_PI / (2.0 * radius); 0148 0149 // Get the Lat and Lon of the center point of the screen. 0150 const qreal centerLon = viewport->centerLongitude(); 0151 const qreal centerLat = viewport->centerLatitude(); 0152 0153 { 0154 const int halfImageWidth = viewport->width() / 2; 0155 const int xPixels = x - halfImageWidth; 0156 0157 lon = + xPixels * pixel2Rad + centerLon; 0158 0159 while ( lon > M_PI ) lon -= 2.0 * M_PI; 0160 while ( lon < -M_PI ) lon += 2.0 * M_PI; 0161 0162 if ( unit == GeoDataCoordinates::Degree ) { 0163 lon *= RAD2DEG; 0164 } 0165 } 0166 0167 { 0168 // Get yTop and yBottom, the limits of the map on the screen. 0169 const int halfImageHeight = viewport->height() / 2; 0170 const int yCenterOffset = (int)( centerLat * (qreal)(2 * radius) / M_PI); 0171 const int yTop = halfImageHeight - radius + yCenterOffset; 0172 const int yBottom = yTop + 2 * radius; 0173 0174 // Return here if the y coordinate is outside the map 0175 if ( yTop <= y && y < yBottom ) { 0176 const int yPixels = y - halfImageHeight; 0177 lat = - yPixels * pixel2Rad + centerLat; 0178 0179 if ( unit == GeoDataCoordinates::Degree ) { 0180 lat *= RAD2DEG; 0181 } 0182 0183 return true; 0184 } 0185 } 0186 0187 return false; 0188 } 0189 0190 GeoDataLatLonAltBox EquirectProjection::latLonAltBox( const QRect& screenRect, 0191 const ViewportParams *viewport ) const 0192 { 0193 qreal west; 0194 qreal north = 90*DEG2RAD; 0195 geoCoordinates( screenRect.left(), screenRect.top(), viewport, west, north, GeoDataCoordinates::Radian ); 0196 0197 qreal east; 0198 qreal south = -90*DEG2RAD; 0199 geoCoordinates( screenRect.right(), screenRect.bottom(), viewport, east, south, GeoDataCoordinates::Radian ); 0200 0201 // For the case where the whole viewport gets covered there is a 0202 // pretty dirty and generic detection algorithm: 0203 GeoDataLatLonAltBox latLonAltBox; 0204 latLonAltBox.setNorth( north, GeoDataCoordinates::Radian ); 0205 latLonAltBox.setSouth( south, GeoDataCoordinates::Radian ); 0206 latLonAltBox.setWest( west, GeoDataCoordinates::Radian ); 0207 latLonAltBox.setEast( east, GeoDataCoordinates::Radian ); 0208 latLonAltBox.setMinAltitude( -100000000.0 ); 0209 latLonAltBox.setMaxAltitude( 100000000000000.0 ); 0210 0211 // Convenience variables 0212 int radius = viewport->radius(); 0213 int width = viewport->width(); 0214 0215 // The remaining algorithm should be pretty generic for all kinds of 0216 // flat projections: 0217 0218 int xRepeatDistance = 4 * radius; 0219 if ( width >= xRepeatDistance ) { 0220 latLonAltBox.setWest( -M_PI ); 0221 latLonAltBox.setEast( +M_PI ); 0222 } 0223 0224 // Now we need to check whether maxLat (e.g. the north pole) gets displayed 0225 // inside the viewport. 0226 0227 // We need a point on the screen at maxLat that definitely gets displayed: 0228 qreal averageLongitude = latLonAltBox.east(); 0229 0230 GeoDataCoordinates maxLatPoint( averageLongitude, maxLat(), 0.0, GeoDataCoordinates::Radian ); 0231 GeoDataCoordinates minLatPoint( averageLongitude, minLat(), 0.0, GeoDataCoordinates::Radian ); 0232 0233 qreal dummyX, dummyY; // not needed 0234 0235 if ( screenCoordinates( maxLatPoint, viewport, dummyX, dummyY ) ) { 0236 latLonAltBox.setEast( +M_PI ); 0237 latLonAltBox.setWest( -M_PI ); 0238 } 0239 if ( screenCoordinates( minLatPoint, viewport, dummyX, dummyY ) ) { 0240 latLonAltBox.setEast( +M_PI ); 0241 latLonAltBox.setWest( -M_PI ); 0242 } 0243 0244 return latLonAltBox; 0245 } 0246 0247 0248 bool EquirectProjection::mapCoversViewport( const ViewportParams *viewport ) const 0249 { 0250 // Convenience variables 0251 int radius = viewport->radius(); 0252 //int width = viewport->width(); 0253 int height = viewport->height(); 0254 int halfImageHeight = viewport->height() / 2; 0255 0256 // Get the Lat and Lon of the center point of the screen. 0257 const qreal centerLat = viewport->centerLatitude(); 0258 0259 // Calculate how many pixel are being represented per radians. 0260 const float rad2Pixel = (qreal)( 2 * radius )/M_PI; 0261 0262 // Get yTop and yBottom, the limits of the map on the screen. 0263 int yCenterOffset = (int)( centerLat * rad2Pixel ); 0264 int yTop = halfImageHeight - radius + yCenterOffset; 0265 int yBottom = yTop + 2 * radius; 0266 0267 return !(yTop >= 0 || yBottom < height); 0268 }