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"