ElevationModel.cpp 7.57 KB
Newer Older
Niko Sams's avatar
Niko Sams committed
1
//
2
// This file is part of the Marble Virtual Globe.
Niko Sams's avatar
Niko Sams committed
3 4 5 6 7 8 9
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2011 Niko Sams <niko.sams@gmail.com>
//
10

11
#include "ElevationModel.h"
Niko Sams's avatar
Niko Sams committed
12
#include "GeoSceneHead.h"
Bernhard Beschow's avatar
Bernhard Beschow committed
13
#include "GeoSceneLayer.h"
Niko Sams's avatar
Niko Sams committed
14 15
#include "GeoSceneMap.h"
#include "GeoSceneDocument.h"
16
#include "GeoSceneTextureTileDataset.h"
Bernhard Beschow's avatar
Bernhard Beschow committed
17
#include "HttpDownloadManager.h"
Ander Pijoan's avatar
Ander Pijoan committed
18
#include "Tile.h"
19
#include "TileLoader.h"
20
#include "TileLoaderHelper.h"
21 22
#include "MarbleDebug.h"
#include "MapThemeManager.h"
23
#include "TileId.h"
24
#include "PluginManager.h"
25

26
#include <QCache>
27
#include <QImage>
28
#include <qmath.h>
29

30 31
namespace Marble
{
32

33
class ElevationModelPrivate
34
{
35
public:
36
    ElevationModelPrivate( ElevationModel *_q, HttpDownloadManager *downloadManager, PluginManager* pluginManager )
37
        : q( _q ),
38
          m_tileLoader( downloadManager, pluginManager ),
39 40
          m_textureLayer( nullptr ),
          m_srtmTheme(nullptr)
41 42 43
    {
        m_cache.setMaxCost( 10 ); //keep 10 tiles in memory (~17MB)

44 45
        m_srtmTheme = MapThemeManager::loadMapTheme( "earth/srtm2/srtm2.dgml" );
        if ( !m_srtmTheme ) {
46 47 48
            mDebug() << "Failed to load map theme earth/srtm2/srtm2.dgml. Check your installation. No elevation will be returned.";
            return;
        }
49

50
        const GeoSceneHead *head = m_srtmTheme->head();
51 52
        Q_ASSERT( head );

53
        const GeoSceneMap *map = m_srtmTheme->map();
54
        Q_ASSERT( map );
55

56
        const GeoSceneLayer *sceneLayer = map->layer( head->theme() );
57 58 59 60
        if (!sceneLayer) {
            mDebug() << "Failed to instantiate elevation map. No elevation will be returned.";
            return;
        }
61 62
        Q_ASSERT( sceneLayer );

63
        m_textureLayer = dynamic_cast<GeoSceneTextureTileDataset*>( sceneLayer->datasets().first() );
64
        Q_ASSERT( m_textureLayer );
65
    }
66

67 68 69 70 71
    ~ElevationModelPrivate()
    {
       delete m_srtmTheme;
    }

72 73 74 75 76
    void tileCompleted( const TileId & tileId, const QImage &image )
    {
        m_cache.insert( tileId, new QImage( image ) );
        emit q->updateAvailable();
    }
77

78
public:
79
    ElevationModel *q;
80

81
    TileLoader m_tileLoader;
82
    const GeoSceneTextureTileDataset *m_textureLayer;
83
    QCache<TileId, const QImage> m_cache;
84
    GeoSceneDocument *m_srtmTheme;
85
};
86

87
ElevationModel::ElevationModel( HttpDownloadManager *downloadManager, PluginManager* pluginManager, QObject *parent ) :
Bernhard Beschow's avatar
Bernhard Beschow committed
88
    QObject( parent ),
89
    d( new ElevationModelPrivate( this, downloadManager, pluginManager ) )
90
{
91
    connect( &d->m_tileLoader, SIGNAL(tileCompleted(TileId,QImage)),
92
             this, SLOT(tileCompleted(TileId,QImage)) );
93
}
94

95 96 97 98 99
ElevationModel::~ElevationModel()
{
    delete d;
}

100

101
qreal ElevationModel::height( qreal lon, qreal lat ) const
102
{
103
    if ( !d->m_textureLayer ) {
104 105 106
        return invalidElevationData;
    }

107
    const int tileZoomLevel = TileLoader::maximumTileLevel( *( d->m_textureLayer ) );
108 109
    Q_ASSERT( tileZoomLevel == 9 );

110 111
    const int width = d->m_textureLayer->tileSize().width();
    const int height = d->m_textureLayer->tileSize().height();
112

113 114
    const int numTilesX = TileLoaderHelper::levelToColumn( d->m_textureLayer->levelZeroColumns(), tileZoomLevel );
    const int numTilesY = TileLoaderHelper::levelToRow( d->m_textureLayer->levelZeroRows(), tileZoomLevel );
115 116 117 118 119 120 121 122 123
    Q_ASSERT( numTilesX > 0 );
    Q_ASSERT( numTilesY > 0 );

    qreal textureX = 180 + lon;
    textureX *= numTilesX * width / 360;

    qreal textureY = 90 - lat;
    textureY *= numTilesY * height / 180;

124
    qreal ret = 0;
Niko Sams's avatar
Niko Sams committed
125
    bool hasHeight = false;
126
    qreal noData = 0;
127

128 129 130
    for ( int i = 0; i < 4; ++i ) {
        const int x = static_cast<int>( textureX + ( i % 2 ) );
        const int y = static_cast<int>( textureY + ( i / 2 ) );
131

Niko Sams's avatar
Niko Sams committed
132 133
        //mDebug() << "x" << x << ( x / width );
        //mDebug() << "y" << y << ( y / height );
134

135
        const TileId id( 0, tileZoomLevel, ( x % ( numTilesX * width ) ) / width, ( y % ( numTilesY * height ) ) / height );
Niko Sams's avatar
Niko Sams committed
136
        //mDebug() << "LAT" << lat << "LON" << lon << "tile" << ( x % ( numTilesX * width ) ) / width << ( y % ( numTilesY * height ) ) / height;
137

138
        const QImage *image = d->m_cache[id];
139
        if ( image == nullptr ) {
140
            image = new QImage( d->m_tileLoader.loadTileImage( d->m_textureLayer, id, DownloadBrowse ) );
141
            d->m_cache.insert( id, image );
142 143 144 145 146 147
        }
        Q_ASSERT( image );
        Q_ASSERT( !image->isNull() );
        Q_ASSERT( width == image->width() );
        Q_ASSERT( height == image->height() );

148 149
        const qreal dx = ( textureX > ( qreal )x ) ? textureX - ( qreal )x : ( qreal )x - textureX;
        const qreal dy = ( textureY > ( qreal )y ) ? textureY - ( qreal )y : ( qreal )y - textureY;
Niko Sams's avatar
Niko Sams committed
150

151 152
        Q_ASSERT( 0 <= dx && dx <= 1 );
        Q_ASSERT( 0 <= dy && dy <= 1 );
153 154
        unsigned int pixel = image->pixel( x % width, y % height ) & 0xffff; // 16 valid bits
        short int elevation = (short int) pixel; // and signed type, so just cast it
Niko Sams's avatar
Niko Sams committed
155
        //mDebug() << "(1-dx)" << (1-dx) << "(1-dy)" << (1-dy);
156
        if ( pixel != invalidElevationData ) { //no data?
Niko Sams's avatar
Niko Sams committed
157
            //mDebug() << "got at x" << x % width << "y" << y % height << "a height of" << pixel << "** RGB" << qRed(pixel) << qGreen(pixel) << qBlue(pixel);
158
            ret += ( qreal )elevation * ( 1 - dx ) * ( 1 - dy );
Niko Sams's avatar
Niko Sams committed
159
            hasHeight = true;
160
        } else {
Niko Sams's avatar
Niko Sams committed
161
            //mDebug() << "no data at" <<  x % width << "y" << y % height;
162
            noData += ( 1 - dx ) * ( 1 - dy );
Niko Sams's avatar
Niko Sams committed
163
        }
164
    }
165

166
    if ( !hasHeight ) {
167
        ret = invalidElevationData; //no data
168
    } else {
169
        if ( noData ) {
Niko Sams's avatar
Niko Sams committed
170
            //mDebug() << "NO DATA" << noData;
171
            ret += ( ret / ( 1 - noData ) ) * noData;
172 173
        }
    }
Niko Sams's avatar
Niko Sams committed
174

175
    //mDebug() << ">>>" << lat << lon << "returning an elevation of" << ret;
176 177
    return ret;
}
178

179
QVector<GeoDataCoordinates> ElevationModel::heightProfile( qreal fromLon, qreal fromLat, qreal toLon, qreal toLat ) const
180
{
181
    if ( !d->m_textureLayer ) {
182
        return QVector<GeoDataCoordinates>();
183 184
    }

185
    const int tileZoomLevel = TileLoader::maximumTileLevel( *( d->m_textureLayer ) );
186 187
    const int width = d->m_textureLayer->tileSize().width();
    const int numTilesX = TileLoaderHelper::levelToColumn( d->m_textureLayer->levelZeroColumns(), tileZoomLevel );
188

189
    qreal distPerPixel = ( qreal )360 / ( width * numTilesX );
Niko Sams's avatar
Niko Sams committed
190
    //mDebug() << "heightProfile" << fromLat << fromLon << toLat << toLon << "distPerPixel" << distPerPixel;
191 192 193 194 195

    qreal lat = fromLat;
    qreal lon = fromLon;
    char dirLat = fromLat < toLat ? 1 : -1;
    char dirLon = fromLon < toLon ? 1 : -1;
Niko Sams's avatar
Niko Sams committed
196
    qreal k = qAbs( ( fromLat - toLat ) / ( fromLon - toLon ) );
Niko Sams's avatar
Niko Sams committed
197 198 199
    //mDebug() << "fromLon" << fromLon << "fromLat" << fromLat;
    //mDebug() << "diff lon" << ( fromLon - toLon ) << "diff lat" << ( fromLat - toLat );
    //mDebug() << "dirLon" << QString::number(dirLon) << "dirLat" << QString::number(dirLat) << "k" << k;
200
    QVector<GeoDataCoordinates> ret;
201
    while ( lat*dirLat <= toLat*dirLat && lon*dirLon <= toLon * dirLon ) {
Niko Sams's avatar
Niko Sams committed
202
        //mDebug() << lat << lon;
203
        qreal h = height( lon, lat );
204
        if ( h < 32000 ) {
205
            ret << GeoDataCoordinates( lon, lat, h, GeoDataCoordinates::Degree );
Niko Sams's avatar
Niko Sams committed
206
        }
207
        if ( k < 0.5 ) {
Niko Sams's avatar
Niko Sams committed
208
            //mDebug() << "lon(x) += distPerPixel";
Niko Sams's avatar
Niko Sams committed
209
            lat += distPerPixel * k * dirLat;
210
            lon += distPerPixel * dirLon;
Niko Sams's avatar
Niko Sams committed
211
        } else {
Niko Sams's avatar
Niko Sams committed
212
            //mDebug() << "lat(y) += distPerPixel";
Niko Sams's avatar
Niko Sams committed
213 214
            lat += distPerPixel * dirLat;
            lon += distPerPixel / k * dirLon;
215 216
        }
    }
Niko Sams's avatar
Niko Sams committed
217
    //mDebug() << ret;
218
    return ret;
219 220 221 222 223 224
}

}



225
#include "moc_ElevationModel.cpp"