File: | /home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp |
Warning: | line 281, column 27 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* ============================================================ | |||
2 | * | |||
3 | * This file is a part of digiKam project | |||
4 | * https://www.digikam.org | |||
5 | * | |||
6 | * Date : 2023-05-15 | |||
7 | * Description : geolocation engine based on Marble. | |||
8 | * (c) 2007-2022 Marble Team | |||
9 | * https://invent.kde.org/education/marble/-/raw/master/data/credits_authors.html | |||
10 | * | |||
11 | * SPDX-FileCopyrightText: 2023-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> | |||
12 | * | |||
13 | * SPDX-License-Identifier: LGPL-2.1-or-later | |||
14 | * | |||
15 | * ============================================================ */ | |||
16 | ||||
17 | #include "DownloadRegion.h" | |||
18 | ||||
19 | // Local includes | |||
20 | ||||
21 | #include "MarbleModel.h" | |||
22 | #include "MarbleMap.h" | |||
23 | #include "MarbleMath.h" | |||
24 | #include "TextureLayer.h" | |||
25 | #include "GeoDataLatLonAltBox.h" | |||
26 | #include "GeoDataLineString.h" | |||
27 | #include "GeoSceneDocument.h" | |||
28 | #include "GeoSceneMap.h" | |||
29 | #include "GeoSceneLayer.h" | |||
30 | #include "GeoSceneTileDataset.h" | |||
31 | #include "GeoSceneAbstractTileProjection.h" | |||
32 | #include "TileCoordsPyramid.h" | |||
33 | #include "digikam_debug.h" | |||
34 | ||||
35 | namespace Marble | |||
36 | { | |||
37 | ||||
38 | class Q_DECL_HIDDEN__attribute__((visibility("hidden"))) DownloadRegionPrivate | |||
39 | { | |||
40 | public: | |||
41 | ||||
42 | MarbleModel* m_marbleModel = nullptr; | |||
43 | ||||
44 | QPair<int, int> m_tileLevelRange; | |||
45 | ||||
46 | int m_visibleTileLevel; | |||
47 | ||||
48 | public: | |||
49 | ||||
50 | DownloadRegionPrivate(); | |||
51 | ||||
52 | int rad2PixelX(qreal const lon, const TileLayer* tileLayer) const; | |||
53 | ||||
54 | int rad2PixelY(qreal const lat, const TileLayer* tileLayer) const; | |||
55 | }; | |||
56 | ||||
57 | DownloadRegionPrivate::DownloadRegionPrivate() : m_marbleModel(nullptr), | |||
58 | m_tileLevelRange(0, 0), m_visibleTileLevel(0) | |||
59 | { | |||
60 | // nothing to do | |||
61 | } | |||
62 | ||||
63 | // copied from AbstractScanlineTextureMapper and slightly adjusted | |||
64 | int DownloadRegionPrivate::rad2PixelX(qreal const lon, const TileLayer* tileLayer) const | |||
65 | { | |||
66 | qreal tileWidth = tileLayer && tileLayer->layerCount() > 0 ? tileLayer->tileSize().width() : 256; | |||
67 | qreal const globalWidth = tileWidth * tileLayer->tileColumnCount(m_visibleTileLevel); | |||
68 | return static_cast<int>(globalWidth * 0.5 * (1 + lon / M_PI3.14159265358979323846)); | |||
69 | } | |||
70 | ||||
71 | // copied from AbstractScanlineTextureMapper and slightly adjusted | |||
72 | int DownloadRegionPrivate::rad2PixelY(qreal const lat, const TileLayer* tileLayer) const | |||
73 | { | |||
74 | if (tileLayer) | |||
75 | { | |||
76 | qreal tileHeight = (tileLayer->layerCount() > 0) ? tileLayer->tileSize().height() | |||
77 | : 256; | |||
78 | qreal const globalHeight = tileHeight * tileLayer->tileRowCount(m_visibleTileLevel); | |||
79 | ||||
80 | switch (tileLayer->tileProjection()->type()) | |||
81 | { | |||
82 | case GeoSceneAbstractTileProjection::Equirectangular: | |||
83 | { | |||
84 | return static_cast<int>(globalHeight * (0.5 - lat / M_PI3.14159265358979323846)); | |||
85 | } | |||
86 | ||||
87 | case GeoSceneAbstractTileProjection::Mercator: | |||
88 | { | |||
89 | if (fabs(lat) < 1.4835) | |||
90 | { | |||
91 | return static_cast<int>(globalHeight * 0.5 * (1 - gdInv(lat) / M_PI3.14159265358979323846)); | |||
92 | } | |||
93 | ||||
94 | if (lat >= +1.4835) | |||
95 | { | |||
96 | return static_cast<int>(globalHeight * 0.5 * (1 - 3.1309587 / M_PI3.14159265358979323846)); | |||
97 | } | |||
98 | ||||
99 | if (lat <= -1.4835) | |||
100 | { | |||
101 | return static_cast<int>(globalHeight * 0.5 * (1 + 3.1309587 / M_PI3.14159265358979323846)); | |||
102 | } | |||
103 | } | |||
104 | } | |||
105 | } | |||
106 | ||||
107 | // Dummy value to avoid a warning. | |||
108 | return 0; | |||
109 | } | |||
110 | ||||
111 | DownloadRegion::DownloadRegion(QObject* parent) : QObject(parent), | |||
112 | d(new DownloadRegionPrivate) | |||
113 | { | |||
114 | // nothing to do | |||
115 | } | |||
116 | ||||
117 | void DownloadRegion::setMarbleModel(MarbleModel* model) | |||
118 | { | |||
119 | d->m_marbleModel = model; | |||
120 | } | |||
121 | ||||
122 | DownloadRegion::~DownloadRegion() | |||
123 | { | |||
124 | delete d; | |||
125 | } | |||
126 | ||||
127 | void DownloadRegion::setTileLevelRange(const int minimumTileLevel, const int maximumTileLevel) | |||
128 | { | |||
129 | Q_ASSERT(minimumTileLevel >= 0)((minimumTileLevel >= 0) ? static_cast<void>(0) : qt_assert ("minimumTileLevel >= 0", "/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" , 129)); | |||
130 | Q_ASSERT(maximumTileLevel >= 0)((maximumTileLevel >= 0) ? static_cast<void>(0) : qt_assert ("maximumTileLevel >= 0", "/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" , 130)); | |||
131 | Q_ASSERT(minimumTileLevel <= maximumTileLevel)((minimumTileLevel <= maximumTileLevel) ? static_cast<void >(0) : qt_assert("minimumTileLevel <= maximumTileLevel" , "/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" , 131)); | |||
132 | d->m_tileLevelRange.first = minimumTileLevel; | |||
133 | d->m_tileLevelRange.second = maximumTileLevel; | |||
134 | } | |||
135 | ||||
136 | QVector<TileCoordsPyramid> DownloadRegion::region(const TileLayer* tileLayer, const GeoDataLatLonAltBox& downloadRegion) const | |||
137 | { | |||
138 | Q_ASSERT(tileLayer)((tileLayer) ? static_cast<void>(0) : qt_assert("tileLayer" , "/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" , 138)); | |||
139 | ||||
140 | int tileLevelRangeFirst = d->m_tileLevelRange.first; | |||
141 | int tileLevelRangeSecond = d->m_tileLevelRange.second; | |||
142 | ||||
143 | TileType tileType = dynamic_cast<const TextureLayer*>(tileLayer) ? TextureTileType : VectorTileType; | |||
144 | ||||
145 | QVector<int> validLevels; | |||
146 | validLevels = validTileLevels(tileType); | |||
147 | ||||
148 | // Align the tileLevelRangeSecond with the validTileLevels | |||
149 | if (!validLevels.isEmpty()) | |||
150 | { | |||
151 | int lastIndex = validLevels.count() - 1; | |||
152 | ||||
153 | for (int i = 0; i < validLevels.count(); ++i) | |||
154 | { | |||
155 | if (validLevels.at(lastIndex - i) <= tileLevelRangeSecond | |||
156 | && validLevels.at(lastIndex - i) >= tileLevelRangeFirst) | |||
157 | { | |||
158 | tileLevelRangeSecond = validLevels.at(lastIndex - i); | |||
159 | break; | |||
160 | } | |||
161 | } | |||
162 | } | |||
163 | ||||
164 | int const westX = d->rad2PixelX(downloadRegion.west(), tileLayer); | |||
165 | int const northY = d->rad2PixelY(downloadRegion.north(), tileLayer); | |||
166 | int const eastX = d->rad2PixelX(downloadRegion.east(), tileLayer); | |||
167 | int const southY = d->rad2PixelY(downloadRegion.south(), tileLayer); | |||
168 | ||||
169 | // FIXME: remove this stuff | |||
170 | qCDebug(DIGIKAM_MARBLE_LOG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_MARBLE_LOG)()); qt_category; qt_category.control = false ) QMessageLogger(static_cast<const char *>("/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" ), 170, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "DownloadRegionDialog downloadRegion:" | |||
171 | << "north:" << downloadRegion.north() | |||
172 | << "south:" << downloadRegion.south() | |||
173 | << "east:" << downloadRegion.east() | |||
174 | << "west:" << downloadRegion.west(); | |||
175 | qCDebug(DIGIKAM_MARBLE_LOG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_MARBLE_LOG)()); qt_category; qt_category.control = false ) QMessageLogger(static_cast<const char *>("/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" ), 175, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "north/west (x/y):" << westX << northY; | |||
176 | qCDebug(DIGIKAM_MARBLE_LOG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_MARBLE_LOG)()); qt_category; qt_category.control = false ) QMessageLogger(static_cast<const char *>("/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" ), 176, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "south/east (x/y):" << eastX << southY; | |||
177 | ||||
178 | int const tileWidth = tileLayer->tileSize().width(); | |||
179 | int const tileHeight = tileLayer->tileSize().height(); | |||
180 | qCDebug(DIGIKAM_MARBLE_LOG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_MARBLE_LOG)()); qt_category; qt_category.control = false ) QMessageLogger(static_cast<const char *>("/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" ), 180, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "DownloadRegionDialog downloadRegion: tileSize:" << tileWidth << tileHeight; | |||
181 | ||||
182 | int const visibleLevelX1 = qMin(westX, eastX); | |||
183 | int const visibleLevelY1 = qMin(northY, southY); | |||
184 | int const visibleLevelX2 = qMax(westX, eastX); | |||
185 | int const visibleLevelY2 = qMax(northY, southY); | |||
186 | ||||
187 | qCDebug(DIGIKAM_MARBLE_LOG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_MARBLE_LOG)()); qt_category; qt_category.control = false ) QMessageLogger(static_cast<const char *>("/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" ), 187, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "visible level pixel coords (level/x1/y1/x2/y2):" << d->m_visibleTileLevel | |||
188 | << visibleLevelX1 << visibleLevelY1 << visibleLevelX2 << visibleLevelY2; | |||
189 | ||||
190 | int bottomLevelX1, bottomLevelY1, bottomLevelX2, bottomLevelY2; | |||
191 | ||||
192 | // the pixel coords calculated above are referring to the visible tile level, | |||
193 | // if the bottom level is a different level, we have to take it into account | |||
194 | if (d->m_visibleTileLevel > tileLevelRangeSecond) | |||
195 | { | |||
196 | int const deltaLevel = d->m_visibleTileLevel - tileLevelRangeSecond; | |||
197 | bottomLevelX1 = visibleLevelX1 >> deltaLevel; | |||
198 | bottomLevelY1 = visibleLevelY1 >> deltaLevel; | |||
199 | bottomLevelX2 = visibleLevelX2 >> deltaLevel; | |||
200 | bottomLevelY2 = visibleLevelY2 >> deltaLevel; | |||
201 | } | |||
202 | ||||
203 | else if (d->m_visibleTileLevel < tileLevelRangeSecond) | |||
204 | { | |||
205 | int const deltaLevel = tileLevelRangeSecond - d->m_visibleTileLevel; | |||
206 | bottomLevelX1 = visibleLevelX1 << deltaLevel; | |||
207 | bottomLevelY1 = visibleLevelY1 << deltaLevel; | |||
208 | bottomLevelX2 = visibleLevelX2 << deltaLevel; | |||
209 | bottomLevelY2 = visibleLevelY2 << deltaLevel; | |||
210 | } | |||
211 | ||||
212 | else | |||
213 | { | |||
214 | bottomLevelX1 = visibleLevelX1; | |||
215 | bottomLevelY1 = visibleLevelY1; | |||
216 | bottomLevelX2 = visibleLevelX2; | |||
217 | bottomLevelY2 = visibleLevelY2; | |||
218 | } | |||
219 | ||||
220 | qCDebug(DIGIKAM_MARBLE_LOG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_MARBLE_LOG)()); qt_category; qt_category.control = false ) QMessageLogger(static_cast<const char *>("/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" ), 220, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "bottom level pixel coords (level/x1/y1/x2/y2):" | |||
221 | << tileLevelRangeSecond | |||
222 | << bottomLevelX1 << bottomLevelY1 << bottomLevelX2 << bottomLevelY2; | |||
223 | ||||
224 | TileCoordsPyramid coordsPyramid(tileLevelRangeFirst, tileLevelRangeSecond); | |||
225 | coordsPyramid.setValidTileLevels(validLevels); | |||
226 | ||||
227 | QRect bottomLevelTileCoords; | |||
228 | bottomLevelTileCoords.setCoords | |||
229 | (bottomLevelX1 / tileWidth, | |||
230 | bottomLevelY1 / tileHeight, | |||
231 | bottomLevelX2 / tileWidth + (bottomLevelX2 % tileWidth > 0 ? 1 : 0) - 1, // -1 needed for proper counting | |||
232 | bottomLevelY2 / tileHeight + (bottomLevelY2 % tileHeight > 0 ? 1 : 0) - 1); // -1 needed for proper counting | |||
233 | qCDebug(DIGIKAM_MARBLE_LOG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_MARBLE_LOG)()); qt_category; qt_category.control = false ) QMessageLogger(static_cast<const char *>("/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" ), 233, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "bottom level tile coords: (x1/y1/size):" << bottomLevelTileCoords; | |||
234 | coordsPyramid.setBottomLevelCoords(bottomLevelTileCoords); | |||
235 | ||||
236 | qCDebug(DIGIKAM_MARBLE_LOG)for (QLoggingCategoryMacroHolder<QtDebugMsg> qt_category ((DIGIKAM_MARBLE_LOG)()); qt_category; qt_category.control = false ) QMessageLogger(static_cast<const char *>("/home/gilles/devel/GIT/8.x/core/utilities/geolocation/engine/storage/DownloadRegion.cpp" ), 236, static_cast<const char *>(__PRETTY_FUNCTION__), qt_category.name()).debug() << "tiles count:" << coordsPyramid.tilesCount(); | |||
237 | QVector<TileCoordsPyramid> pyramid; | |||
238 | pyramid << coordsPyramid; | |||
239 | return pyramid; | |||
240 | } | |||
241 | ||||
242 | void DownloadRegion::setVisibleTileLevel(const int tileLevel) | |||
243 | { | |||
244 | d->m_visibleTileLevel = tileLevel; | |||
245 | } | |||
246 | ||||
247 | QVector<TileCoordsPyramid> DownloadRegion::fromPath(const TileLayer* tileLayer, qreal offset, const GeoDataLineString& waypoints) const | |||
248 | { | |||
249 | if (!d->m_marbleModel) | |||
| ||||
250 | { | |||
251 | return QVector<TileCoordsPyramid>(); | |||
252 | } | |||
253 | ||||
254 | int tileLevelRangeFirst = d->m_tileLevelRange.first; | |||
255 | int tileLevelRangeSecond = d->m_tileLevelRange.second; | |||
256 | ||||
257 | TileType tileType = dynamic_cast<const TextureLayer*>(tileLayer) ? TextureTileType : VectorTileType; | |||
258 | ||||
259 | QVector<int> validLevels; | |||
260 | validLevels = validTileLevels(tileType); | |||
261 | ||||
262 | // Align the tileLevelRangeSecond with the validTileLevels | |||
263 | if (!validLevels.isEmpty()) | |||
264 | { | |||
265 | int lastIndex = validLevels.count() - 1; | |||
266 | ||||
267 | for (int i = 0; i < validLevels.count(); ++i) | |||
268 | { | |||
269 | if (validLevels.at(lastIndex - i) <= tileLevelRangeSecond | |||
270 | && validLevels.at(lastIndex - i) >= tileLevelRangeFirst) | |||
271 | { | |||
272 | tileLevelRangeSecond = validLevels.at(lastIndex - i); | |||
273 | break; | |||
274 | } | |||
275 | } | |||
276 | } | |||
277 | ||||
278 | TileCoordsPyramid coordsPyramid(tileLevelRangeFirst, tileLevelRangeSecond); | |||
279 | coordsPyramid.setValidTileLevels(validLevels); | |||
280 | ||||
281 | int const tileWidth = tileLayer->tileSize().width(); | |||
| ||||
282 | int const tileHeight = tileLayer->tileSize().height(); | |||
283 | ||||
284 | qreal radius = d->m_marbleModel->planetRadius(); | |||
285 | QVector<TileCoordsPyramid> pyramid; | |||
286 | qreal radianOffset = offset / radius; | |||
287 | ||||
288 | for (int i = 1; i < waypoints.size(); ++i) | |||
289 | { | |||
290 | GeoDataCoordinates position = waypoints[i]; | |||
291 | qreal lonCenter = position.longitude(); | |||
292 | qreal latCenter = position.latitude(); | |||
293 | ||||
294 | // coordinates of the of the vertices of the square(topleft and bottomright) at an offset distance from the waypoint | |||
295 | qreal latNorth = asin(sin(latCenter) * cos(radianOffset) + cos(latCenter) * sin(radianOffset) * cos(7 * M_PI3.14159265358979323846 / 4)); | |||
296 | qreal dlonWest = atan2(sin(7 * M_PI3.14159265358979323846 / 4) * sin(radianOffset) * cos(latCenter), cos(radianOffset) - sin(latCenter) * sin(latNorth)); | |||
297 | qreal lonWest = fmod(lonCenter - dlonWest + M_PI3.14159265358979323846, 2 * M_PI3.14159265358979323846) - M_PI3.14159265358979323846; | |||
298 | qreal latSouth = asin(sin(latCenter) * cos(radianOffset) + cos(latCenter) * sin(radianOffset) * cos(3 * M_PI3.14159265358979323846 / 4)); | |||
299 | qreal dlonEast = atan2(sin(3 * M_PI3.14159265358979323846 / 4) * sin(radianOffset) * cos(latCenter), cos(radianOffset) - sin(latCenter) * sin(latSouth)); | |||
300 | qreal lonEast = fmod(lonCenter - dlonEast + M_PI3.14159265358979323846, 2 * M_PI3.14159265358979323846) - M_PI3.14159265358979323846; | |||
301 | ||||
302 | int const northY = d->rad2PixelY(latNorth, tileLayer); | |||
303 | int const southY = d->rad2PixelY(latSouth, tileLayer); | |||
304 | int const eastX = d->rad2PixelX(lonEast, tileLayer); | |||
305 | int const westX = d->rad2PixelX(lonWest, tileLayer); | |||
306 | ||||
307 | int const west = qMin(westX, eastX); | |||
308 | int const north = qMin(northY, southY); | |||
309 | int const east = qMax(westX, eastX); | |||
310 | int const south = qMax(northY, southY); | |||
311 | ||||
312 | int bottomLevelTileX1 = 0; | |||
313 | int bottomLevelTileY1 = 0; | |||
314 | int bottomLevelTileX2 = 0; | |||
315 | int bottomLevelTileY2 = 0; | |||
316 | ||||
317 | if (d->m_visibleTileLevel > tileLevelRangeSecond) | |||
318 | { | |||
319 | int const deltaLevel = d->m_visibleTileLevel - tileLevelRangeSecond; | |||
320 | bottomLevelTileX1 = west >> deltaLevel; | |||
321 | bottomLevelTileY1 = north >> deltaLevel; | |||
322 | bottomLevelTileX2 = east >> deltaLevel; | |||
323 | bottomLevelTileY2 = south >> deltaLevel; | |||
324 | } | |||
325 | ||||
326 | else if (d->m_visibleTileLevel < tileLevelRangeSecond) | |||
327 | { | |||
328 | int const deltaLevel = tileLevelRangeSecond - d->m_visibleTileLevel; | |||
329 | bottomLevelTileX1 = west << deltaLevel; | |||
330 | bottomLevelTileY1 = north << deltaLevel; | |||
331 | bottomLevelTileX2 = east << deltaLevel; | |||
332 | bottomLevelTileY2 = south << deltaLevel; | |||
333 | } | |||
334 | ||||
335 | else | |||
336 | { | |||
337 | bottomLevelTileX1 = west; | |||
338 | bottomLevelTileY1 = north; | |||
339 | bottomLevelTileX2 = east; | |||
340 | bottomLevelTileY2 = south; | |||
341 | } | |||
342 | ||||
343 | QRect waypointRegion; | |||
344 | //square region around the waypoint | |||
345 | waypointRegion.setCoords(bottomLevelTileX1 / tileWidth, bottomLevelTileY1 / tileHeight, | |||
346 | bottomLevelTileX2 / tileWidth, bottomLevelTileY2 / tileHeight); | |||
347 | coordsPyramid.setBottomLevelCoords(waypointRegion); | |||
348 | pyramid << coordsPyramid; | |||
349 | } | |||
350 | ||||
351 | return pyramid; | |||
352 | } | |||
353 | ||||
354 | QVector<int> DownloadRegion::validTileLevels(const TileType tileType) const | |||
355 | { | |||
356 | QVector<int> validTileLevels; | |||
357 | ||||
358 | GeoSceneMap* map = d->m_marbleModel->mapTheme()->map(); | |||
359 | QVector<GeoSceneLayer*> layers = map->layers(); | |||
360 | ||||
361 | for (auto layer : layers) | |||
362 | { | |||
363 | if ((layer->backend() == QString::fromUtf8("vectortile") && tileType == VectorTileType) | |||
364 | || (layer->backend() == QString::fromUtf8("texture") && tileType == TextureTileType)) | |||
365 | { | |||
366 | GeoSceneTileDataset* dataset = dynamic_cast<GeoSceneTileDataset*>(layer->datasets().first()); | |||
367 | validTileLevels = dataset->tileLevels(); | |||
368 | break; | |||
369 | } | |||
370 | } | |||
371 | ||||
372 | return validTileLevels; | |||
373 | } | |||
374 | ||||
375 | } // namespace Marble | |||
376 | ||||
377 | #include "moc_DownloadRegion.cpp" |