Marble

MergedLayerDecorator.cpp
1// SPDX-FileCopyrightText: 2008 David Roberts <dvdr18@gmail.com>
2// SPDX-FileCopyrightText: 2009 Jens-Michael Hoffmann <jensmh@gmx.de>
3// SPDX-FileCopyrightText: 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
4//
5// SPDX-License-Identifier: LGPL-2.1-or-later
6
7
8#include "MergedLayerDecorator.h"
9
10#include "blendings/Blending.h"
11#include "blendings/BlendingFactory.h"
12#include "SunLocator.h"
13#include "MarbleMath.h"
14#include "MarbleDebug.h"
15#include "GeoDataGroundOverlay.h"
16#include "GeoSceneTextureTileDataset.h"
17#include "ImageF.h"
18#include "StackedTile.h"
19#include "TileLoaderHelper.h"
20#include "TextureTile.h"
21#include "TileLoader.h"
22#include "RenderState.h"
23
24#include "GeoDataCoordinates.h"
25
26#include <QPointer>
27#include <QPainter>
28#include <QPainterPath>
29
30using namespace Marble;
31
32class Q_DECL_HIDDEN MergedLayerDecorator::Private
33{
34public:
35 Private( TileLoader *tileLoader, const SunLocator *sunLocator );
36
37 static int maxDivisor( int maximum, int fullLength );
38
39 StackedTile *createTile( const QVector<QSharedPointer<TextureTile> > &tiles ) const;
40
41 void renderGroundOverlays( QImage *tileImage, const QVector<QSharedPointer<TextureTile> > &tiles ) const;
42 void paintSunShading( QImage *tileImage, const TileId &id ) const;
43 void paintTileId( QImage *tileImage, const TileId &id ) const;
44
45 void detectMaxTileLevel();
46 QVector<const GeoSceneTextureTileDataset *> findRelevantTextureLayers( const TileId &stackedTileId ) const;
47
48 TileLoader *const m_tileLoader;
49 const SunLocator *const m_sunLocator;
50 BlendingFactory m_blendingFactory;
53 int m_maxTileLevel;
54 QString m_themeId;
55 int m_levelZeroColumns;
56 int m_levelZeroRows;
57 bool m_showSunShading;
58 bool m_showCityLights;
59 bool m_showTileId;
60};
61
62MergedLayerDecorator::Private::Private( TileLoader *tileLoader, const SunLocator *sunLocator ) :
63 m_tileLoader( tileLoader ),
64 m_sunLocator( sunLocator ),
65 m_blendingFactory( sunLocator ),
66 m_textureLayers(),
67 m_maxTileLevel( 0 ),
68 m_themeId(),
69 m_levelZeroColumns( 0 ),
70 m_levelZeroRows( 0 ),
71 m_showSunShading( false ),
72 m_showCityLights( false ),
73 m_showTileId( false )
74{
75}
76
77MergedLayerDecorator::MergedLayerDecorator( TileLoader * const tileLoader,
78 const SunLocator* sunLocator )
79 : d( new Private( tileLoader, sunLocator ) )
80{
81}
82
83MergedLayerDecorator::~MergedLayerDecorator()
84{
85 delete d;
86}
87
88void MergedLayerDecorator::setTextureLayers( const QVector<const GeoSceneTextureTileDataset *> &textureLayers )
89{
90 if ( textureLayers.count() > 0 ) {
91 const GeoSceneTileDataset *const firstTexture = textureLayers.at( 0 );
92 d->m_levelZeroColumns = firstTexture->levelZeroColumns();
93 d->m_levelZeroRows = firstTexture->levelZeroRows();
94 d->m_blendingFactory.setLevelZeroLayout( d->m_levelZeroColumns, d->m_levelZeroRows );
95 d->m_themeId = QLatin1String("maps/") + firstTexture->sourceDir();
96 }
97
98 d->m_textureLayers = textureLayers;
99
100 d->detectMaxTileLevel();
101}
102
103void MergedLayerDecorator::updateGroundOverlays(const QList<const GeoDataGroundOverlay *> &groundOverlays )
104{
105 d->m_groundOverlays = groundOverlays;
106}
107
108
109int MergedLayerDecorator::textureLayersSize() const
110{
111 return d->m_textureLayers.size();
112}
113
114int MergedLayerDecorator::maximumTileLevel() const
115{
116 return d->m_maxTileLevel;
117}
118
119int MergedLayerDecorator::tileColumnCount( int level ) const
120{
121 Q_ASSERT( !d->m_textureLayers.isEmpty() );
122
123 const int levelZeroColumns = d->m_textureLayers.at( 0 )->levelZeroColumns();
124
125 return TileLoaderHelper::levelToColumn( levelZeroColumns, level );
126}
127
128int MergedLayerDecorator::tileRowCount( int level ) const
129{
130 Q_ASSERT( !d->m_textureLayers.isEmpty() );
131
132 const int levelZeroRows = d->m_textureLayers.at( 0 )->levelZeroRows();
133
134 return TileLoaderHelper::levelToRow( levelZeroRows, level );
135}
136
137const GeoSceneAbstractTileProjection *MergedLayerDecorator::tileProjection() const
138{
139 Q_ASSERT( !d->m_textureLayers.isEmpty() );
140
141 return d->m_textureLayers.at(0)->tileProjection();
142}
143
144QSize MergedLayerDecorator::tileSize() const
145{
146 Q_ASSERT( !d->m_textureLayers.isEmpty() );
147
148 return d->m_textureLayers.at( 0 )->tileSize();
149}
150
151StackedTile *MergedLayerDecorator::Private::createTile( const QVector<QSharedPointer<TextureTile> > &tiles ) const
152{
153 Q_ASSERT( !tiles.isEmpty() );
154
155 const TileId firstId = tiles.first()->id();
156 const TileId id( 0, firstId.zoomLevel(), firstId.x(), firstId.y() );
157
158 // Image for blending all the texture tiles on it
159 QImage resultImage;
160
161 // if there are more than one active texture layers, we have to convert the
162 // result tile into QImage::Format_ARGB32_Premultiplied to make blending possible
163 const bool withConversion = tiles.count() > 1 || m_showSunShading || m_showTileId || !m_groundOverlays.isEmpty();
164 for ( const QSharedPointer<TextureTile> &tile: tiles ) {
165
166 // Image blending. If there are several images in the same tile (like clouds
167 // or hillshading images over the map) blend them all into only one image
168
169 const Blending *const blending = tile->blending();
170 if ( blending ) {
171
172 mDebug() << "blending";
173
174 if ( resultImage.isNull() ) {
175 resultImage = QImage( tile->image()->size(), QImage::Format_ARGB32_Premultiplied );
176 }
177
178 blending->blend( &resultImage, tile.data() );
179 }
180 else {
181 mDebug() << "no blending defined => copying top over bottom image";
182 if ( withConversion ) {
183 resultImage = tile->image()->convertToFormat( QImage::Format_ARGB32_Premultiplied );
184 } else {
185 resultImage = tile->image()->copy();
186 }
187 }
188 }
189
190 renderGroundOverlays( &resultImage, tiles );
191
192 if ( m_showSunShading && !m_showCityLights ) {
193 paintSunShading( &resultImage, id );
194 }
195
196 if ( m_showTileId ) {
197 paintTileId( &resultImage, id );
198 }
199
200 return new StackedTile( id, resultImage, tiles );
201}
202
203void MergedLayerDecorator::Private::renderGroundOverlays( QImage *tileImage, const QVector<QSharedPointer<TextureTile> > &tiles ) const
204{
205
206 /* All tiles are covering the same area. Pick one. */
207 const TileId tileId = tiles.first()->id();
208
209 const GeoDataLatLonBox tileLatLonBox = findRelevantTextureLayers(tileId).first()->tileProjection()->geoCoordinates(tileId);
210
211 /* Map the ground overlay to the image. */
212 for ( int i = 0; i < m_groundOverlays.size(); ++i ) {
213
214 const GeoDataGroundOverlay* overlay = m_groundOverlays.at( i );
215 if ( !overlay->isGloballyVisible() ) {
216 continue;
217 }
218
219 const GeoDataLatLonBox overlayLatLonBox = overlay->latLonBox();
220
221 if ( !tileLatLonBox.intersects( overlayLatLonBox.toCircumscribedRectangle() ) ) {
222 continue;
223 }
224
225 const qreal pixelToLat = tileLatLonBox.height() / tileImage->height();
226 const qreal pixelToLon = tileLatLonBox.width() / tileImage->width();
227
228 const qreal latToPixel = overlay->icon().height() / overlayLatLonBox.height();
229 const qreal lonToPixel = overlay->icon().width() / overlayLatLonBox.width();
230
231 const qreal global_height = tileImage->height()
232 * TileLoaderHelper::levelToRow( m_levelZeroRows, tileId.zoomLevel() );
233 const qreal pixel2Rad = M_PI / global_height;
234 const qreal rad2Pixel = global_height / M_PI;
235
236 qreal latPixelPosition = rad2Pixel/2 * gdInv(tileLatLonBox.north());
237 const bool isMercatorTileProjection = (m_textureLayers.at( 0 )->tileProjectionType() == GeoSceneAbstractTileProjection::Mercator);
238
239 for ( int y = 0; y < tileImage->height(); ++y ) {
240 QRgb *scanLine = ( QRgb* ) ( tileImage->scanLine( y ) );
241
242 const qreal lat = isMercatorTileProjection
243 ? gd(2 * (latPixelPosition - y) * pixel2Rad )
244 : tileLatLonBox.north() - y * pixelToLat;
245
246 for ( int x = 0; x < tileImage->width(); ++x, ++scanLine ) {
247 qreal lon = GeoDataCoordinates::normalizeLon( tileLatLonBox.west() + x * pixelToLon );
248
249 GeoDataCoordinates coords(lon, lat);
250 GeoDataCoordinates rotatedCoords(coords);
251
252 if (overlay->latLonBox().rotation() != 0) {
253 // Possible TODO: Make this faster by creating the axisMatrix beforehand
254 // and just call Quaternion::rotateAroundAxis(const matrix &m) here.
255 rotatedCoords = coords.rotateAround(overlayLatLonBox.center(), -overlay->latLonBox().rotation());
256 }
257
258 // TODO: The rotated latLonBox is bigger. We need to take this into account.
259 // (Currently the GroundOverlay sometimes gets clipped because of that)
260 if ( overlay->latLonBox().contains( rotatedCoords ) ) {
261
262 qreal px = GeoDataLatLonBox::width( rotatedCoords.longitude(), overlayLatLonBox.west() ) * lonToPixel;
263 qreal py = (qreal)( overlay->icon().height() ) - ( GeoDataLatLonBox::height( rotatedCoords.latitude(), overlayLatLonBox.south() ) * latToPixel ) - 1;
264
265 if ( px >= 0 && px < overlay->icon().width() && py >= 0 && py < overlay->icon().height() ) {
266 int alpha = qAlpha( overlay->icon().pixel( px, py ) );
267 if ( alpha != 0 )
268 {
269 QRgb result = ImageF::pixelF( overlay->icon(), px, py );
270
271 if (alpha == 255)
272 {
273 *scanLine = result;
274 }
275 else
276 {
277 *scanLine = qRgb( ( alpha * qRed(result) + (255 - alpha) * qRed(*scanLine) ) / 255,
278 ( alpha * qGreen(result) + (255 - alpha) * qGreen(*scanLine) ) / 255,
279 ( alpha * qBlue(result) + (255 - alpha) * qBlue(*scanLine) ) / 255 );
280 }
281 }
282 }
283 }
284 }
285 }
286 }
287}
288
289StackedTile *MergedLayerDecorator::loadTile( const TileId &stackedTileId )
290{
291 const QVector<const GeoSceneTextureTileDataset *> textureLayers = d->findRelevantTextureLayers( stackedTileId );
293 tiles.reserve(textureLayers.size());
294
295 for ( const GeoSceneTextureTileDataset *layer: textureLayers ) {
296 const TileId tileId( layer->sourceDir(), stackedTileId.zoomLevel(),
297 stackedTileId.x(), stackedTileId.y() );
298
299 mDebug() << layer->sourceDir() << tileId << layer->tileSize() << layer->fileFormat();
300
301 // Blending (how to merge the images into an only image)
302 const Blending *blending = d->m_blendingFactory.findBlending( layer->blending() );
303 if ( blending == nullptr && !layer->blending().isEmpty() ) {
304 mDebug() << "could not find blending" << layer->blending();
305 }
306
307 const GeoSceneTextureTileDataset *const textureLayer = static_cast<const GeoSceneTextureTileDataset *>( layer );
308 const QImage tileImage = d->m_tileLoader->loadTileImage( textureLayer, tileId, DownloadBrowse );
309
310 QSharedPointer<TextureTile> tile( new TextureTile( tileId, tileImage, blending ) );
311 tiles.append( tile );
312 }
313
314 Q_ASSERT( !tiles.isEmpty() );
315
316 return d->createTile( tiles );
317}
318
319RenderState MergedLayerDecorator::renderState( const TileId &stackedTileId ) const
320{
321 QString const nameTemplate = "Tile %1/%2/%3";
322 RenderState state( nameTemplate.arg( stackedTileId.zoomLevel() )
323 .arg( stackedTileId.x() )
324 .arg( stackedTileId.y() ) );
325 const QVector<const GeoSceneTextureTileDataset *> textureLayers = d->findRelevantTextureLayers( stackedTileId );
326 for ( const GeoSceneTextureTileDataset *layer: textureLayers ) {
327 const TileId tileId( layer->sourceDir(), stackedTileId.zoomLevel(),
328 stackedTileId.x(), stackedTileId.y() );
329 RenderStatus tileStatus = Complete;
330 switch ( TileLoader::tileStatus( layer, tileId ) ) {
331 case TileLoader::Available:
332 tileStatus = Complete;
333 break;
334 case TileLoader::Expired:
335 tileStatus = WaitingForUpdate;
336 break;
337 case TileLoader::Missing:
338 tileStatus = WaitingForData;
339 break;
340 }
341
342 state.addChild( RenderState( layer->name(), tileStatus ) );
343 }
344
345 return state;
346}
347
348bool MergedLayerDecorator::hasTextureLayer() const
349{
350 return !d->m_textureLayers.isEmpty();
351}
352
353StackedTile *MergedLayerDecorator::updateTile( const StackedTile &stackedTile, const TileId &tileId, const QImage &tileImage )
354{
355 Q_ASSERT( !tileImage.isNull() );
356
357 d->detectMaxTileLevel();
358
359 QVector<QSharedPointer<TextureTile> > tiles = stackedTile.tiles();
360
361 for ( int i = 0; i < tiles.count(); ++ i) {
362 if ( tiles[i]->id() == tileId ) {
363 const Blending *blending = tiles[i]->blending();
364
365 tiles[i] = QSharedPointer<TextureTile>( new TextureTile( tileId, tileImage, blending ) );
366 }
367 }
368
369 return d->createTile( tiles );
370}
371
372void MergedLayerDecorator::downloadStackedTile( const TileId &id, DownloadUsage usage )
373{
374 const QVector<const GeoSceneTextureTileDataset *> textureLayers = d->findRelevantTextureLayers( id );
375
376 for ( const GeoSceneTextureTileDataset *textureLayer: textureLayers ) {
377 if (textureLayer->tileLevels().isEmpty() || textureLayer->tileLevels().contains(id.zoomLevel())) {
378 if ( TileLoader::tileStatus( textureLayer, id ) != TileLoader::Available || usage == DownloadBrowse ) {
379 d->m_tileLoader->downloadTile( textureLayer, id, usage );
380 }
381 }
382 }
383}
384
385void MergedLayerDecorator::setShowSunShading( bool show )
386{
387 d->m_showSunShading = show;
388}
389
390bool MergedLayerDecorator::showSunShading() const
391{
392 return d->m_showSunShading;
393}
394
395void MergedLayerDecorator::setShowCityLights( bool show )
396{
397 d->m_showCityLights = show;
398}
399
400bool MergedLayerDecorator::showCityLights() const
401{
402 return d->m_showCityLights;
403}
404
405void MergedLayerDecorator::setShowTileId( bool visible )
406{
407 d->m_showTileId = visible;
408}
409
410void MergedLayerDecorator::Private::paintSunShading( QImage *tileImage, const TileId &id ) const
411{
412 if ( tileImage->depth() != 32 )
413 return;
414
415 // TODO add support for 8-bit maps?
416 // add sun shading
417 const qreal global_width = tileImage->width()
418 * TileLoaderHelper::levelToColumn( m_levelZeroColumns, id.zoomLevel() );
419 const qreal global_height = tileImage->height()
420 * TileLoaderHelper::levelToRow( m_levelZeroRows, id.zoomLevel() );
421 const qreal lon_scale = 2*M_PI / global_width;
422 const qreal lat_scale = -M_PI / global_height;
423 const int tileHeight = tileImage->height();
424 const int tileWidth = tileImage->width();
425
426 // First we determine the supporting point interval for the interpolation.
427 const int n = maxDivisor( 30, tileWidth );
428 const int ipRight = n * (int)( tileWidth / n );
429
430 for ( int cur_y = 0; cur_y < tileHeight; ++cur_y ) {
431 const qreal lat = lat_scale * ( id.y() * tileHeight + cur_y ) - 0.5*M_PI;
432 const qreal a = sin( (lat+DEG2RAD * m_sunLocator->getLat() )/2.0 );
433 const qreal c = cos(lat)*cos( -DEG2RAD * m_sunLocator->getLat() );
434
435 QRgb* scanline = (QRgb*)tileImage->scanLine( cur_y );
436
437 qreal lastShade = -10.0;
438
439 int cur_x = 0;
440
441 while ( cur_x < tileWidth ) {
442
443 const bool interpolate = ( cur_x != 0 && cur_x < ipRight && cur_x + n < tileWidth );
444
445 qreal shade = 0;
446
447 if ( interpolate ) {
448 const int check = cur_x + n;
449 const qreal checklon = lon_scale * ( id.x() * tileWidth + check );
450 shade = m_sunLocator->shading( checklon, a, c );
451
452 // if the shading didn't change across the interpolation
453 // interval move on and don't change anything.
454 if ( shade == lastShade && shade == 1.0 ) {
455 scanline += n;
456 cur_x += n;
457 continue;
458 }
459 if ( shade == lastShade && shade == 0.0 ) {
460 for ( int t = 0; t < n; ++t ) {
461 SunLocator::shadePixel(*scanline, shade);
462 ++scanline;
463 }
464 cur_x += n;
465 continue;
466 }
467 for ( int t = 0; t < n ; ++t ) {
468 const qreal lon = lon_scale * ( id.x() * tileWidth + cur_x );
469 shade = m_sunLocator->shading( lon, a, c );
470 SunLocator::shadePixel(*scanline, shade);
471 ++scanline;
472 ++cur_x;
473 }
474 }
475
476 else {
477 // Make sure we don't exceed the image memory
478 if ( cur_x < tileWidth ) {
479 const qreal lon = lon_scale * ( id.x() * tileWidth + cur_x );
480 shade = m_sunLocator->shading( lon, a, c );
481 SunLocator::shadePixel(*scanline, shade);
482 ++scanline;
483 ++cur_x;
484 }
485 }
486 lastShade = shade;
487 }
488 }
489}
490
491void MergedLayerDecorator::Private::paintTileId( QImage *tileImage, const TileId &id ) const
492{
493 QString filename = QString( "%1_%2.jpg" )
494 .arg(id.x(), tileDigits, 10, QLatin1Char('0'))
495 .arg(id.y(), tileDigits, 10, QLatin1Char('0'));
496
497 QPainter painter( tileImage );
498
499 QColor foreground;
500 QColor background;
501
502 if ( ( (qreal)(id.x())/2 == id.x()/2 && (qreal)(id.y())/2 == id.y()/2 )
503 || ( (qreal)(id.x())/2 != id.x()/2 && (qreal)(id.y())/2 != id.y()/2 )
504 )
505 {
506 foreground.setNamedColor( "#FFFFFF" );
507 background.setNamedColor( "#000000" );
508 }
509 else {
510 foreground.setNamedColor( "#000000" );
511 background.setNamedColor( "#FFFFFF" );
512 }
513
514 int strokeWidth = 10;
515 QPen testPen( foreground );
516 testPen.setWidth( strokeWidth );
517 testPen.setJoinStyle( Qt::MiterJoin );
518
519 painter.setPen( testPen );
520 painter.drawRect( strokeWidth / 2, strokeWidth / 2,
521 tileImage->width() - strokeWidth,
522 tileImage->height() - strokeWidth );
523 QFont testFont(QStringLiteral("Sans Serif"), 12);
524 QFontMetrics testFm( testFont );
525 painter.setFont( testFont );
526
527 QPen outlinepen( foreground );
528 outlinepen.setWidthF( 6 );
529
530 painter.setPen( outlinepen );
531 painter.setBrush( background );
532
533 QPainterPath outlinepath;
534
535 QPointF baseline1( ( tileImage->width() - testFm.boundingRect(filename).width() ) / 2,
536 ( tileImage->height() * 0.25) );
537 outlinepath.addText( baseline1, testFont, QString( "level: %1" ).arg(id.zoomLevel()) );
538
539 QPointF baseline2( ( tileImage->width() - testFm.boundingRect(filename).width() ) / 2,
540 tileImage->height() * 0.50 );
541 outlinepath.addText( baseline2, testFont, filename );
542
543 QPointF baseline3( ( tileImage->width() - testFm.boundingRect(filename).width() ) / 2,
544 tileImage->height() * 0.75 );
545 outlinepath.addText( baseline3, testFont, m_themeId );
546
547 painter.drawPath( outlinepath );
548
549 painter.setPen( Qt::NoPen );
550 painter.drawPath( outlinepath );
551}
552
553void MergedLayerDecorator::Private::detectMaxTileLevel()
554{
555 if ( m_textureLayers.isEmpty() ) {
556 m_maxTileLevel = -1;
557 return;
558 }
559
560 m_maxTileLevel = TileLoader::maximumTileLevel( *m_textureLayers.at( 0 ) );
561}
562
563QVector<const GeoSceneTextureTileDataset *> MergedLayerDecorator::Private::findRelevantTextureLayers( const TileId &stackedTileId ) const
564{
566
567 for ( const GeoSceneTextureTileDataset *candidate: m_textureLayers ) {
568 Q_ASSERT( candidate );
569 // check, if layer provides tiles for the current level
570 if ( !candidate->hasMaximumTileLevel() ||
571 candidate->maximumTileLevel() >= stackedTileId.zoomLevel() ) {
572 //check if the tile intersects with texture bounds
573 if (candidate->latLonBox().isNull()) {
574 result.append(candidate);
575 }
576 else {
577 const GeoDataLatLonBox bbox = candidate->tileProjection()->geoCoordinates(stackedTileId);
578
579 if (candidate->latLonBox().intersects(bbox)) {
580 result.append( candidate );
581 }
582 }
583 }
584 }
585
586 return result;
587}
588
589// TODO: This should likely go into a math class in the future ...
590
591int MergedLayerDecorator::Private::maxDivisor( int maximum, int fullLength )
592{
593 // Find the optimal interpolation interval n for the
594 // current image canvas width
595 int best = 2;
596
597 int nEvalMin = fullLength;
598 for ( int it = 1; it <= maximum; ++it ) {
599 // The optimum is the interval which results in the least amount
600 // supporting points taking into account the rest which can't
601 // get used for interpolation.
602 int nEval = fullLength / it + fullLength % it;
603 if ( nEval < nEvalMin ) {
604 nEvalMin = nEval;
605 best = it;
606 }
607 }
608 return best;
609}
A 3d point representation.
static qreal normalizeLon(qreal lon, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
bool isGloballyVisible() const
Return whether this feature is visible or not in the context of its parenting.
A class that defines a 2D bounding box for geographic data.
qreal north(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the northern boundary of the bounding box.
qreal height(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the height of the latitude interval.
GeoDataLatLonBox toCircumscribedRectangle() const
qreal width(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the width of the longitude interval.
qreal west(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the western boundary of the bounding box.
qreal south(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the southern boundary of the bounding box.
qreal rotation(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the rotation of the bounding box.
virtual GeoDataCoordinates center() const
returns the center of this box
A base class for projections between tile indizes and geo coordinates in Marble.
A single tile that consists of a stack of Tile layers.
Definition StackedTile.h:51
QVector< QSharedPointer< TextureTile > > tiles() const
Returns the stack of Tiles.
A class that resembles an image tile (extends Tile).
Definition TextureTile.h:46
KGUIADDONS_EXPORT QColor shade(const QColor &, qreal lumaAmount, qreal chromaAmount=0.0)
Binds a QML item to a specific geodetic location in screen coordinates.
qreal gdInv(qreal x)
This method is a fast Mac Laurin power series approximation of the.
Definition MarbleMath.h:73
DownloadUsage
This enum is used to describe the type of download.
@ DownloadBrowse
Browsing mode, normal operation of Marble, like a web browser.
@ WaitingForData
Rendering is based on no or partial data, more data was requested (e.g. pending network queries)
@ Complete
All data is there and up to date.
@ WaitingForUpdate
Rendering is based on complete, but outdated data, data update was requested.
void setNamedColor(QLatin1StringView name)
Format_ARGB32_Premultiplied
QImage convertToFormat(Format format, Qt::ImageConversionFlags flags) &&
QImage copy(const QRect &rectangle) const const
int depth() const const
int height() const const
bool isNull() const const
QRgb pixel(const QPoint &position) const const
uchar * scanLine(int i)
int width() const const
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
qsizetype count() const const
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
void addText(const QPointF &point, const QFont &font, const QString &text)
QString arg(Args &&... args) const const
MiterJoin
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.