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

KDE's Doxygen guidelines are available online.