1 #include "OsmTileClusterRenderer.h"
2 
3 #include "ReadOnlyMapImage.h"
4 
5 #include <QDebug>
6 #include <QTime>
7 
8 #include <cmath>
9 
OsmTileClusterRenderer(QObject * const parent)10 OsmTileClusterRenderer::OsmTileClusterRenderer( QObject * const parent )
11     : QObject( parent ),
12       m_osmTileEdgeLengthPixel( 256 ),
13       m_emptyPixel( qRgba( 0, 0, 0, 255 )),
14       m_osmBaseDirectory(),
15       m_osmTileLevel(),
16       m_osmMapEdgeLengthPixel(),
17       m_clusterEdgeLengthTiles(),
18       m_mapSourceDefinitions(),
19       m_mapSources(),
20       m_mapSourceCount()
21 {
22 }
23 
setClusterEdgeLengthTiles(int const clusterEdgeLengthTiles)24 void OsmTileClusterRenderer::setClusterEdgeLengthTiles( int const clusterEdgeLengthTiles )
25 {
26     m_clusterEdgeLengthTiles = clusterEdgeLengthTiles;
27 }
28 
setMapSources(QVector<ReadOnlyMapDefinition> const & mapSourceDefinitions)29 void OsmTileClusterRenderer::setMapSources( QVector<ReadOnlyMapDefinition> const & mapSourceDefinitions )
30 {
31     m_mapSourceDefinitions = mapSourceDefinitions;
32 }
33 
setOsmBaseDirectory(QDir const & osmBaseDirectory)34 void OsmTileClusterRenderer::setOsmBaseDirectory( QDir const & osmBaseDirectory )
35 {
36     m_osmBaseDirectory = osmBaseDirectory;
37 }
38 
setOsmTileLevel(int const level)39 void OsmTileClusterRenderer::setOsmTileLevel( int const level )
40 {
41     m_osmTileLevel = level;
42     int const osmMapEdgeLengthTiles = pow( 2, m_osmTileLevel );
43     m_osmMapEdgeLengthPixel = osmMapEdgeLengthTiles * m_osmTileEdgeLengthPixel;
44     qDebug() << "osmTileLevel:" << m_osmTileLevel
45              << "\nosmMapEdgeLengthTiles:" << osmMapEdgeLengthTiles
46              << "\nosmMapEdgeLengthPixel:" << m_osmMapEdgeLengthPixel;
47 }
48 
checkAndCreateDirectory(int const tileX) const49 QDir OsmTileClusterRenderer::checkAndCreateDirectory( int const tileX ) const
50 {
51     QDir const tileDirectory( m_osmBaseDirectory.path() + QString("/%1/%2").arg( m_osmTileLevel ).arg( tileX ));
52     if ( !tileDirectory.exists() ) {
53         bool const created = tileDirectory.mkpath( tileDirectory.path() );
54         if ( !created ) {
55             // maybe it was created in the meantime by a different thread, then it should be there now
56             if ( !tileDirectory.exists() )
57                 qFatal("Unable to create directory '%s'.", tileDirectory.path().toStdString().c_str() );
58         }
59     }
60     return tileDirectory;
61 }
62 
initMapSources()63 void OsmTileClusterRenderer::initMapSources()
64 {
65     QVector<ReadOnlyMapDefinition>::const_iterator pos = m_mapSourceDefinitions.constBegin();
66     QVector<ReadOnlyMapDefinition>::const_iterator const end = m_mapSourceDefinitions.constEnd();
67     for (; pos != end; ++pos )
68     {
69         ReadOnlyMapImage * const mapImage = (*pos).createReadOnlyMap();
70         if ( !mapImage )
71             qFatal("Invalid map source definition.");
72         m_mapSources.push_back( mapImage );
73     }
74     m_mapSourceCount = m_mapSources.count();
75 }
76 
renderOsmTileCluster(int const clusterX,int const clusterY)77 void OsmTileClusterRenderer::renderOsmTileCluster( int const clusterX, int const clusterY )
78 {
79     qDebug() << objectName() << "rendering clusterX:" << clusterX << ", clusterY:" << clusterY;
80     int tilesRenderedCount = 0;
81     QTime t;
82     t.start();
83     int const tileX1 = clusterX * m_clusterEdgeLengthTiles;
84     int const tileX2 = tileX1 + m_clusterEdgeLengthTiles;
85     int const tileY1 = clusterY * m_clusterEdgeLengthTiles;
86     int const tileY2 = tileY1 + m_clusterEdgeLengthTiles;
87 
88     for ( int tileX = tileX1; tileX < tileX2; ++tileX ) {
89         QDir const tileDirectory = checkAndCreateDirectory( tileX );
90         for ( int tileY = tileY1; tileY < tileY2; ++tileY ) {
91             QImage const osmTile = renderOsmTile( tileX, tileY );
92 
93             // hack
94             if ( osmTile.isNull() )
95                 continue;
96 
97             QString const filename = tileDirectory.path() + QString( "/%1.png" ).arg( tileY );
98             bool const saved = osmTile.save( filename );
99             if ( saved )
100                 ++tilesRenderedCount;
101             else
102                 qFatal("Unable to save tile '%s'.", filename.toStdString().c_str() );
103         }
104     }
105     int const durationMs = t.elapsed();
106     qDebug() << objectName() << "clusterX:" <<clusterX << ", clusterY:" << clusterY
107              << "rendered:" << tilesRenderedCount << "tiles in" << durationMs << "ms =>"
108              << static_cast<double>( tilesRenderedCount ) * 1000.0 / static_cast<double>( durationMs ) << "tiles/s";
109     emit clusterRendered( this );
110 }
111 
renderOsmTile(int const tileX,int const tileY)112 QImage OsmTileClusterRenderer::renderOsmTile( int const tileX, int const tileY )
113 {
114     //qDebug() << objectName() << "renderOsmTile tileX:" << tileX << ", tileY:" << tileY;
115     int const basePixelX = tileX * m_osmTileEdgeLengthPixel;
116     int const basePixelY = tileY * m_osmTileEdgeLengthPixel;
117 
118     QSize const tileSize( m_osmTileEdgeLengthPixel, m_osmTileEdgeLengthPixel );
119     QImage tile( tileSize, QImage::Format_ARGB32 );
120     bool tileEmpty = true;
121 
122     for ( int y = 0; y < m_osmTileEdgeLengthPixel; ++y ) {
123         int const pixelY = basePixelY + y;
124         double const latRad = osmPixelYtoLatRad( pixelY );
125 
126         for ( int x = 0; x < m_osmTileEdgeLengthPixel; ++x ) {
127             int const pixelX = basePixelX + x;
128             double const lonRad = osmPixelXtoLonRad( pixelX );
129 
130             QRgb color = m_emptyPixel;
131             for (int i = 0; i < m_mapSourceCount; ++i)
132             {
133                color = m_mapSources[i]->pixel( lonRad, latRad );
134                if ( color != m_emptyPixel ) {
135                    tileEmpty = false;
136                    break;
137                }
138             }
139 
140             tile.setPixel( x, y, color );
141         }
142     }
143     return tileEmpty ? QImage() : tile;
144 }
145 
osmPixelXtoLonRad(int const pixelX) const146 inline double OsmTileClusterRenderer::osmPixelXtoLonRad( int const pixelX ) const
147 {
148     double const pixelXd = static_cast<double>( pixelX );
149     double const osmMapEdgeLengthPixeld = static_cast<double>( m_osmMapEdgeLengthPixel );
150     return pixelXd * 2.0 * M_PI / osmMapEdgeLengthPixeld - M_PI;
151 }
152 
osmPixelYtoLatRad(int const pixelY) const153 inline double OsmTileClusterRenderer::osmPixelYtoLatRad( int const pixelY ) const
154 {
155     double const pixelYd = static_cast<double>( pixelY );
156     double const osmMapEdgeLengthPixeld = static_cast<double>( m_osmMapEdgeLengthPixel );
157     return -atan( sinh(( pixelYd - 0.5 * osmMapEdgeLengthPixeld ) * 2.0 * M_PI / osmMapEdgeLengthPixeld ));
158 }
159 
160 #include "moc_OsmTileClusterRenderer.cpp"
161