File indexing completed on 2024-04-14 14:16:39

0001 #include "NwwMapImage.h"
0002 
0003 #include "InterpolationMethod.h"
0004 
0005 #include <QDebug>
0006 #include <cmath>
0007 
0008 NwwMapImage::NwwMapImage( QDir const & baseDirectory, int const tileLevel )
0009     : m_tileEdgeLengthPixel( 512 ),
0010       m_emptyPixel( qRgba( 0, 0, 0, 255 )),
0011       m_baseDirectory( baseDirectory ),
0012       m_tileLevel( tileLevel ),
0013       m_mapWidthTiles( 10 * pow( 2, m_tileLevel )),
0014       m_mapHeightTiles( 5 * pow( 2, m_tileLevel )),
0015       m_mapWidthPixel( m_mapWidthTiles * m_tileEdgeLengthPixel ),
0016       m_mapHeightPixel( m_mapHeightTiles * m_tileEdgeLengthPixel ),
0017       m_interpolationMethod(),
0018       m_tileCache( DefaultCacheSizeBytes )
0019 {
0020     if ( !m_baseDirectory.exists() )
0021         qFatal( "Base directory '%s' does not exist.", m_baseDirectory.path().toStdString().c_str() );
0022 
0023     qDebug() << "tileLevel:" << m_tileLevel
0024              << "\nmapWidthTiles:" << m_mapWidthTiles
0025              << "\nmapHeightTiles:" << m_mapHeightTiles
0026              << "\nmapWidthPixel:" << m_mapWidthPixel
0027              << "\nmapHeightPixel:" << m_mapHeightPixel;
0028 }
0029 
0030 QRgb NwwMapImage::pixel( double const lonRad, double const latRad )
0031 {
0032     double const x = lonRadToPixelX( lonRad );
0033     double const y = latRadToPixelY( latRad );
0034     return m_interpolationMethod->interpolate( x, y );
0035 }
0036 
0037 QRgb NwwMapImage::pixel( int const x, int const y )
0038 {
0039     int const tileX = x / m_tileEdgeLengthPixel;
0040     int const tileY = y / m_tileEdgeLengthPixel;
0041 
0042     // fast check if tile is missing
0043     int const tileKey = tileId( tileX, tileY );
0044     if ( m_tileMissing.contains( tileKey ))
0045         return m_emptyPixel;
0046 
0047     QPair<QImage, bool> potentialTile = tile( tileX, tileY );
0048     if ( !potentialTile.second )
0049         return m_emptyPixel;
0050     else
0051         return potentialTile.first.pixel( x % m_tileEdgeLengthPixel,
0052                                           m_tileEdgeLengthPixel - y % m_tileEdgeLengthPixel - 1 );
0053 }
0054 
0055 void NwwMapImage::setBaseDirectory( QDir const & baseDirectory )
0056 {
0057     m_baseDirectory = baseDirectory;
0058 }
0059 
0060 void NwwMapImage::setCacheSizeBytes(const int cacheSizeBytes)
0061 {
0062     m_tileCache.setMaxCost( cacheSizeBytes );
0063 }
0064 
0065 void NwwMapImage::setInterpolationMethod( InterpolationMethod * const method )
0066 {
0067     m_interpolationMethod = method;
0068     m_interpolationMethod->setMapImage( this );
0069 }
0070 
0071 void NwwMapImage::setTileLevel( int const tileLevel )
0072 {
0073     m_tileLevel = tileLevel;
0074     m_mapWidthTiles = 10 * pow( 2, m_tileLevel );
0075     m_mapHeightTiles = 5 * pow( 2, m_tileLevel );
0076     m_mapWidthPixel = m_mapWidthTiles * m_tileEdgeLengthPixel;
0077     m_mapHeightPixel = m_mapHeightTiles * m_tileEdgeLengthPixel;
0078 }
0079 
0080 inline int NwwMapImage::tileId( int const tileX, int const tileY )
0081 {
0082     return (tileX << 16) + tileY;
0083 }
0084 
0085 QPair<QImage, bool> NwwMapImage::tile( int const tileX, int const tileY )
0086 {
0087     int const tileKey = tileId( tileX, tileY );
0088 
0089     // first check cache
0090     QImage * const cachedTile = m_tileCache.object( tileKey );
0091     if ( cachedTile )
0092         return QPair<QImage, bool>( *cachedTile, true );
0093 
0094     QString const filename = QString("%1/%2/%2_%3.jpg")
0095             .arg( m_baseDirectory.path() )
0096             .arg( tileY, 4, 10, QLatin1Char('0'))
0097             .arg( tileX, 4, 10, QLatin1Char('0'));
0098     QImage tile;
0099     bool const loaded = tile.load( filename );
0100     if ( !loaded ) {
0101         m_tileMissing.insert( tileKey );
0102         //qDebug() << "Tile" << filename << "not found";
0103     } else {
0104         m_tileCache.insert( tileKey, new QImage( tile ), tile.byteCount() );
0105         //qDebug() << "Tile" << filename << "loaded and inserted in cache";
0106     }
0107     return QPair<QImage, bool>( tile, loaded );
0108 }
0109 
0110 inline double NwwMapImage::lonRadToPixelX( double const lonRad ) const
0111 {
0112     return static_cast<double>( m_mapWidthPixel ) / ( 2.0 * M_PI ) * lonRad
0113             + 0.5 * static_cast<double>( m_mapWidthPixel );
0114 }
0115 
0116 inline double NwwMapImage::latRadToPixelY( double const latRad ) const
0117 {
0118     return static_cast<double>( m_mapHeightPixel ) / M_PI * latRad
0119             + 0.5 * static_cast<double>( m_mapHeightPixel );
0120 }
0121 
0122 QRgb NwwMapImage::nearestNeighbor( double const x, double const y )
0123 {
0124     int const xr = round( x );
0125     int const yr = round( y );
0126     return pixel( xr, yr );
0127 }
0128 
0129 QRgb NwwMapImage::bilinearInterpolation( double const x, double const y )
0130 {
0131     int const x1 = x;
0132     int const x2 = x1 + 1;
0133     int const y1 = y;
0134     int const y2 = y1 + 1;
0135 
0136     QRgb const lowerLeftPixel = pixel( x1, y1 );
0137     QRgb const lowerRightPixel = pixel( x2, y1 );
0138     QRgb const upperLeftPixel = pixel( x1, y2 );
0139     QRgb const upperRightPixel = pixel( x2, y2 );
0140 
0141     // interpolate horizontically
0142     //
0143     // x2 - x    x2 - x
0144     // ------- = ------ = x1 + 1 - x = 1 - fractionX
0145     // x2 - x1      1
0146     //
0147     // x - x1    x - x1
0148     // ------- = ------ = fractionX
0149     // x2 - x1     1
0150 
0151     double const fractionX = x - x1;
0152     double const lowerMidRed   = ( 1.0 - fractionX ) * qRed( lowerLeftPixel )   + fractionX * qRed( lowerRightPixel );
0153     double const lowerMidGreen = ( 1.0 - fractionX ) * qGreen( lowerLeftPixel ) + fractionX * qGreen( lowerRightPixel );
0154     double const lowerMidBlue  = ( 1.0 - fractionX ) * qBlue( lowerLeftPixel )  + fractionX * qBlue( lowerRightPixel );
0155     double const lowerMidAlpha = ( 1.0 - fractionX ) * qAlpha( lowerLeftPixel ) + fractionX * qAlpha( lowerRightPixel );
0156 
0157     double const upperMidRed   = ( 1.0 - fractionX ) * qRed( upperLeftPixel )   + fractionX * qRed( upperRightPixel );
0158     double const upperMidGreen = ( 1.0 - fractionX ) * qGreen( upperLeftPixel ) + fractionX * qGreen( upperRightPixel );
0159     double const upperMidBlue  = ( 1.0 - fractionX ) * qBlue( upperLeftPixel )  + fractionX * qBlue( upperRightPixel );
0160     double const upperMidAlpha = ( 1.0 - fractionX ) * qAlpha( upperLeftPixel ) + fractionX * qAlpha( upperRightPixel );
0161 
0162     // interpolate vertically
0163     //
0164     // y2 - y    y2 - y
0165     // ------- = ------ = y1 + 1 - y = 1 - fractionY
0166     // y2 - y1      1
0167     //
0168     // y - y1    y - y1
0169     // ------- = ------ = fractionY
0170     // y2 - y1     1
0171 
0172     double const fractionY = y - y1;
0173     double const red   = ( 1.0 - fractionY ) * lowerMidRed   + fractionY * upperMidRed;
0174     double const green = ( 1.0 - fractionY ) * lowerMidGreen + fractionY * upperMidGreen;
0175     double const blue  = ( 1.0 - fractionY ) * lowerMidBlue  + fractionY * upperMidBlue;
0176     double const alpha = ( 1.0 - fractionY ) * lowerMidAlpha + fractionY * upperMidAlpha;
0177 
0178     return qRgba( round( red ), round( green ), round( blue ), round( alpha ));
0179 }