File indexing completed on 2025-01-05 03:59:35
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2012 Dennis Nienhüser <nienhueser@kde.org> 0004 // 0005 0006 #include "DownloadRegion.h" 0007 0008 #include "MarbleModel.h" 0009 #include "MarbleMap.h" 0010 #include "MarbleMath.h" 0011 #include "TextureLayer.h" 0012 #include "GeoDataLatLonAltBox.h" 0013 #include "GeoDataLineString.h" 0014 #include "GeoSceneDocument.h" 0015 #include "GeoSceneMap.h" 0016 #include "GeoSceneLayer.h" 0017 #include "GeoSceneTileDataset.h" 0018 #include "GeoSceneAbstractTileProjection.h" 0019 #include "TileCoordsPyramid.h" 0020 0021 #include "digikam_debug.h" 0022 0023 namespace Marble { 0024 0025 class DownloadRegionPrivate 0026 { 0027 public: 0028 MarbleModel* m_marbleModel; 0029 0030 QPair<int,int> m_tileLevelRange; 0031 0032 int m_visibleTileLevel; 0033 0034 DownloadRegionPrivate(); 0035 0036 int rad2PixelX( qreal const lon, const TileLayer *tileLayer ) const; 0037 0038 int rad2PixelY( qreal const lat, const TileLayer *tileLayer ) const; 0039 }; 0040 0041 DownloadRegionPrivate::DownloadRegionPrivate() : m_marbleModel( nullptr ), 0042 m_tileLevelRange( 0, 0 ), m_visibleTileLevel( 0 ) 0043 { 0044 // nothing to do 0045 } 0046 0047 // copied from AbstractScanlineTextureMapper and slightly adjusted 0048 int DownloadRegionPrivate::rad2PixelX( qreal const lon, const TileLayer *tileLayer ) const 0049 { 0050 qreal tileWidth = tileLayer && tileLayer->layerCount() > 0 ? tileLayer->tileSize().width() : 256; 0051 qreal const globalWidth = tileWidth * tileLayer->tileColumnCount( m_visibleTileLevel ); 0052 return static_cast<int>(globalWidth * 0.5 * (1 + lon / M_PI)); 0053 } 0054 0055 // copied from AbstractScanlineTextureMapper and slightly adjusted 0056 int DownloadRegionPrivate::rad2PixelY( qreal const lat, const TileLayer *tileLayer ) const 0057 { 0058 qreal tileHeight = tileLayer && tileLayer->layerCount() > 0 ? tileLayer->tileSize().height() : 256; 0059 qreal const globalHeight = tileHeight * tileLayer->tileRowCount( m_visibleTileLevel ); 0060 0061 switch (tileLayer->tileProjection()->type()) { 0062 case GeoSceneAbstractTileProjection::Equirectangular: 0063 return static_cast<int>(globalHeight * (0.5 - lat / M_PI)); 0064 case GeoSceneAbstractTileProjection::Mercator: 0065 if ( fabs( lat ) < 1.4835 ) 0066 return static_cast<int>(globalHeight * 0.5 * (1 - gdInv(lat) / M_PI)); 0067 if ( lat >= +1.4835 ) 0068 return static_cast<int>(globalHeight * 0.5 * (1 - 3.1309587 / M_PI)); 0069 if ( lat <= -1.4835 ) 0070 return static_cast<int>(globalHeight * 0.5 * (1 + 3.1309587 / M_PI)); 0071 } 0072 0073 // Dummy value to avoid a warning. 0074 return 0; 0075 } 0076 0077 DownloadRegion::DownloadRegion( QObject* parent ) : QObject( parent ), 0078 d( new DownloadRegionPrivate ) 0079 { 0080 // nothing to do 0081 } 0082 0083 void DownloadRegion::setMarbleModel( MarbleModel* model ) 0084 { 0085 d->m_marbleModel = model; 0086 } 0087 0088 DownloadRegion::~DownloadRegion() 0089 { 0090 delete d; 0091 } 0092 0093 void DownloadRegion::setTileLevelRange( const int minimumTileLevel, const int maximumTileLevel ) 0094 { 0095 Q_ASSERT( minimumTileLevel >= 0 ); 0096 Q_ASSERT( maximumTileLevel >= 0 ); 0097 Q_ASSERT( minimumTileLevel <= maximumTileLevel ); 0098 d->m_tileLevelRange.first = minimumTileLevel; 0099 d->m_tileLevelRange.second = maximumTileLevel; 0100 } 0101 0102 QVector<TileCoordsPyramid> DownloadRegion::region( const TileLayer *tileLayer, const GeoDataLatLonAltBox &downloadRegion ) const 0103 { 0104 Q_ASSERT( tileLayer ); 0105 0106 int tileLevelRangeFirst = d->m_tileLevelRange.first; 0107 int tileLevelRangeSecond = d->m_tileLevelRange.second; 0108 0109 TileType tileType = dynamic_cast<const TextureLayer*>( tileLayer ) ? TextureTileType : VectorTileType; 0110 0111 QVector<int> validLevels; 0112 validLevels = validTileLevels(tileType); 0113 0114 // Align the tileLevelRangeSecond with the validTileLevels 0115 if (!validLevels.isEmpty()) { 0116 int lastIndex = validLevels.count() - 1; 0117 for ( int i = 0; i < validLevels.count(); ++i ) { 0118 if (validLevels.at(lastIndex - i) <= tileLevelRangeSecond 0119 && validLevels.at(lastIndex - i) >= tileLevelRangeFirst) { 0120 tileLevelRangeSecond = validLevels.at(lastIndex - i); 0121 break; 0122 } 0123 } 0124 } 0125 0126 int const westX = d->rad2PixelX( downloadRegion.west(), tileLayer ); 0127 int const northY = d->rad2PixelY( downloadRegion.north(), tileLayer ); 0128 int const eastX = d->rad2PixelX( downloadRegion.east(), tileLayer ); 0129 int const southY = d->rad2PixelY( downloadRegion.south(), tileLayer ); 0130 0131 // FIXME: remove this stuff 0132 qCDebug(DIGIKAM_MARBLE_LOG) << "DownloadRegionDialog downloadRegion:" 0133 << "north:" << downloadRegion.north() 0134 << "south:" << downloadRegion.south() 0135 << "east:" << downloadRegion.east() 0136 << "west:" << downloadRegion.west(); 0137 qCDebug(DIGIKAM_MARBLE_LOG) << "north/west (x/y):" << westX << northY; 0138 qCDebug(DIGIKAM_MARBLE_LOG) << "south/east (x/y):" << eastX << southY; 0139 0140 int const tileWidth = tileLayer->tileSize().width(); 0141 int const tileHeight = tileLayer->tileSize().height(); 0142 qCDebug(DIGIKAM_MARBLE_LOG) << "DownloadRegionDialog downloadRegion: tileSize:" << tileWidth << tileHeight; 0143 0144 int const visibleLevelX1 = qMin( westX, eastX ); 0145 int const visibleLevelY1 = qMin( northY, southY ); 0146 int const visibleLevelX2 = qMax( westX, eastX ); 0147 int const visibleLevelY2 = qMax( northY, southY ); 0148 0149 qCDebug(DIGIKAM_MARBLE_LOG) << "visible level pixel coords (level/x1/y1/x2/y2):" << d->m_visibleTileLevel 0150 << visibleLevelX1 << visibleLevelY1 << visibleLevelX2 << visibleLevelY2; 0151 0152 int bottomLevelX1, bottomLevelY1, bottomLevelX2, bottomLevelY2; 0153 // the pixel coords calculated above are referring to the visible tile level, 0154 // if the bottom level is a different level, we have to take it into account 0155 if ( d->m_visibleTileLevel > tileLevelRangeSecond ) { 0156 int const deltaLevel = d->m_visibleTileLevel - tileLevelRangeSecond; 0157 bottomLevelX1 = visibleLevelX1 >> deltaLevel; 0158 bottomLevelY1 = visibleLevelY1 >> deltaLevel; 0159 bottomLevelX2 = visibleLevelX2 >> deltaLevel; 0160 bottomLevelY2 = visibleLevelY2 >> deltaLevel; 0161 } 0162 else if ( d->m_visibleTileLevel < tileLevelRangeSecond ) { 0163 int const deltaLevel = tileLevelRangeSecond - d->m_visibleTileLevel; 0164 bottomLevelX1 = visibleLevelX1 << deltaLevel; 0165 bottomLevelY1 = visibleLevelY1 << deltaLevel; 0166 bottomLevelX2 = visibleLevelX2 << deltaLevel; 0167 bottomLevelY2 = visibleLevelY2 << deltaLevel; 0168 } 0169 else { 0170 bottomLevelX1 = visibleLevelX1; 0171 bottomLevelY1 = visibleLevelY1; 0172 bottomLevelX2 = visibleLevelX2; 0173 bottomLevelY2 = visibleLevelY2; 0174 } 0175 qCDebug(DIGIKAM_MARBLE_LOG) << "bottom level pixel coords (level/x1/y1/x2/y2):" 0176 << tileLevelRangeSecond 0177 << bottomLevelX1 << bottomLevelY1 << bottomLevelX2 << bottomLevelY2; 0178 0179 TileCoordsPyramid coordsPyramid( tileLevelRangeFirst, tileLevelRangeSecond ); 0180 coordsPyramid.setValidTileLevels(validLevels); 0181 0182 QRect bottomLevelTileCoords; 0183 bottomLevelTileCoords.setCoords 0184 ( bottomLevelX1 / tileWidth, 0185 bottomLevelY1 / tileHeight, 0186 bottomLevelX2 / tileWidth + ( bottomLevelX2 % tileWidth > 0 ? 1 : 0 ) - 1, // -1 needed for proper counting 0187 bottomLevelY2 / tileHeight + ( bottomLevelY2 % tileHeight > 0 ? 1 : 0 ) - 1); // -1 needed for proper counting 0188 qCDebug(DIGIKAM_MARBLE_LOG) << "bottom level tile coords: (x1/y1/size):" << bottomLevelTileCoords; 0189 coordsPyramid.setBottomLevelCoords( bottomLevelTileCoords ); 0190 0191 qCDebug(DIGIKAM_MARBLE_LOG) << "tiles count:" << coordsPyramid.tilesCount( ); 0192 QVector<TileCoordsPyramid> pyramid; 0193 pyramid << coordsPyramid; 0194 return pyramid; 0195 } 0196 0197 void DownloadRegion::setVisibleTileLevel(const int tileLevel) 0198 { 0199 d->m_visibleTileLevel = tileLevel; 0200 } 0201 0202 QVector<TileCoordsPyramid> DownloadRegion::fromPath( const TileLayer *tileLayer, qreal offset, const GeoDataLineString &waypoints ) const 0203 { 0204 if ( !d->m_marbleModel ) { 0205 return QVector<TileCoordsPyramid>(); 0206 } 0207 0208 int tileLevelRangeFirst = d->m_tileLevelRange.first; 0209 int tileLevelRangeSecond = d->m_tileLevelRange.second; 0210 0211 TileType tileType = dynamic_cast<const TextureLayer*>( tileLayer ) ? TextureTileType : VectorTileType; 0212 0213 QVector<int> validLevels; 0214 validLevels = validTileLevels(tileType); 0215 0216 // Align the tileLevelRangeSecond with the validTileLevels 0217 if (!validLevels.isEmpty()) { 0218 int lastIndex = validLevels.count() - 1; 0219 for ( int i = 0; i < validLevels.count(); ++i ) { 0220 if (validLevels.at(lastIndex - i) <= tileLevelRangeSecond 0221 && validLevels.at(lastIndex - i) >= tileLevelRangeFirst) { 0222 tileLevelRangeSecond = validLevels.at(lastIndex - i); 0223 break; 0224 } 0225 } 0226 } 0227 0228 TileCoordsPyramid coordsPyramid( tileLevelRangeFirst, tileLevelRangeSecond ); 0229 coordsPyramid.setValidTileLevels(validLevels); 0230 0231 int const tileWidth = tileLayer->tileSize().width(); 0232 int const tileHeight = tileLayer->tileSize().height(); 0233 0234 qreal radius = d->m_marbleModel->planetRadius(); 0235 QVector<TileCoordsPyramid> pyramid; 0236 qreal radianOffset = offset / radius; 0237 0238 for( int i = 1; i < waypoints.size(); ++i ) { 0239 GeoDataCoordinates position = waypoints[i]; 0240 qreal lonCenter = position.longitude(); 0241 qreal latCenter = position.latitude(); 0242 0243 // coordinates of the of the vertices of the square(topleft and bottomright) at an offset distance from the waypoint 0244 qreal latNorth = asin( sin( latCenter ) * cos( radianOffset ) + cos( latCenter ) * sin( radianOffset ) * cos( 7*M_PI/4 ) ); 0245 qreal dlonWest = atan2( sin( 7*M_PI/4 ) * sin( radianOffset ) * cos( latCenter ), cos( radianOffset ) - sin( latCenter ) * sin( latNorth ) ); 0246 qreal lonWest = fmod( lonCenter - dlonWest + M_PI, 2*M_PI ) - M_PI; 0247 qreal latSouth = asin( sin( latCenter ) * cos( radianOffset ) + cos( latCenter ) * sin( radianOffset ) * cos( 3*M_PI/4 ) ); 0248 qreal dlonEast = atan2( sin( 3*M_PI/4 ) * sin( radianOffset ) * cos( latCenter ), cos( radianOffset ) - sin( latCenter ) * sin( latSouth ) ); 0249 qreal lonEast = fmod( lonCenter - dlonEast+M_PI, 2*M_PI ) - M_PI; 0250 0251 int const northY = d->rad2PixelY( latNorth, tileLayer ); 0252 int const southY = d->rad2PixelY( latSouth, tileLayer ); 0253 int const eastX = d->rad2PixelX( lonEast, tileLayer ); 0254 int const westX = d->rad2PixelX( lonWest, tileLayer ); 0255 0256 int const west = qMin( westX, eastX ); 0257 int const north = qMin( northY, southY ); 0258 int const east = qMax( westX, eastX ); 0259 int const south = qMax( northY, southY ); 0260 0261 int bottomLevelTileX1 = 0; 0262 int bottomLevelTileY1 = 0; 0263 int bottomLevelTileX2 = 0; 0264 int bottomLevelTileY2 = 0; 0265 0266 if ( d->m_visibleTileLevel > tileLevelRangeSecond ) { 0267 int const deltaLevel = d->m_visibleTileLevel - tileLevelRangeSecond; 0268 bottomLevelTileX1 = west >> deltaLevel; 0269 bottomLevelTileY1 = north >> deltaLevel; 0270 bottomLevelTileX2 = east >> deltaLevel; 0271 bottomLevelTileY2 = south >> deltaLevel; 0272 } 0273 else if ( d->m_visibleTileLevel < tileLevelRangeSecond ) { 0274 int const deltaLevel = tileLevelRangeSecond - d->m_visibleTileLevel; 0275 bottomLevelTileX1 = west << deltaLevel; 0276 bottomLevelTileY1 = north << deltaLevel; 0277 bottomLevelTileX2 = east << deltaLevel; 0278 bottomLevelTileY2 = south << deltaLevel; 0279 } 0280 else { 0281 bottomLevelTileX1 = west; 0282 bottomLevelTileY1 = north; 0283 bottomLevelTileX2 = east; 0284 bottomLevelTileY2 = south; 0285 } 0286 0287 QRect waypointRegion; 0288 //square region around the waypoint 0289 waypointRegion.setCoords( bottomLevelTileX1/tileWidth, bottomLevelTileY1/tileHeight, 0290 bottomLevelTileX2/tileWidth, bottomLevelTileY2/tileHeight ); 0291 coordsPyramid.setBottomLevelCoords( waypointRegion ); 0292 pyramid << coordsPyramid; 0293 } 0294 0295 return pyramid; 0296 } 0297 0298 QVector<int> DownloadRegion::validTileLevels( const TileType tileType ) const 0299 { 0300 QVector<int> validTileLevels; 0301 0302 GeoSceneMap * map = d->m_marbleModel->mapTheme()->map(); 0303 QVector<GeoSceneLayer*> layers = map->layers(); 0304 for (auto layer : layers) { 0305 if ((layer->backend() == QString::fromUtf8("vectortile") && tileType == VectorTileType ) 0306 || (layer->backend() == QString::fromUtf8("texture") && tileType == TextureTileType ) ) { 0307 GeoSceneTileDataset * dataset = dynamic_cast<GeoSceneTileDataset*>(layer->datasets().first()); 0308 validTileLevels = dataset->tileLevels(); 0309 break; 0310 } 0311 } 0312 0313 return validTileLevels; 0314 } 0315 0316 } 0317 0318 #include "moc_DownloadRegion.cpp"