Marble

ElevationModel.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2011 Niko Sams <niko.sams@gmail.com>
4//
5
6#include "ElevationModel.h"
7#include "GeoSceneHead.h"
8#include "GeoSceneLayer.h"
9#include "GeoSceneMap.h"
10#include "GeoSceneDocument.h"
11#include "GeoSceneTextureTileDataset.h"
12#include "HttpDownloadManager.h"
13#include "Tile.h"
14#include "TileLoader.h"
15#include "TileLoaderHelper.h"
16#include "MarbleDebug.h"
17#include "MapThemeManager.h"
18#include "TileId.h"
19#include "PluginManager.h"
20
21#include <QCache>
22#include <QImage>
23#include <qmath.h>
24
25namespace Marble
26{
27
28class ElevationModelPrivate
29{
30public:
31 ElevationModelPrivate( ElevationModel *_q, HttpDownloadManager *downloadManager, PluginManager* pluginManager )
32 : q( _q ),
33 m_tileLoader( downloadManager, pluginManager ),
34 m_textureLayer( nullptr ),
35 m_srtmTheme(nullptr)
36 {
37 m_cache.setMaxCost( 10 ); //keep 10 tiles in memory (~17MB)
38
39 m_srtmTheme = MapThemeManager::loadMapTheme( "earth/srtm2/srtm2.dgml" );
40 if ( !m_srtmTheme ) {
41 mDebug() << "Failed to load map theme earth/srtm2/srtm2.dgml. Check your installation. No elevation will be returned.";
42 return;
43 }
44
45 const GeoSceneHead *head = m_srtmTheme->head();
46 Q_ASSERT( head );
47
48 const GeoSceneMap *map = m_srtmTheme->map();
49 Q_ASSERT( map );
50
51 const GeoSceneLayer *sceneLayer = map->layer( head->theme() );
52 if (!sceneLayer) {
53 mDebug() << "Failed to instantiate elevation map. No elevation will be returned.";
54 return;
55 }
56 Q_ASSERT( sceneLayer );
57
58 m_textureLayer = dynamic_cast<GeoSceneTextureTileDataset*>( sceneLayer->datasets().first() );
59 Q_ASSERT( m_textureLayer );
60 }
61
62 ~ElevationModelPrivate()
63 {
64 delete m_srtmTheme;
65 }
66
67 void tileCompleted( const TileId & tileId, const QImage &image )
68 {
69 m_cache.insert( tileId, new QImage( image ) );
70 emit q->updateAvailable();
71 }
72
73public:
74 ElevationModel *q;
75
76 TileLoader m_tileLoader;
77 const GeoSceneTextureTileDataset *m_textureLayer;
79 GeoSceneDocument *m_srtmTheme;
80};
81
82ElevationModel::ElevationModel( HttpDownloadManager *downloadManager, PluginManager* pluginManager, QObject *parent ) :
83 QObject( parent ),
84 d( new ElevationModelPrivate( this, downloadManager, pluginManager ) )
85{
86 connect( &d->m_tileLoader, SIGNAL(tileCompleted(TileId,QImage)),
87 this, SLOT(tileCompleted(TileId,QImage)) );
88}
89
90ElevationModel::~ElevationModel()
91{
92 delete d;
93}
94
95
96qreal ElevationModel::height( qreal lon, qreal lat ) const
97{
98 if ( !d->m_textureLayer ) {
99 return invalidElevationData;
100 }
101
102 const int tileZoomLevel = TileLoader::maximumTileLevel( *( d->m_textureLayer ) );
103 Q_ASSERT( tileZoomLevel == 9 );
104
105 const int width = d->m_textureLayer->tileSize().width();
106 const int height = d->m_textureLayer->tileSize().height();
107
108 const int numTilesX = TileLoaderHelper::levelToColumn( d->m_textureLayer->levelZeroColumns(), tileZoomLevel );
109 const int numTilesY = TileLoaderHelper::levelToRow( d->m_textureLayer->levelZeroRows(), tileZoomLevel );
110 Q_ASSERT( numTilesX > 0 );
111 Q_ASSERT( numTilesY > 0 );
112
113 qreal textureX = 180 + lon;
114 textureX *= numTilesX * width / 360;
115
116 qreal textureY = 90 - lat;
117 textureY *= numTilesY * height / 180;
118
119 qreal ret = 0;
120 bool hasHeight = false;
121 qreal noData = 0;
122
123 for ( int i = 0; i < 4; ++i ) {
124 const int x = static_cast<int>( textureX + ( i % 2 ) );
125 const int y = static_cast<int>( textureY + ( i / 2 ) );
126
127 //mDebug() << "x" << x << ( x / width );
128 //mDebug() << "y" << y << ( y / height );
129
130 const TileId id( 0, tileZoomLevel, ( x % ( numTilesX * width ) ) / width, ( y % ( numTilesY * height ) ) / height );
131 //mDebug() << "LAT" << lat << "LON" << lon << "tile" << ( x % ( numTilesX * width ) ) / width << ( y % ( numTilesY * height ) ) / height;
132
133 const QImage *image = d->m_cache[id];
134 if ( image == nullptr ) {
135 image = new QImage( d->m_tileLoader.loadTileImage( d->m_textureLayer, id, DownloadBrowse ) );
136 d->m_cache.insert( id, image );
137 }
138 Q_ASSERT( image );
139 Q_ASSERT( !image->isNull() );
140 Q_ASSERT( width == image->width() );
141 Q_ASSERT( height == image->height() );
142
143 const qreal dx = ( textureX > ( qreal )x ) ? textureX - ( qreal )x : ( qreal )x - textureX;
144 const qreal dy = ( textureY > ( qreal )y ) ? textureY - ( qreal )y : ( qreal )y - textureY;
145
146 Q_ASSERT( 0 <= dx && dx <= 1 );
147 Q_ASSERT( 0 <= dy && dy <= 1 );
148 unsigned int pixel = image->pixel( x % width, y % height ) & 0xffff; // 16 valid bits
149 short int elevation = (short int) pixel; // and signed type, so just cast it
150 //mDebug() << "(1-dx)" << (1-dx) << "(1-dy)" << (1-dy);
151 if ( pixel != invalidElevationData ) { //no data?
152 //mDebug() << "got at x" << x % width << "y" << y % height << "a height of" << pixel << "** RGB" << qRed(pixel) << qGreen(pixel) << qBlue(pixel);
153 ret += ( qreal )elevation * ( 1 - dx ) * ( 1 - dy );
154 hasHeight = true;
155 } else {
156 //mDebug() << "no data at" << x % width << "y" << y % height;
157 noData += ( 1 - dx ) * ( 1 - dy );
158 }
159 }
160
161 if ( !hasHeight ) {
162 ret = invalidElevationData; //no data
163 } else {
164 if ( noData ) {
165 //mDebug() << "NO DATA" << noData;
166 ret += ( ret / ( 1 - noData ) ) * noData;
167 }
168 }
169
170 //mDebug() << ">>>" << lat << lon << "returning an elevation of" << ret;
171 return ret;
172}
173
174QVector<GeoDataCoordinates> ElevationModel::heightProfile( qreal fromLon, qreal fromLat, qreal toLon, qreal toLat ) const
175{
176 if ( !d->m_textureLayer ) {
178 }
179
180 const int tileZoomLevel = TileLoader::maximumTileLevel( *( d->m_textureLayer ) );
181 const int width = d->m_textureLayer->tileSize().width();
182 const int numTilesX = TileLoaderHelper::levelToColumn( d->m_textureLayer->levelZeroColumns(), tileZoomLevel );
183
184 qreal distPerPixel = ( qreal )360 / ( width * numTilesX );
185 //mDebug() << "heightProfile" << fromLat << fromLon << toLat << toLon << "distPerPixel" << distPerPixel;
186
187 qreal lat = fromLat;
188 qreal lon = fromLon;
189 char dirLat = fromLat < toLat ? 1 : -1;
190 char dirLon = fromLon < toLon ? 1 : -1;
191 qreal k = qAbs( ( fromLat - toLat ) / ( fromLon - toLon ) );
192 //mDebug() << "fromLon" << fromLon << "fromLat" << fromLat;
193 //mDebug() << "diff lon" << ( fromLon - toLon ) << "diff lat" << ( fromLat - toLat );
194 //mDebug() << "dirLon" << QString::number(dirLon) << "dirLat" << QString::number(dirLat) << "k" << k;
196 while ( lat*dirLat <= toLat*dirLat && lon*dirLon <= toLon * dirLon ) {
197 //mDebug() << lat << lon;
198 qreal h = height( lon, lat );
199 if ( h < 32000 ) {
200 ret << GeoDataCoordinates( lon, lat, h, GeoDataCoordinates::Degree );
201 }
202 if ( k < 0.5 ) {
203 //mDebug() << "lon(x) += distPerPixel";
204 lat += distPerPixel * k * dirLat;
205 lon += distPerPixel * dirLon;
206 } else {
207 //mDebug() << "lat(y) += distPerPixel";
208 lat += distPerPixel * dirLat;
209 lon += distPerPixel / k * dirLon;
210 }
211 }
212 //mDebug() << ret;
213 return ret;
214}
215
216}
217
218
219
220#include "moc_ElevationModel.cpp"
static GeoSceneDocument * loadMapTheme(const QString &mapThemeStringID)
Returns the map theme as a GeoSceneDocument object.
Binds a QML item to a specific geodetic location in screen coordinates.
bool insert(const Key &key, T *object, qsizetype cost)
void setMaxCost(qsizetype cost)
int height() const const
bool isNull() const const
QRgb pixel(const QPoint &position) const const
int width() const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 21 2024 12:00:06 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.