Marble

VectorTileModel.cpp
1 /*
2  SPDX-License-Identifier: LGPL-2.1-or-later
3 
4  SPDX-FileCopyrightText: 2012 Ander Pijoan <[email protected]>
5  SPDX-FileCopyrightText: 2013 Bernhard Beschow <[email protected]>
6 */
7 
8 #include "VectorTileModel.h"
9 
10 #include "GeoDataDocument.h"
11 #include "GeoDataTreeModel.h"
12 #include "GeoSceneVectorTileDataset.h"
13 #include "MarbleGlobal.h"
14 #include "MarbleDebug.h"
15 #include "MathHelper.h"
16 #include "TileLoader.h"
17 
18 #include <qmath.h>
19 #include <QThreadPool>
20 
21 namespace Marble
22 {
23 
24 TileRunner::TileRunner(TileLoader *loader, const GeoSceneVectorTileDataset *tileDataset, const TileId &id) :
25  m_loader(loader),
26  m_tileDataset(tileDataset),
27  m_id(id)
28 {
29 }
30 
31 void TileRunner::run()
32 {
33  GeoDataDocument *const document = m_loader->loadTileVectorData(m_tileDataset, m_id, DownloadBrowse);
34 
35  emit documentLoaded(m_id, document);
36 }
37 
38 VectorTileModel::CacheDocument::CacheDocument(GeoDataDocument *doc, VectorTileModel *vectorTileModel, const GeoDataLatLonBox &boundingBox) :
39  m_document(doc),
40  m_vectorTileModel(vectorTileModel),
41  m_boundingBox(boundingBox)
42 {
43  // nothing to do
44 }
45 
46 VectorTileModel::CacheDocument::~CacheDocument()
47 {
48  m_vectorTileModel->removeTile(m_document);
49 }
50 
51 VectorTileModel::VectorTileModel(TileLoader *loader, const GeoSceneVectorTileDataset *layer, GeoDataTreeModel *treeModel, QThreadPool *threadPool) :
52  m_loader(loader),
53  m_layer(layer),
54  m_treeModel(treeModel),
55  m_threadPool(threadPool),
56  m_tileLoadLevel(-1),
57  m_tileZoomLevel(-1),
58  m_deleteDocumentsLater(false)
59 {
60  connect(this, SIGNAL(tileAdded(GeoDataDocument*)), treeModel, SLOT(addDocument(GeoDataDocument*)));
61  connect(this, SIGNAL(tileRemoved(GeoDataDocument*)), treeModel, SLOT(removeDocument(GeoDataDocument*)));
62  connect(treeModel, SIGNAL(removed(GeoDataObject*)), this, SLOT(cleanupTile(GeoDataObject*)));
63 }
64 
65 void VectorTileModel::setViewport(const GeoDataLatLonBox &latLonBox)
66 {
67  bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
68  int const nTiles = smallScreen ? 12 : 20;
69  qreal const viewportArea = latLonBox.width() * latLonBox.height();
70  qreal const level = log((nTiles * 2.0 * M_PI * M_PI) / viewportArea) / log(4);
71  m_tileZoomLevel = qFloor(level);
72  int tileLoadLevel = m_tileZoomLevel;
73 
74  // Determine available tile levels in the layer and thereby
75  // select the tileZoomLevel that is actually used:
76  QVector<int> tileLevels = m_layer->tileLevels();
77  if (tileLevels.isEmpty() /* || tileZoomLevel < tileLevels.first() */) {
78  // if there is no (matching) tile level then show nothing
79  // and bail out.
80  m_documents.clear();
81  return;
82  }
83  int tileLevel = tileLevels.first();
84  for (int i = 1, n = tileLevels.size(); i < n; ++i) {
85  if (tileLevels[i] > tileLoadLevel) {
86  break;
87  }
88  tileLevel = tileLevels[i];
89  }
90  tileLoadLevel = tileLevel;
91 
92  // if zoom level has changed, empty vectortile cache
93  if (tileLoadLevel != m_tileLoadLevel) {
94  m_deleteDocumentsLater = m_tileLoadLevel >= 0;
95  m_tileLoadLevel = tileLoadLevel;
96  }
97 
98  /** LOGIC FOR DOWNLOADING ALL THE TILES THAT ARE INSIDE THE SCREEN AT THE CURRENT ZOOM LEVEL **/
99 
100  // New tiles X and Y for moved screen coordinates
101  // More info: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Subtiles
102  // More info: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#C.2FC.2B.2B
103  const QRect rect = m_layer->tileProjection()->tileIndexes(latLonBox, tileLoadLevel);
104 
105  // Download tiles and send them to VectorTileLayer
106  // When changing zoom, download everything inside the screen
107  // TODO: hardcodes assumption about tiles indexing also ends at dateline
108  // TODO: what about crossing things in y direction?
109  if (!latLonBox.crossesDateLine()) {
110  queryTiles(tileLoadLevel, rect);
111  }
112  // When only moving screen, just download the new tiles
113  else {
114  // TODO: maxTileX (calculation knowledge) should be a property of tileProjection or m_layer
115  const int maxTileX = (1 << tileLoadLevel) * m_layer->levelZeroColumns() - 1;
116 
117  queryTiles(tileLoadLevel, QRect(QPoint(0, rect.top()), rect.bottomRight()));
118  queryTiles(tileLoadLevel, QRect(rect.topLeft(), QPoint(maxTileX, rect.bottom())));
119  }
120  removeTilesOutOfView(latLonBox);
121 }
122 
123 void VectorTileModel::removeTilesOutOfView(const GeoDataLatLonBox &boundingBox)
124 {
125  GeoDataLatLonBox const extendedViewport = boundingBox.scaled(2.0, 2.0);
126  for (auto iter = m_documents.begin(); iter != m_documents.end();) {
127  bool const isOutOfView = !extendedViewport.intersects(iter.value()->latLonBox());
128  if (isOutOfView) {
129  iter = m_documents.erase(iter);
130  } else {
131  ++iter;
132  }
133  }
134 }
135 
136 QString VectorTileModel::name() const
137 {
138  return m_layer->name();
139 }
140 
141 const GeoSceneVectorTileDataset *VectorTileModel::layer() const
142 {
143  return m_layer;
144 }
145 
146 void VectorTileModel::removeTile(GeoDataDocument *document)
147 {
148  emit tileRemoved(document);
149 }
150 
151 int VectorTileModel::tileZoomLevel() const
152 {
153  return m_tileZoomLevel;
154 }
155 
156 int VectorTileModel::cachedDocuments() const
157 {
158  return m_documents.size();
159 }
160 
161 void VectorTileModel::reload()
162 {
163  for (auto const &tile : m_documents.keys()) {
164  m_loader->downloadTile(m_layer, tile, DownloadBrowse);
165  }
166 }
167 
168 void VectorTileModel::updateTile(const TileId &idWithMapThemeHash, GeoDataDocument *document)
169 {
170  TileId const id(0, idWithMapThemeHash.zoomLevel(), idWithMapThemeHash.x(), idWithMapThemeHash.y());
171  m_pendingDocuments.removeAll(id);
172  if (!document) {
173  return;
174  }
175 
176  if (m_tileLoadLevel != id.zoomLevel()) {
177  delete document;
178  return;
179  }
180 
181  document->setName(QString("%1/%2/%3").arg(id.zoomLevel()).arg(id.x()).arg(id.y()));
182  m_garbageQueue << document;
183  if (m_documents.contains(id)) {
184  m_documents.remove(id);
185  }
186  if (m_deleteDocumentsLater) {
187  m_deleteDocumentsLater = false;
188  m_documents.clear();
189  }
190  const GeoDataLatLonBox boundingBox = m_layer->tileProjection()->geoCoordinates(id);
191  m_documents[id] = QSharedPointer<CacheDocument>(new CacheDocument(document, this, boundingBox));
192  emit tileAdded(document);
193 }
194 
195 void VectorTileModel::clear()
196 {
197  m_documents.clear();
198 }
199 
200 void VectorTileModel::queryTiles(int tileZoomLevel, const QRect &rect)
201 {
202  // Download all the tiles inside the given indexes
203  for (int x = rect.left(); x <= rect.right(); ++x) {
204  for (int y = rect.top(); y <= rect.bottom(); ++y) {
205  const TileId tileId = TileId(0, tileZoomLevel, x, y);
206  if (!m_documents.contains(tileId) && !m_pendingDocuments.contains(tileId)) {
207  m_pendingDocuments << tileId;
208  TileRunner *job = new TileRunner(m_loader, m_layer, tileId);
209  connect(job, SIGNAL(documentLoaded(TileId,GeoDataDocument*)), this, SLOT(updateTile(TileId,GeoDataDocument*)));
210  m_threadPool->start(job);
211  }
212  }
213  }
214 }
215 
216 void VectorTileModel::cleanupTile(GeoDataObject *object)
217 {
218  if (GeoDataDocument *document = geodata_cast<GeoDataDocument>(object)) {
219  if (m_garbageQueue.contains(document)) {
220  m_garbageQueue.removeAll(document);
221  delete document;
222  }
223  }
224 }
225 
226 }
227 
228 #include "moc_VectorTileModel.cpp"
bool isEmpty() const const
QPoint topLeft() const const
@ DownloadBrowse
Browsing mode, normal operation of Marble, like a web browser.
Definition: MarbleGlobal.h:155
int right() const const
QStringView level(QStringView ifopt)
QPoint bottomRight() const const
T & first()
int left() const const
int bottom() const const
int top() const const
void clear()
Binds a QML item to a specific geodetic location in screen coordinates.
int size() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Oct 4 2023 04:09:43 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.