Marble

DownloadRegion.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2012 Dennis Nienhüser <nienhueser@kde.org>
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
22namespace Marble {
23
24class DownloadRegionPrivate
25{
26public:
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
40DownloadRegionPrivate::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
47int 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
55int 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
76DownloadRegion::DownloadRegion( QObject* parent ) : QObject( parent ),
77 d( new DownloadRegionPrivate )
78{
79 // nothing to do
80}
81
82void DownloadRegion::setMarbleModel( MarbleModel* model )
83{
84 d->m_marbleModel = model;
85}
86
87DownloadRegion::~DownloadRegion()
88{
89 delete d;
90}
91
92void 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
101QVector<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
196void DownloadRegion::setVisibleTileLevel(const int tileLevel)
197{
198 d->m_visibleTileLevel = tileLevel;
199}
200
201QVector<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
297QVector<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"
This file contains the headers for MarbleMap.
This file contains the headers for MarbleModel.
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
@ VectorTileType
Tiles that consist of vector data.
const_reference at(qsizetype i) const const
qsizetype count() const const
bool isEmpty() const const
void setCoords(int x1, int y1, int x2, int y2)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
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.