Marble

DownloadRegion.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2012 Dennis Nienhüser <[email protected]>
4 //
5 
6 #include "DownloadRegion.h"
7 
8 #include "MarbleModel.h"
9 #include "MarbleMap.h"
10 #include "MarbleMath.h"
11 #include "MarbleDebug.h"
12 #include "TextureLayer.h"
13 #include "GeoDataLatLonAltBox.h"
14 #include "GeoDataLineString.h"
15 #include "GeoSceneDocument.h"
16 #include "GeoSceneMap.h"
17 #include "GeoSceneLayer.h"
18 #include "GeoSceneTileDataset.h"
19 #include "GeoSceneAbstractTileProjection.h"
20 #include "TileCoordsPyramid.h"
21 
22 namespace Marble {
23 
24 class DownloadRegionPrivate
25 {
26 public:
27  MarbleModel* m_marbleModel;
28 
29  QPair<int,int> m_tileLevelRange;
30 
31  int m_visibleTileLevel;
32 
33  DownloadRegionPrivate();
34 
35  int rad2PixelX( qreal const lon, const TileLayer *tileLayer ) const;
36 
37  int rad2PixelY( qreal const lat, const TileLayer *tileLayer ) const;
38 };
39 
40 DownloadRegionPrivate::DownloadRegionPrivate() : m_marbleModel( nullptr ),
41  m_tileLevelRange( 0, 0 ), m_visibleTileLevel( 0 )
42 {
43  // nothing to do
44 }
45 
46 // copied from AbstractScanlineTextureMapper and slightly adjusted
47 int DownloadRegionPrivate::rad2PixelX( qreal const lon, const TileLayer *tileLayer ) const
48 {
49  qreal tileWidth = tileLayer && tileLayer->layerCount() > 0 ? tileLayer->tileSize().width() : 256;
50  qreal const globalWidth = tileWidth * tileLayer->tileColumnCount( m_visibleTileLevel );
51  return static_cast<int>(globalWidth * 0.5 * (1 + lon / M_PI));
52 }
53 
54 // copied from AbstractScanlineTextureMapper and slightly adjusted
55 int DownloadRegionPrivate::rad2PixelY( qreal const lat, const TileLayer *tileLayer ) const
56 {
57  qreal tileHeight = tileLayer && tileLayer->layerCount() > 0 ? tileLayer->tileSize().height() : 256;
58  qreal const globalHeight = tileHeight * tileLayer->tileRowCount( m_visibleTileLevel );
59 
60  switch (tileLayer->tileProjection()->type()) {
61  case GeoSceneAbstractTileProjection::Equirectangular:
62  return static_cast<int>(globalHeight * (0.5 - lat / M_PI));
63  case GeoSceneAbstractTileProjection::Mercator:
64  if ( fabs( lat ) < 1.4835 )
65  return static_cast<int>(globalHeight * 0.5 * (1 - gdInv(lat) / M_PI));
66  if ( lat >= +1.4835 )
67  return static_cast<int>(globalHeight * 0.5 * (1 - 3.1309587 / M_PI));
68  if ( lat <= -1.4835 )
69  return static_cast<int>(globalHeight * 0.5 * (1 + 3.1309587 / M_PI));
70  }
71 
72  // Dummy value to avoid a warning.
73  return 0;
74 }
75 
76 DownloadRegion::DownloadRegion( QObject* parent ) : QObject( parent ),
77  d( new DownloadRegionPrivate )
78 {
79  // nothing to do
80 }
81 
82 void DownloadRegion::setMarbleModel( MarbleModel* model )
83 {
84  d->m_marbleModel = model;
85 }
86 
87 DownloadRegion::~DownloadRegion()
88 {
89  delete d;
90 }
91 
92 void DownloadRegion::setTileLevelRange( const int minimumTileLevel, const int maximumTileLevel )
93 {
94  Q_ASSERT( minimumTileLevel >= 0 );
95  Q_ASSERT( maximumTileLevel >= 0 );
96  Q_ASSERT( minimumTileLevel <= maximumTileLevel );
97  d->m_tileLevelRange.first = minimumTileLevel;
98  d->m_tileLevelRange.second = maximumTileLevel;
99 }
100 
101 QVector<TileCoordsPyramid> DownloadRegion::region( const TileLayer *tileLayer, const GeoDataLatLonAltBox &downloadRegion ) const
102 {
103  Q_ASSERT( tileLayer );
104 
105  int tileLevelRangeFirst = d->m_tileLevelRange.first;
106  int tileLevelRangeSecond = d->m_tileLevelRange.second;
107 
108  TileType tileType = dynamic_cast<const TextureLayer*>( tileLayer ) ? TextureTileType : VectorTileType;
109 
110  QVector<int> validLevels;
111  validLevels = validTileLevels(tileType);
112 
113  // Align the tileLevelRangeSecond with the validTileLevels
114  if (!validLevels.isEmpty()) {
115  int lastIndex = validLevels.count() - 1;
116  for ( int i = 0; i < validLevels.count(); ++i ) {
117  if (validLevels.at(lastIndex - i) <= tileLevelRangeSecond
118  && validLevels.at(lastIndex - i) >= tileLevelRangeFirst) {
119  tileLevelRangeSecond = validLevels.at(lastIndex - i);
120  break;
121  }
122  }
123  }
124 
125  int const westX = d->rad2PixelX( downloadRegion.west(), tileLayer );
126  int const northY = d->rad2PixelY( downloadRegion.north(), tileLayer );
127  int const eastX = d->rad2PixelX( downloadRegion.east(), tileLayer );
128  int const southY = d->rad2PixelY( downloadRegion.south(), tileLayer );
129 
130  // FIXME: remove this stuff
131  mDebug() << "DownloadRegionDialog downloadRegion:"
132  << "north:" << downloadRegion.north()
133  << "south:" << downloadRegion.south()
134  << "east:" << downloadRegion.east()
135  << "west:" << downloadRegion.west();
136  mDebug() << "north/west (x/y):" << westX << northY;
137  mDebug() << "south/east (x/y):" << eastX << southY;
138 
139  int const tileWidth = tileLayer->tileSize().width();
140  int const tileHeight = tileLayer->tileSize().height();
141  mDebug() << "DownloadRegionDialog downloadRegion: tileSize:" << tileWidth << tileHeight;
142 
143  int const visibleLevelX1 = qMin( westX, eastX );
144  int const visibleLevelY1 = qMin( northY, southY );
145  int const visibleLevelX2 = qMax( westX, eastX );
146  int const visibleLevelY2 = qMax( northY, southY );
147 
148  mDebug() << "visible level pixel coords (level/x1/y1/x2/y2):" << d->m_visibleTileLevel
149  << visibleLevelX1 << visibleLevelY1 << visibleLevelX2 << visibleLevelY2;
150 
151  int bottomLevelX1, bottomLevelY1, bottomLevelX2, bottomLevelY2;
152  // the pixel coords calculated above are referring to the visible tile level,
153  // if the bottom level is a different level, we have to take it into account
154  if ( d->m_visibleTileLevel > tileLevelRangeSecond ) {
155  int const deltaLevel = d->m_visibleTileLevel - tileLevelRangeSecond;
156  bottomLevelX1 = visibleLevelX1 >> deltaLevel;
157  bottomLevelY1 = visibleLevelY1 >> deltaLevel;
158  bottomLevelX2 = visibleLevelX2 >> deltaLevel;
159  bottomLevelY2 = visibleLevelY2 >> deltaLevel;
160  }
161  else if ( d->m_visibleTileLevel < tileLevelRangeSecond ) {
162  int const deltaLevel = tileLevelRangeSecond - d->m_visibleTileLevel;
163  bottomLevelX1 = visibleLevelX1 << deltaLevel;
164  bottomLevelY1 = visibleLevelY1 << deltaLevel;
165  bottomLevelX2 = visibleLevelX2 << deltaLevel;
166  bottomLevelY2 = visibleLevelY2 << deltaLevel;
167  }
168  else {
169  bottomLevelX1 = visibleLevelX1;
170  bottomLevelY1 = visibleLevelY1;
171  bottomLevelX2 = visibleLevelX2;
172  bottomLevelY2 = visibleLevelY2;
173  }
174  mDebug() << "bottom level pixel coords (level/x1/y1/x2/y2):"
175  << tileLevelRangeSecond
176  << bottomLevelX1 << bottomLevelY1 << bottomLevelX2 << bottomLevelY2;
177 
178  TileCoordsPyramid coordsPyramid( tileLevelRangeFirst, tileLevelRangeSecond );
179  coordsPyramid.setValidTileLevels(validLevels);
180 
181  QRect bottomLevelTileCoords;
182  bottomLevelTileCoords.setCoords
183  ( bottomLevelX1 / tileWidth,
184  bottomLevelY1 / tileHeight,
185  bottomLevelX2 / tileWidth + ( bottomLevelX2 % tileWidth > 0 ? 1 : 0 ) - 1, // -1 needed for proper counting
186  bottomLevelY2 / tileHeight + ( bottomLevelY2 % tileHeight > 0 ? 1 : 0 ) - 1); // -1 needed for proper counting
187  mDebug() << "bottom level tile coords: (x1/y1/size):" << bottomLevelTileCoords;
188  coordsPyramid.setBottomLevelCoords( bottomLevelTileCoords );
189 
190  mDebug() << "tiles count:" << coordsPyramid.tilesCount( );
192  pyramid << coordsPyramid;
193  return pyramid;
194 }
195 
196 void DownloadRegion::setVisibleTileLevel(const int tileLevel)
197 {
198  d->m_visibleTileLevel = tileLevel;
199 }
200 
201 QVector<TileCoordsPyramid> DownloadRegion::fromPath( const TileLayer *tileLayer, qreal offset, const GeoDataLineString &waypoints ) const
202 {
203  if ( !d->m_marbleModel ) {
205  }
206 
207  int tileLevelRangeFirst = d->m_tileLevelRange.first;
208  int tileLevelRangeSecond = d->m_tileLevelRange.second;
209 
210  TileType tileType = dynamic_cast<const TextureLayer*>( tileLayer ) ? TextureTileType : VectorTileType;
211 
212  QVector<int> validLevels;
213  validLevels = validTileLevels(tileType);
214 
215  // Align the tileLevelRangeSecond with the validTileLevels
216  if (!validLevels.isEmpty()) {
217  int lastIndex = validLevels.count() - 1;
218  for ( int i = 0; i < validLevels.count(); ++i ) {
219  if (validLevels.at(lastIndex - i) <= tileLevelRangeSecond
220  && validLevels.at(lastIndex - i) >= tileLevelRangeFirst) {
221  tileLevelRangeSecond = validLevels.at(lastIndex - i);
222  break;
223  }
224  }
225  }
226 
227  TileCoordsPyramid coordsPyramid( tileLevelRangeFirst, tileLevelRangeSecond );
228  coordsPyramid.setValidTileLevels(validLevels);
229 
230  int const tileWidth = tileLayer->tileSize().width();
231  int const tileHeight = tileLayer->tileSize().height();
232 
233  qreal radius = d->m_marbleModel->planetRadius();
235  qreal radianOffset = offset / radius;
236 
237  for( int i = 1; i < waypoints.size(); ++i ) {
238  GeoDataCoordinates position = waypoints[i];
239  qreal lonCenter = position.longitude();
240  qreal latCenter = position.latitude();
241 
242  // coordinates of the of the vertices of the square(topleft and bottomright) at an offset distance from the waypoint
243  qreal latNorth = asin( sin( latCenter ) * cos( radianOffset ) + cos( latCenter ) * sin( radianOffset ) * cos( 7*M_PI/4 ) );
244  qreal dlonWest = atan2( sin( 7*M_PI/4 ) * sin( radianOffset ) * cos( latCenter ), cos( radianOffset ) - sin( latCenter ) * sin( latNorth ) );
245  qreal lonWest = fmod( lonCenter - dlonWest + M_PI, 2*M_PI ) - M_PI;
246  qreal latSouth = asin( sin( latCenter ) * cos( radianOffset ) + cos( latCenter ) * sin( radianOffset ) * cos( 3*M_PI/4 ) );
247  qreal dlonEast = atan2( sin( 3*M_PI/4 ) * sin( radianOffset ) * cos( latCenter ), cos( radianOffset ) - sin( latCenter ) * sin( latSouth ) );
248  qreal lonEast = fmod( lonCenter - dlonEast+M_PI, 2*M_PI ) - M_PI;
249 
250  int const northY = d->rad2PixelY( latNorth, tileLayer );
251  int const southY = d->rad2PixelY( latSouth, tileLayer );
252  int const eastX = d->rad2PixelX( lonEast, tileLayer );
253  int const westX = d->rad2PixelX( lonWest, tileLayer );
254 
255  int const west = qMin( westX, eastX );
256  int const north = qMin( northY, southY );
257  int const east = qMax( westX, eastX );
258  int const south = qMax( northY, southY );
259 
260  int bottomLevelTileX1 = 0;
261  int bottomLevelTileY1 = 0;
262  int bottomLevelTileX2 = 0;
263  int bottomLevelTileY2 = 0;
264 
265  if ( d->m_visibleTileLevel > tileLevelRangeSecond ) {
266  int const deltaLevel = d->m_visibleTileLevel - tileLevelRangeSecond;
267  bottomLevelTileX1 = west >> deltaLevel;
268  bottomLevelTileY1 = north >> deltaLevel;
269  bottomLevelTileX2 = east >> deltaLevel;
270  bottomLevelTileY2 = south >> deltaLevel;
271  }
272  else if ( d->m_visibleTileLevel < tileLevelRangeSecond ) {
273  int const deltaLevel = tileLevelRangeSecond - d->m_visibleTileLevel;
274  bottomLevelTileX1 = west << deltaLevel;
275  bottomLevelTileY1 = north << deltaLevel;
276  bottomLevelTileX2 = east << deltaLevel;
277  bottomLevelTileY2 = south << deltaLevel;
278  }
279  else {
280  bottomLevelTileX1 = west;
281  bottomLevelTileY1 = north;
282  bottomLevelTileX2 = east;
283  bottomLevelTileY2 = south;
284  }
285 
286  QRect waypointRegion;
287  //square region around the waypoint
288  waypointRegion.setCoords( bottomLevelTileX1/tileWidth, bottomLevelTileY1/tileHeight,
289  bottomLevelTileX2/tileWidth, bottomLevelTileY2/tileHeight );
290  coordsPyramid.setBottomLevelCoords( waypointRegion );
291  pyramid << coordsPyramid;
292  }
293 
294  return pyramid;
295 }
296 
297 QVector<int> DownloadRegion::validTileLevels( const TileType tileType ) const
298 {
299  QVector<int> validTileLevels;
300 
301  GeoSceneMap * map = d->m_marbleModel->mapTheme()->map();
302  QVector<GeoSceneLayer*> layers = map->layers();
303  for (auto layer : layers) {
304  if ((layer->backend() == "vectortile" && tileType == VectorTileType )
305  || (layer->backend() == "texture" && tileType == TextureTileType ) ) {
306  GeoSceneTileDataset * dataset = dynamic_cast<GeoSceneTileDataset*>(layer->datasets().first());
307  validTileLevels = dataset->tileLevels();
308  break;
309  }
310  }
311 
312  return validTileLevels;
313 }
314 
315 }
316 
317 #include "moc_DownloadRegion.cpp"
qreal gdInv(qreal x)
This method is a fast Mac Laurin power series approximation of the.
Definition: MarbleMath.h:73
bool isEmpty() const const
void setCoords(int x1, int y1, int x2, int y2)
const T & at(int i) const const
Binds a QML item to a specific geodetic location in screen coordinates.
@ TextureTileType
Tiles that consist of bitmap data.
Definition: MarbleGlobal.h:146
int count(const T &value) const const
QFuture< void > map(Sequence &sequence, MapFunctor function)
@ VectorTileType
Tiles that consist of vector data.
Definition: MarbleGlobal.h:147
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Sep 25 2023 03:50:18 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.