Marble

GeoSceneTileDataset.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Torsten Rahn <rahn@kde.org>
3 SPDX-FileCopyrightText: 2008 Jens-Michael Hoffmann <jensmh@gmx.de>
4 SPDX-FileCopyrightText: 2012 Ander Pijoan <ander.pijoan@deusto.es>
5
6 SPDX-License-Identifier: LGPL-2.1-or-later
7*/
8
9#include "GeoSceneTileDataset.h"
10#include "GeoSceneEquirectTileProjection.h"
11#include "GeoSceneMercatorTileProjection.h"
12#include "GeoSceneTypes.h"
13
14#include "DownloadPolicy.h"
15#include "MarbleDebug.h"
16#include "MarbleDirs.h"
17#include "ServerLayout.h"
18#include "TileId.h"
19
20#include <QImage>
21#include <QUrl>
22
23namespace Marble
24{
25
26GeoSceneTileDataset::GeoSceneTileDataset(const QString &name)
27 : GeoSceneAbstractDataset(name)
28 , m_sourceDir()
29 , m_installMap()
30 , m_storageLayoutMode(Marble)
31 , m_serverLayout(new MarbleServerLayout(this))
32 , m_levelZeroColumns(defaultLevelZeroColumns)
33 , m_levelZeroRows(defaultLevelZeroRows)
34 , m_minimumTileLevel(0)
35 , m_maximumTileLevel(-1)
36 , m_tileProjection(new GeoSceneEquirectTileProjection())
37 , m_blending()
38 , m_downloadUrls()
39 , m_nextUrl(m_downloadUrls.constEnd())
40{
41 m_tileProjection->setLevelZeroColumns(m_levelZeroColumns);
42 m_tileProjection->setLevelZeroRows(m_levelZeroRows);
43}
44
45GeoSceneTileDataset::~GeoSceneTileDataset()
46{
47 qDeleteAll(m_downloadPolicies);
48 delete m_serverLayout;
49 delete m_tileProjection;
50}
51
52const char *GeoSceneTileDataset::nodeType() const
53{
54 return GeoSceneTypes::GeoSceneTileDatasetType;
55}
56
57QString GeoSceneTileDataset::sourceDir() const
58{
59 return m_sourceDir;
60}
61
62void GeoSceneTileDataset::setSourceDir(const QString &sourceDir)
63{
64 m_sourceDir = sourceDir;
65}
66
67QString GeoSceneTileDataset::installMap() const
68{
69 return m_installMap;
70}
71
72void GeoSceneTileDataset::setInstallMap(const QString &installMap)
73{
74 m_installMap = installMap;
75}
76
77GeoSceneTileDataset::StorageLayout GeoSceneTileDataset::storageLayout() const
78{
79 return m_storageLayoutMode;
80}
81
82void GeoSceneTileDataset::setStorageLayout(const StorageLayout layout)
83{
84 m_storageLayoutMode = layout;
85}
86
87void GeoSceneTileDataset::setServerLayout(const ServerLayout *layout)
88{
89 delete m_serverLayout;
90 m_serverLayout = layout;
91}
92
93const ServerLayout *GeoSceneTileDataset::serverLayout() const
94{
95 return m_serverLayout;
96}
97
98int GeoSceneTileDataset::levelZeroColumns() const
99{
100 return m_levelZeroColumns;
101}
102
103void GeoSceneTileDataset::setLevelZeroColumns(const int columns)
104{
105 m_levelZeroColumns = columns;
106 m_tileProjection->setLevelZeroColumns(m_levelZeroColumns);
107}
108
109int GeoSceneTileDataset::levelZeroRows() const
110{
111 return m_levelZeroRows;
112}
113
114void GeoSceneTileDataset::setLevelZeroRows(const int rows)
115{
116 m_levelZeroRows = rows;
117 m_tileProjection->setLevelZeroRows(m_levelZeroRows);
118}
119
120int GeoSceneTileDataset::maximumTileLevel() const
121{
122 return m_maximumTileLevel;
123}
124
125void GeoSceneTileDataset::setMaximumTileLevel(const int maximumTileLevel)
126{
127 m_maximumTileLevel = maximumTileLevel;
128}
129
130int GeoSceneTileDataset::minimumTileLevel() const
131{
132 return m_minimumTileLevel;
133}
134
135void GeoSceneTileDataset::setMinimumTileLevel(int level)
136{
137 m_minimumTileLevel = level;
138}
139
140void GeoSceneTileDataset::setTileLevels(const QString &tileLevels)
141{
142 if (tileLevels.isEmpty()) {
143 m_tileLevels.clear();
144 return;
145 }
146
147 const QStringList values = tileLevels.split(QLatin1Char(','));
148 for (const QString &value : values) {
149 bool canParse(false);
150 int const tileLevel = value.trimmed().toInt(&canParse);
151 if (canParse && tileLevel >= 0 && tileLevel < 100) {
152 m_tileLevels << tileLevel;
153 } else {
154 mDebug() << "Cannot parse tile level part " << value << " in " << tileLevels << ", ignoring it.";
155 }
156 }
157
158 if (!m_tileLevels.isEmpty()) {
159 std::sort(m_tileLevels.begin(), m_tileLevels.end());
160 m_minimumTileLevel = m_tileLevels.first();
161 m_maximumTileLevel = m_tileLevels.last();
162 }
163}
164
165QList<int> GeoSceneTileDataset::tileLevels() const
166{
167 return m_tileLevels;
168}
169
170QList<QUrl> GeoSceneTileDataset::downloadUrls() const
171{
172 return m_downloadUrls;
173}
174
175const QSize GeoSceneTileDataset::tileSize() const
176{
177 if (m_tileSize.isEmpty()) {
178 const TileId id(0, 0, 0, 0);
179 QString const fileName = relativeTileFileName(id);
180 QFileInfo const dirInfo(fileName);
181 QString const path = dirInfo.isAbsolute() ? fileName : MarbleDirs::path(fileName);
182
183 QImage testTile(path);
184
185 if (testTile.isNull()) {
186 mDebug() << "Tile size is missing in dgml and no base tile found in " << themeStr();
187 mDebug() << "Using default tile size " << c_defaultTileSize;
188 m_tileSize = QSize(c_defaultTileSize, c_defaultTileSize);
189 } else {
190 m_tileSize = testTile.size();
191 }
192
193 if (m_tileSize.isEmpty()) {
194 mDebug() << "Tile width or height cannot be 0. Falling back to default tile size.";
195 m_tileSize = QSize(c_defaultTileSize, c_defaultTileSize);
196 }
197 }
198
199 Q_ASSERT(!m_tileSize.isEmpty());
200 return m_tileSize;
201}
202
203GeoDataLatLonBox GeoSceneTileDataset::latLonBox() const
204{
205 return m_latLonBox;
206}
207
208void GeoSceneTileDataset::setLatLonBox(const GeoDataLatLonBox &box)
209{
210 m_latLonBox = box;
211}
212
213void GeoSceneTileDataset::setTileSize(const QSize &tileSize)
214{
215 if (tileSize.isEmpty()) {
216 mDebug() << "Ignoring invalid tile size " << tileSize;
217 } else {
218 m_tileSize = tileSize;
219 }
220}
221
222void GeoSceneTileDataset::setTileProjection(GeoSceneAbstractTileProjection::Type projectionType)
223{
224 if (m_tileProjection->type() == projectionType) {
225 return;
226 }
227
228 delete m_tileProjection;
229 if (projectionType == GeoSceneAbstractTileProjection::Mercator) {
230 m_tileProjection = new GeoSceneMercatorTileProjection();
231 } else {
232 m_tileProjection = new GeoSceneEquirectTileProjection();
233 }
234
235 m_tileProjection->setLevelZeroColumns(m_levelZeroColumns);
236 m_tileProjection->setLevelZeroRows(m_levelZeroRows);
237}
238
239const GeoSceneAbstractTileProjection *GeoSceneTileDataset::tileProjection() const
240{
241 return m_tileProjection;
242}
243
244GeoSceneAbstractTileProjection::Type GeoSceneTileDataset::tileProjectionType() const
245{
246 return m_tileProjection->type();
247}
248
249// Even though this method changes the internal state, it may be const
250// because the compiler is forced to invoke this method for different TileIds.
251QUrl GeoSceneTileDataset::downloadUrl(const TileId &id) const
252{
253 // default download url
254 if (m_downloadUrls.empty()) {
255 QUrl const defaultUrl = QUrl(QLatin1StringView("https://maps.kde.org/") + m_serverLayout->sourceDir());
256 mDebug() << "No download URL specified for tiles stored in " << m_sourceDir << ", falling back to " << defaultUrl.toString();
257 return m_serverLayout->downloadUrl(defaultUrl, id);
258 } else if (m_downloadUrls.size() == 1) {
259 return m_serverLayout->downloadUrl(*m_nextUrl, id);
260 } else {
261 if (m_nextUrl == m_downloadUrls.constEnd()) {
262 m_nextUrl = m_downloadUrls.constBegin();
263 }
264 const QUrl url = m_serverLayout->downloadUrl(*m_nextUrl, id);
265 ++m_nextUrl;
266 return url;
267 }
268}
269
270void GeoSceneTileDataset::addDownloadUrl(const QUrl &url)
271{
272 m_downloadUrls.append(url);
273 // FIXME: this could be done only once
274 m_nextUrl = m_downloadUrls.constBegin();
275}
276
277QString GeoSceneTileDataset::relativeTileFileName(const TileId &id) const
278{
279 const QString suffix = fileFormat().toLower();
280
281 QString relFileName;
282
283 switch (m_storageLayoutMode) {
284 case GeoSceneTileDataset::Marble:
285 relFileName = QStringLiteral("%1/%2/%3/%3_%4.%5")
286 .arg(themeStr())
287 .arg(id.zoomLevel())
288 .arg(id.y(), tileDigits, 10, QLatin1Char('0'))
289 .arg(id.x(), tileDigits, 10, QLatin1Char('0'))
290 .arg(suffix);
291 break;
292 case GeoSceneTileDataset::OpenStreetMap:
293 relFileName = QStringLiteral("%1/%2/%3/%4.%5").arg(themeStr()).arg(id.zoomLevel()).arg(id.x()).arg(id.y()).arg(suffix);
294 break;
295 case GeoSceneTileDataset::TileMapService:
296 relFileName = QStringLiteral("%1/%2/%3/%4.%5")
297 .arg(themeStr())
298 .arg(id.zoomLevel())
299 .arg(id.x())
300 .arg((1 << id.zoomLevel()) - id.y() - 1) // Y coord in TMS runs from bottom to top
301 .arg(suffix);
302 break;
303 }
304
305 return relFileName;
306}
307
308QString GeoSceneTileDataset::themeStr() const
309{
310 QFileInfo const dirInfo(sourceDir());
311 return dirInfo.isAbsolute() ? sourceDir() : QLatin1StringView("maps/") + sourceDir();
312}
313
314QList<const DownloadPolicy *> GeoSceneTileDataset::downloadPolicies() const
315{
316 return m_downloadPolicies;
317}
318
319void GeoSceneTileDataset::addDownloadPolicy(const DownloadUsage usage, const int maximumConnections)
320{
321 auto const policy = new DownloadPolicy(DownloadPolicyKey(hostNames(), usage));
322 policy->setMaximumConnections(maximumConnections);
323 m_downloadPolicies.append(policy);
324 mDebug() << "added download policy" << hostNames() << usage << maximumConnections;
325}
326
327QStringList GeoSceneTileDataset::hostNames() const
328{
329 QStringList result;
330 result.reserve(m_downloadUrls.size());
331
332 QList<QUrl>::const_iterator pos = m_downloadUrls.constBegin();
333 QList<QUrl>::const_iterator const end = m_downloadUrls.constEnd();
334 for (; pos != end; ++pos)
335 result.append((*pos).host());
336 return result;
337}
338
339}
QString path(const QString &relativePath)
QStringView level(QStringView ifopt)
QString name(StandardAction id)
const QList< QKeySequence > & end()
Binds a QML item to a specific geodetic location in screen coordinates.
void append(QList< T > &&value)
void reserve(qsizetype size)
bool isEmpty() const const
QString arg(Args &&... args) const const
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString toLower() const const
QString toString(FormattingOptions options) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:04:14 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.