File indexing completed on 2024-07-14 14:25:19

0001 #include "OsmTileClusterRenderer.h"
0002 
0003 #include "ReadOnlyMapImage.h"
0004 
0005 #include <QDebug>
0006 #include <QTime>
0007 
0008 #include <cmath>
0009 
0010 OsmTileClusterRenderer::OsmTileClusterRenderer( QObject * const parent )
0011     : QObject( parent ),
0012       m_osmTileEdgeLengthPixel( 256 ),
0013       m_emptyPixel( qRgba( 0, 0, 0, 255 )),
0014       m_osmBaseDirectory(),
0015       m_osmTileLevel(),
0016       m_osmMapEdgeLengthPixel(),
0017       m_clusterEdgeLengthTiles(),
0018       m_mapSourceDefinitions(),
0019       m_mapSources(),
0020       m_mapSourceCount()
0021 {
0022 }
0023 
0024 void OsmTileClusterRenderer::setClusterEdgeLengthTiles( int const clusterEdgeLengthTiles )
0025 {
0026     m_clusterEdgeLengthTiles = clusterEdgeLengthTiles;
0027 }
0028 
0029 void OsmTileClusterRenderer::setMapSources( QVector<ReadOnlyMapDefinition> const & mapSourceDefinitions )
0030 {
0031     m_mapSourceDefinitions = mapSourceDefinitions;
0032 }
0033 
0034 void OsmTileClusterRenderer::setOsmBaseDirectory( QDir const & osmBaseDirectory )
0035 {
0036     m_osmBaseDirectory = osmBaseDirectory;
0037 }
0038 
0039 void OsmTileClusterRenderer::setOsmTileLevel( int const level )
0040 {
0041     m_osmTileLevel = level;
0042     int const osmMapEdgeLengthTiles = pow( 2, m_osmTileLevel );
0043     m_osmMapEdgeLengthPixel = osmMapEdgeLengthTiles * m_osmTileEdgeLengthPixel;
0044     qDebug() << "osmTileLevel:" << m_osmTileLevel
0045              << "\nosmMapEdgeLengthTiles:" << osmMapEdgeLengthTiles
0046              << "\nosmMapEdgeLengthPixel:" << m_osmMapEdgeLengthPixel;
0047 }
0048 
0049 QDir OsmTileClusterRenderer::checkAndCreateDirectory( int const tileX ) const
0050 {
0051     QDir const tileDirectory( m_osmBaseDirectory.path() + QString("/%1/%2").arg( m_osmTileLevel ).arg( tileX ));
0052     if ( !tileDirectory.exists() ) {
0053         bool const created = tileDirectory.mkpath( tileDirectory.path() );
0054         if ( !created ) {
0055             // maybe it was created in the meantime by a different thread, then it should be there now
0056             if ( !tileDirectory.exists() )
0057                 qFatal("Unable to create directory '%s'.", tileDirectory.path().toStdString().c_str() );
0058         }
0059     }
0060     return tileDirectory;
0061 }
0062 
0063 void OsmTileClusterRenderer::initMapSources()
0064 {
0065     QVector<ReadOnlyMapDefinition>::const_iterator pos = m_mapSourceDefinitions.constBegin();
0066     QVector<ReadOnlyMapDefinition>::const_iterator const end = m_mapSourceDefinitions.constEnd();
0067     for (; pos != end; ++pos )
0068     {
0069         ReadOnlyMapImage * const mapImage = (*pos).createReadOnlyMap();
0070         if ( !mapImage )
0071             qFatal("Invalid map source definition.");
0072         m_mapSources.push_back( mapImage );
0073     }
0074     m_mapSourceCount = m_mapSources.count();
0075 }
0076 
0077 void OsmTileClusterRenderer::renderOsmTileCluster( int const clusterX, int const clusterY )
0078 {
0079     qDebug() << objectName() << "rendering clusterX:" << clusterX << ", clusterY:" << clusterY;
0080     int tilesRenderedCount = 0;
0081     QTime t;
0082     t.start();
0083     int const tileX1 = clusterX * m_clusterEdgeLengthTiles;
0084     int const tileX2 = tileX1 + m_clusterEdgeLengthTiles;
0085     int const tileY1 = clusterY * m_clusterEdgeLengthTiles;
0086     int const tileY2 = tileY1 + m_clusterEdgeLengthTiles;
0087 
0088     for ( int tileX = tileX1; tileX < tileX2; ++tileX ) {
0089         QDir const tileDirectory = checkAndCreateDirectory( tileX );
0090         for ( int tileY = tileY1; tileY < tileY2; ++tileY ) {
0091             QImage const osmTile = renderOsmTile( tileX, tileY );
0092 
0093             // hack
0094             if ( osmTile.isNull() )
0095                 continue;
0096 
0097             QString const filename = tileDirectory.path() + QString( "/%1.png" ).arg( tileY );
0098             bool const saved = osmTile.save( filename );
0099             if ( saved )
0100                 ++tilesRenderedCount;
0101             else
0102                 qFatal("Unable to save tile '%s'.", filename.toStdString().c_str() );
0103         }
0104     }
0105     int const durationMs = t.elapsed();
0106     qDebug() << objectName() << "clusterX:" <<clusterX << ", clusterY:" << clusterY
0107              << "rendered:" << tilesRenderedCount << "tiles in" << durationMs << "ms =>"
0108              << static_cast<double>( tilesRenderedCount ) * 1000.0 / static_cast<double>( durationMs ) << "tiles/s";
0109     emit clusterRendered( this );
0110 }
0111 
0112 QImage OsmTileClusterRenderer::renderOsmTile( int const tileX, int const tileY )
0113 {
0114     //qDebug() << objectName() << "renderOsmTile tileX:" << tileX << ", tileY:" << tileY;
0115     int const basePixelX = tileX * m_osmTileEdgeLengthPixel;
0116     int const basePixelY = tileY * m_osmTileEdgeLengthPixel;
0117 
0118     QSize const tileSize( m_osmTileEdgeLengthPixel, m_osmTileEdgeLengthPixel );
0119     QImage tile( tileSize, QImage::Format_ARGB32 );
0120     bool tileEmpty = true;
0121 
0122     for ( int y = 0; y < m_osmTileEdgeLengthPixel; ++y ) {
0123         int const pixelY = basePixelY + y;
0124         double const latRad = osmPixelYtoLatRad( pixelY );
0125 
0126         for ( int x = 0; x < m_osmTileEdgeLengthPixel; ++x ) {
0127             int const pixelX = basePixelX + x;
0128             double const lonRad = osmPixelXtoLonRad( pixelX );
0129 
0130             QRgb color = m_emptyPixel;
0131             for (int i = 0; i < m_mapSourceCount; ++i)
0132             {
0133                color = m_mapSources[i]->pixel( lonRad, latRad );
0134                if ( color != m_emptyPixel ) {
0135                    tileEmpty = false;
0136                    break;
0137                }
0138             }
0139 
0140             tile.setPixel( x, y, color );
0141         }
0142     }
0143     return tileEmpty ? QImage() : tile;
0144 }
0145 
0146 inline double OsmTileClusterRenderer::osmPixelXtoLonRad( int const pixelX ) const
0147 {
0148     double const pixelXd = static_cast<double>( pixelX );
0149     double const osmMapEdgeLengthPixeld = static_cast<double>( m_osmMapEdgeLengthPixel );
0150     return pixelXd * 2.0 * M_PI / osmMapEdgeLengthPixeld - M_PI;
0151 }
0152 
0153 inline double OsmTileClusterRenderer::osmPixelYtoLatRad( int const pixelY ) const
0154 {
0155     double const pixelYd = static_cast<double>( pixelY );
0156     double const osmMapEdgeLengthPixeld = static_cast<double>( m_osmMapEdgeLengthPixel );
0157     return -atan( sinh(( pixelYd - 0.5 * osmMapEdgeLengthPixeld ) * 2.0 * M_PI / osmMapEdgeLengthPixeld ));
0158 }
0159 
0160 #include "moc_OsmTileClusterRenderer.cpp"