Marble

ElevationModel.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2011 Niko Sams <[email protected]>
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 
25 namespace Marble
26 {
27 
28 class ElevationModelPrivate
29 {
30 public:
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 
73 public:
74  ElevationModel *q;
75 
76  TileLoader m_tileLoader;
77  const GeoSceneTextureTileDataset *m_textureLayer;
79  GeoSceneDocument *m_srtmTheme;
80 };
81 
82 ElevationModel::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 
90 ElevationModel::~ElevationModel()
91 {
92  delete d;
93 }
94 
95 
96 qreal 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 
174 QVector<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"
@ DownloadBrowse
Browsing mode, normal operation of Marble, like a web browser.
Definition: MarbleGlobal.h:155
int height() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QRgb pixel(int x, int y) const const
bool isNull() const const
Binds a QML item to a specific geodetic location in screen coordinates.
static GeoSceneDocument * loadMapTheme(const QString &mapThemeStringID)
Returns the map theme as a GeoSceneDocument object.
QFuture< void > map(Sequence &sequence, MapFunctor function)
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
int width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Sep 21 2023 04:12:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.