Marble

MergedLayerDecorator.cpp
1 // SPDX-FileCopyrightText: 2008 David Roberts <[email protected]>
2 // SPDX-FileCopyrightText: 2009 Jens-Michael Hoffmann <[email protected]>
3 // SPDX-FileCopyrightText: 2011 Bernhard Beschow <[email protected]>
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 
30 using namespace Marble;
31 
32 class Q_DECL_HIDDEN MergedLayerDecorator::Private
33 {
34 public:
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;
52  QList<const GeoDataGroundOverlay *> m_groundOverlays;
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 
62 MergedLayerDecorator::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 
77 MergedLayerDecorator::MergedLayerDecorator( TileLoader * const tileLoader,
78  const SunLocator* sunLocator )
79  : d( new Private( tileLoader, sunLocator ) )
80 {
81 }
82 
83 MergedLayerDecorator::~MergedLayerDecorator()
84 {
85  delete d;
86 }
87 
88 void 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 
103 void MergedLayerDecorator::updateGroundOverlays(const QList<const GeoDataGroundOverlay *> &groundOverlays )
104 {
105  d->m_groundOverlays = groundOverlays;
106 }
107 
108 
109 int MergedLayerDecorator::textureLayersSize() const
110 {
111  return d->m_textureLayers.size();
112 }
113 
114 int MergedLayerDecorator::maximumTileLevel() const
115 {
116  return d->m_maxTileLevel;
117 }
118 
119 int 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 
128 int 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 
137 const GeoSceneAbstractTileProjection *MergedLayerDecorator::tileProjection() const
138 {
139  Q_ASSERT( !d->m_textureLayers.isEmpty() );
140 
141  return d->m_textureLayers.at(0)->tileProjection();
142 }
143 
144 QSize MergedLayerDecorator::tileSize() const
145 {
146  Q_ASSERT( !d->m_textureLayers.isEmpty() );
147 
148  return d->m_textureLayers.at( 0 )->tileSize();
149 }
150 
151 StackedTile *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() << Q_FUNC_INFO << "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() << Q_FUNC_INFO << "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 
203 void 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 
289 StackedTile *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() << Q_FUNC_INFO << 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() << Q_FUNC_INFO << "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 
319 RenderState 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 
348 bool MergedLayerDecorator::hasTextureLayer() const
349 {
350  return !d->m_textureLayers.isEmpty();
351 }
352 
353 StackedTile *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 
372 void 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 
385 void MergedLayerDecorator::setShowSunShading( bool show )
386 {
387  d->m_showSunShading = show;
388 }
389 
390 bool MergedLayerDecorator::showSunShading() const
391 {
392  return d->m_showSunShading;
393 }
394 
395 void MergedLayerDecorator::setShowCityLights( bool show )
396 {
397  d->m_showCityLights = show;
398 }
399 
400 bool MergedLayerDecorator::showCityLights() const
401 {
402  return d->m_showCityLights;
403 }
404 
405 void MergedLayerDecorator::setShowTileId( bool visible )
406 {
407  d->m_showTileId = visible;
408 }
409 
410 void 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 
491 void 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 
553 void 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 
563 QVector<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 
591 int 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.
qreal gdInv(qreal x)
This method is a fast Mac Laurin power series approximation of the.
Definition: MarbleMath.h:73
qreal height(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the height of the latitude interval.
bool isEmpty() const const
QVector< QSharedPointer< TextureTile > > tiles() const
Returns the stack of Tiles.
virtual GeoDataCoordinates center() const
returns the center of this box
A single tile that consists of a stack of Tile layers.
Definition: StackedTile.h:50
@ DownloadBrowse
Browsing mode, normal operation of Marble, like a web browser.
Definition: MarbleGlobal.h:155
Format_ARGB32_Premultiplied
int height() const const
MiterJoin
void setNamedColor(const QString &name)
int depth() const const
DownloadUsage
This enum is used to describe the type of download.
Definition: MarbleGlobal.h:153
void append(const T &value)
void addText(const QPointF &point, const QFont &font, const QString &text)
qreal north(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the northern boundary of the bounding box.
int size() const 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.
const T & at(int i) const const
QImage convertToFormat(QImage::Format format, Qt::ImageConversionFlags flags) const &const
bool isNull() const const
A class that defines a 2D bounding box for geographic data.
Binds a QML item to a specific geodetic location in screen coordinates.
@ WaitingForUpdate
Rendering is based on complete, but outdated data, data update was requested.
Definition: MarbleGlobal.h:182
void reserve(int size)
uchar * scanLine(int i)
@ Complete
All data is there and up to date.
Definition: MarbleGlobal.h:181
qreal south(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the southern boundary of the bounding box.
GeoDataLatLonBox toCircumscribedRectangle() const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
@ WaitingForData
Rendering is based on no or partial data, more data was requested (e.g. pending network queries)
Definition: MarbleGlobal.h:183
int count(const T &value) const const
int size() const const
A class that resembles an image tile (extends Tile).
Definition: TextureTile.h:45
KGUIADDONS_EXPORT QColor shade(const QColor &, qreal lumaAmount, qreal chromaAmount=0.0)
static qreal normalizeLon(qreal lon, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
QImage copy(const QRect &rectangle) const const
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
A base class for projections between tile indizes and geo coordinates in Marble.
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:27 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.