1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2011 Niko Sams <niko.sams@gmail.com>
4 //
5
6 #include "tccore.h"
7 #include <MarbleGlobal.h>
8 #include <TileLoaderHelper.h>
9 #include <QFile>
10 #include <cmath>
11 #include <QPainter>
12 #include <QProcess>
13 #include <QFileInfo>
14 #include <QDir>
15 #include <QCache>
16
17 using namespace Marble;
18
19 class TileCreatorSourceSrtm : public TileCreatorSource
20 {
21 public:
TileCreatorSourceSrtm(const QString & sourceDir)22 TileCreatorSourceSrtm( const QString &sourceDir )
23 : m_sourceDir( sourceDir )
24 {
25 }
26
fullImageSize() const27 QSize fullImageSize() const override
28 {
29 return QSize( 512*c_defaultTileSize*2, 512*c_defaultTileSize ); //512: 2**9 (9 zoom levels)
30 }
31
tile(int n,int m,int maxTileLevel)32 QImage tile( int n, int m, int maxTileLevel ) override
33 {
34 Q_ASSERT( maxTileLevel == 9 );
35
36 int nmax = TileLoaderHelper::levelToRow( defaultLevelZeroRows, maxTileLevel );
37 Q_ASSERT( nmax == 512 );
38 int mmax = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, maxTileLevel );
39
40 qreal startLat = ( ( ( (qreal)n * 180 ) / nmax ) - 90 ) * -1;
41 qreal startLng = ( ( (qreal)m * 360 ) / mmax ) - 180;
42
43 int startLngPxResized = c_defaultTileSize * m;
44 int startLatPxResized = c_defaultTileSize * n;
45
46
47 if (hgtFileName(std::floor(startLng), std::floor(startLat)).isNull()
48 && hgtFileName(std::floor(startLng)+1, std::floor(startLat)).isNull()
49 && hgtFileName(std::floor(startLng), std::floor(startLat)-1).isNull()
50 && hgtFileName(std::floor(startLng)+1, std::floor(startLat)-1).isNull()
51 ) {
52 QImage ret( c_defaultTileSize, c_defaultTileSize, QImage::Format_ARGB32 );
53 QPainter painter( &ret );
54 painter.fillRect( 0, 0, c_defaultTileSize, c_defaultTileSize, QColor( Qt::black ) );
55 return ret;
56 }
57
58 QImage image( 2400, 2400, QImage::Format_ARGB32 );
59 {
60 QPainter painter( &image );
61 painter.fillRect( 0, 0, 2400, 2400, QColor( Qt::black ) );
62 QImage i = readHgt(std::floor(startLng), std::floor(startLat));
63 painter.drawImage( 0, 0, i );
64 painter.drawImage( 1200, 0, readHgt( std::floor(startLng)+1, std::floor(startLat) ) );
65 painter.drawImage( 0, 1200, readHgt( std::floor(startLng), std::floor(startLat)-1 ) );
66 painter.drawImage( 1200, 1200, readHgt( std::floor(startLng)+1, std::floor(startLat)-1 ) );
67 }
68
69 int imageSizeResized = 2400 * (512 * c_defaultTileSize) / (1200 * 180);
70
71 // Pick the current row and smooth scale it
72 // to make it match the expected size
73 image = image.scaled( QSize(imageSizeResized, imageSizeResized),
74 Qt::IgnoreAspectRatio );
75
76
77 //startL??Px: position in px of what we are looking for
78 int startLngPx = startLng * 1200;
79 startLngPx = ( 180 * 1200 ) + startLngPx;
80 //qDebug() << "startLngPx(/1200)" << (qreal)startLngPx / 1200;
81
82 int startLatPx = startLat * 1200;
83 startLatPx = ( 90 * 1200 ) - startLatPx;
84 //qDebug() << "startLatPx(/1200)" << (qreal)startLatPx / 1200;
85
86
87 //imageL??Px: position in px of image
88 int imageLngPx = std::floor(startLng);
89 imageLngPx = 180 + imageLngPx;
90 //qDebug() << "imageLngPx(/1200)" << imageLngPx << "*1200" << imageLngPx*1200;
91 imageLngPx *= 1200;
92
93 int imageLatPx = std::floor(90 - startLat);
94 //qDebug() << "imageLatPx(/1200)" << imageLatPx;
95 imageLatPx *= 1200;
96
97 //qDebug() << "lng" << imageLngPx << startLngPx << "offset" << startLngPx - imageLngPx;
98 //qDebug() << "lat" << imageLatPx << startLatPx << "offset" << startLatPx - imageLatPx;
99 Q_ASSERT(1200*2 - (startLngPx - imageLngPx) >= 675);
100 Q_ASSERT(1200*2 - (startLatPx - imageLatPx) >= 675);
101 Q_ASSERT(startLngPx - imageLngPx >= 0);
102 Q_ASSERT(startLatPx - imageLatPx >= 0);
103
104 int imageLngPxResized = imageLngPx * 1.6; //(512 * c_defaultTileSize) / (1200 * 180);
105 int imageLatPxResized = imageLatPx * 1.6; //(512 * c_defaultTileSize) / (1200 * 180);
106 //qDebug() << "lng(m)" << startLngPxResized << imageLngPxResized << "diff" << startLngPxResized - imageLngPxResized;
107 //qDebug() << "lat(n)" << startLatPxResized << imageLngPxResized << "diff" << startLatPxResized - imageLatPxResized;
108 Q_ASSERT(startLngPxResized - imageLngPxResized < imageSizeResized);
109 Q_ASSERT(startLatPxResized - imageLatPxResized < imageSizeResized);
110 Q_ASSERT(startLngPxResized - imageLngPxResized >= 0);
111 Q_ASSERT(startLatPxResized - imageLatPxResized >= 0);
112
113 QImage croppedImage = image.copy(startLngPx - imageLngPx, startLatPx - imageLatPx, 675, 675);
114 QImage ret = image.copy(startLngPxResized - imageLngPxResized, startLatPxResized - imageLatPxResized, c_defaultTileSize, c_defaultTileSize);
115 //qDebug() << image.size() << ret.size();
116 return ret;
117 }
118
119 private:
hgtFileName(int lng,int lat) const120 QString hgtFileName( int lng, int lat ) const
121 {
122 QChar EW(QLatin1Char(lng >= 0 ? 'E' : 'W'));
123 QChar NS(QLatin1Char(lat >= 0 ? 'N' : 'S'));
124
125 QStringList dirs;
126 dirs << "Africa" << "Australia" << "Eurasia" << "Silands" << "North_America" << "South_America";
127 for( const QString &dir: dirs) {
128 QString fileName = m_sourceDir + QLatin1Char('/') + dir + QLatin1Char('/');
129 if ( lat < 0 ) lat *= -1;
130 fileName += QString( "%1%2%3%4.hgt" ).arg( NS ).arg( lat<0 ? lat*-1 : lat, 2, 10, QLatin1Char('0') )
131 .arg( EW ).arg( lng<0 ? lng*-1 : lng, 3, 10, QLatin1Char('0' ) );
132 //qDebug() << fileName;
133
134 if (!QFile::exists(fileName) && QFile::exists(fileName + QLatin1String(".zip"))) {
135 qDebug() << "zip found, unzipping";
136 QProcess p;
137 p.execute("unzip", QStringList() << fileName + QLatin1String(".zip"));
138 p.waitForFinished();
139 QFile(QDir::currentPath() + QLatin1Char('/') + QFileInfo(fileName).fileName()).rename(fileName);
140 }
141 if ( QFile::exists( fileName ) ) {
142 return fileName;
143 }
144 }
145
146 return QString();
147 }
148
readHgt(int lng,int lat)149 QImage readHgt( int lng, int lat )
150 {
151 static QCache<QPair<int, int>, QImage > cache( 10 );
152 if ( cache.contains( qMakePair( lng, lat ) ) ) {
153 return *cache[ qMakePair( lng, lat ) ];
154 }
155 QString fileName = hgtFileName( lng, lat );
156 if ( fileName.isNull() ) {
157 //qDebug() << lng << lat << "hgt file does not exist, returning null image";
158 return QImage();
159 } else {
160 //qDebug() << lng << lat << "reading hgt file" << fileName;
161 }
162
163 QFile file( fileName );
164
165 file.open( QIODevice::ReadOnly );
166 int iLat = 0;
167 int iLng = 0;
168
169 //hgt file is 1201px large, but the last px is overlapping
170 QImage image( 1200, 1200, QImage::Format_ARGB32 );
171 while(true) {
172 QByteArray data = file.read( 2 );
173
174 if ( iLng < 1200 ) {
175 unsigned short height = *(unsigned short*)data.data();
176 height = ( height << 8 | height >> 8 );
177 unsigned int pixel;
178 pixel = height;
179 pixel += 0xFF000000; //fully opaque
180 image.setPixel( iLng, iLat, pixel );
181 }
182
183 if ( iLat >= 1199 && iLng >= 1199 ) break;
184
185 iLng++;
186 if ( iLng > 1200 ) { //here not 1199 but one more, because of overlapping px at the end of the line
187 iLng = 0;
188 iLat++;
189 }
190 }
191 file.close();
192
193 cache.insert( qMakePair( lng, lat ), new QImage( image ) );
194
195 return image;
196 }
197
198 QString m_sourceDir;
199 };
200
TCCoreApplication(int argc,char ** argv)201 TCCoreApplication::TCCoreApplication( int argc, char ** argv ) : QCoreApplication( argc, argv )
202 {
203
204 if( !(argc < 2) )
205 {
206 TileCreatorSource *source = new TileCreatorSourceSrtm( argv[1] );
207 m_tilecreator = new TileCreator( source, "false", argv[2] );
208 m_tilecreator->setTileFormat( "png" );
209 m_tilecreator->setTileQuality( 25 );
210 m_tilecreator->setResume( true );
211 m_tilecreator->setVerifyExactResult( true );
212 connect( m_tilecreator, SIGNAL(finished()), this, SLOT(quit()) );
213 m_tilecreator->start();
214 }
215 }
216