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