Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgeotiledmappingmanagerengine_nokia.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
10
11#include <QDebug>
12#include <QDir>
13#include <QVariant>
14#include <QtCore/QJsonArray>
15#include <QtCore/QJsonObject>
16#include <QtCore/QJsonDocument>
17#include <QtCore/qmath.h>
18#include <QtCore/qstandardpaths.h>
19
20#include <QtPositioning/private/qwebmercator_p.h>
21#include <QtPositioning/private/qdoublevector2d_p.h>
22
24
26 QGeoNetworkAccessManager *networkManager,
27 const QVariantMap &parameters,
28 QGeoServiceProvider::Error *error,
29 QString *errorString)
30 : QGeoTiledMappingManagerEngine()
31{
32 Q_UNUSED(error);
33 Q_UNUSED(errorString);
34
35 int ppi = 72;
36 if (parameters.contains(QStringLiteral("here.mapping.highdpi_tiles"))) {
37 const QString param = parameters.value(QStringLiteral("here.mapping.highdpi_tiles")).toString().toLower();
38 if (param == "true")
39 ppi = 250;
40 }
41
42 QGeoCameraCapabilities capabilities;
43
44 capabilities.setMinimumZoomLevel(0.0);
45 capabilities.setMaximumZoomLevel(20.0);
46 if (ppi > 72) {
47 // Zoom levels 0 and 20 are not supported for 512x512 tiles.
48 capabilities.setMinimumZoomLevel(1.0);
49 capabilities.setMaximumZoomLevel(19.0);
50 }
51 capabilities.setSupportsBearing(true);
52 capabilities.setSupportsTilting(true);
53 capabilities.setMinimumTilt(0);
54 capabilities.setMaximumTilt(80);
55 capabilities.setMinimumFieldOfView(20.0);
56 capabilities.setMaximumFieldOfView(120.0);
57 capabilities.setOverzoomEnabled(true);
58 setCameraCapabilities(capabilities);
59
60 setTileSize(QSize(256, 256));
61
62 int mapId = 0;
63 const QByteArray pluginName = "here";
64 QList<QGeoMapType> types;
65 types << QGeoMapType(QGeoMapType::StreetMap, tr("Street Map"), tr("Normal map view in daylight mode"), false, false, ++mapId, pluginName, capabilities);
66 types << QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Satellite Map"), tr("Satellite map view in daylight mode"), false, false, ++mapId, pluginName, capabilities);
67 types << QGeoMapType(QGeoMapType::TerrainMap, tr("Terrain Map"), tr("Terrain map view in daylight mode"), false, false, ++mapId, pluginName, capabilities);
68 types << QGeoMapType(QGeoMapType::HybridMap, tr("Hybrid Map"), tr("Satellite map view with streets in daylight mode"), false, false, ++mapId, pluginName, capabilities);
69 types << QGeoMapType(QGeoMapType::TransitMap, tr("Transit Map"), tr("Color-reduced map view with public transport scheme in daylight mode"), false, false, ++mapId, pluginName, capabilities);
70 types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Gray Street Map"), tr("Color-reduced map view in daylight mode"), false, false, ++mapId, pluginName, capabilities);
71 types << QGeoMapType(QGeoMapType::StreetMap, tr("Mobile Street Map"), tr("Mobile normal map view in daylight mode"), true, false, ++mapId, pluginName, capabilities);
72 types << QGeoMapType(QGeoMapType::TerrainMap, tr("Mobile Terrain Map"), tr("Mobile terrain map view in daylight mode"), true, false, ++mapId, pluginName, capabilities);
73 types << QGeoMapType(QGeoMapType::HybridMap, tr("Mobile Hybrid Map"), tr("Mobile satellite map view with streets in daylight mode"), true, false, ++mapId, pluginName, capabilities);
74 types << QGeoMapType(QGeoMapType::TransitMap, tr("Mobile Transit Map"), tr("Mobile color-reduced map view with public transport scheme in daylight mode"), true, false, ++mapId, pluginName, capabilities);
75 types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Mobile Gray Street Map"), tr("Mobile color-reduced map view in daylight mode"), true, false, ++mapId, pluginName, capabilities);
76 types << QGeoMapType(QGeoMapType::StreetMap, tr("Custom Street Map"), tr("Normal map view in daylight mode"), false, false, ++mapId, pluginName, capabilities);
77 types << QGeoMapType(QGeoMapType::StreetMap, tr("Night Street Map"), tr("Normal map view in night mode"), false, true, ++mapId, pluginName, capabilities);
78 types << QGeoMapType(QGeoMapType::StreetMap, tr("Mobile Night Street Map"), tr("Mobile normal map view in night mode"), true, true, ++mapId, pluginName, capabilities);
79 types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Gray Night Street Map"), tr("Color-reduced map view in night mode (especially used for background maps)"), false, true, ++mapId, pluginName, capabilities);
80 types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Mobile Gray Night Street Map"), tr("Mobile color-reduced map view in night mode (especially used for background maps)"), true, true, ++mapId, pluginName, capabilities);
81 types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Pedestrian Street Map"), tr("Pedestrian map view in daylight mode"), false, false, ++mapId, pluginName, capabilities);
82 types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Mobile Pedestrian Street Map"), tr("Mobile pedestrian map view in daylight mode for mobile usage"), true, false, ++mapId, pluginName, capabilities);
83 types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Pedestrian Night Street Map"), tr("Pedestrian map view in night mode"), false, true, ++mapId, pluginName, capabilities);
84 types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Mobile Pedestrian Night Street Map"), tr("Mobile pedestrian map view in night mode for mobile usage"), true, true, ++mapId, pluginName, capabilities);
85 types << QGeoMapType(QGeoMapType::CarNavigationMap, tr("Car Navigation Map"), tr("Normal map view in daylight mode for car navigation"), false, false, ++mapId, pluginName, capabilities);
86 setSupportedMapTypes(types);
87
88 QGeoTileFetcherNokia *fetcher = new QGeoTileFetcherNokia(parameters, networkManager, this, tileSize(), ppi);
89 setTileFetcher(fetcher);
90
91 /* TILE CACHE */
92 // TODO: do this in a plugin-neutral way so that other tiled map plugins
93 // don't need this boilerplate or hardcode plugin name
94 if (parameters.contains(QStringLiteral("here.mapping.cache.directory"))) {
95 m_cacheDirectory = parameters.value(QStringLiteral("here.mapping.cache.directory")).toString();
96 } else {
97 // managerName() is not yet set, we have to hardcode the plugin name below
98 m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String(pluginName);
99 }
100
101 QGeoFileTileCache *tileCache = new QGeoFileTileCacheNokia(ppi, m_cacheDirectory);
102
103 /*
104 * Disk cache setup -- defaults to ByteSize (old behavior)
105 */
106 if (parameters.contains(QStringLiteral("here.mapping.cache.disk.cost_strategy"))) {
107 QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.disk.cost_strategy")).toString().toLower();
108 if (cacheStrategy == QLatin1String("bytesize"))
109 tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize);
110 else
111 tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary);
112 } else {
113 tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize);
114 }
115 if (parameters.contains(QStringLiteral("here.mapping.cache.disk.size"))) {
116 bool ok = false;
117 int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.disk.size")).toString().toInt(&ok);
118 if (ok)
119 tileCache->setMaxDiskUsage(cacheSize);
120 }
121
122 /*
123 * Memory cache setup -- defaults to ByteSize (old behavior)
124 */
125 if (parameters.contains(QStringLiteral("here.mapping.cache.memory.cost_strategy"))) {
126 QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.memory.cost_strategy")).toString().toLower();
127 if (cacheStrategy == QLatin1String("bytesize"))
128 tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize);
129 else
130 tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary);
131 } else {
132 tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize);
133 }
134 if (parameters.contains(QStringLiteral("here.mapping.cache.memory.size"))) {
135 bool ok = false;
136 int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.memory.size")).toString().toInt(&ok);
137 if (ok)
138 tileCache->setMaxMemoryUsage(cacheSize);
139 }
140
141 /*
142 * Texture cache setup -- defaults to ByteSize (old behavior)
143 */
144 if (parameters.contains(QStringLiteral("here.mapping.cache.texture.cost_strategy"))) {
145 QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.texture.cost_strategy")).toString().toLower();
146 if (cacheStrategy == QLatin1String("bytesize"))
147 tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize);
148 else
149 tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary);
150 } else {
151 tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize);
152 }
153 if (parameters.contains(QStringLiteral("here.mapping.cache.texture.size"))) {
154 bool ok = false;
155 int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.texture.size")).toString().toInt(&ok);
156 if (ok)
157 tileCache->setExtraTextureUsage(cacheSize);
158 }
159
160 /* PREFETCHING */
161 if (parameters.contains(QStringLiteral("here.mapping.prefetching_style"))) {
162 const QString prefetchingMode = parameters.value(QStringLiteral("here.mapping.prefetching_style")).toString();
163 if (prefetchingMode == QStringLiteral("TwoNeighbourLayers"))
164 m_prefetchStyle = QGeoTiledMap::PrefetchTwoNeighbourLayers;
165 else if (prefetchingMode == QStringLiteral("OneNeighbourLayer"))
166 m_prefetchStyle = QGeoTiledMap::PrefetchNeighbourLayer;
167 else if (prefetchingMode == QStringLiteral("NoPrefetching"))
168 m_prefetchStyle = QGeoTiledMap::NoPrefetching;
169 }
170
171 setTileCache(tileCache);
172 populateMapSchemes();
173 loadMapVersion();
174 QMetaObject::invokeMethod(fetcher, "fetchCopyrightsData", Qt::QueuedConnection);
175 QMetaObject::invokeMethod(fetcher, "fetchVersionData", Qt::QueuedConnection);
176}
177
181
182void QGeoTiledMappingManagerEngineNokia::populateMapSchemes()
183{
184 m_mapSchemes[0] = QStringLiteral("normal.day");
185 m_mapSchemes[1] = QStringLiteral("normal.day");
186 m_mapSchemes[2] = QStringLiteral("satellite.day");
187 m_mapSchemes[3] = QStringLiteral("terrain.day");
188 m_mapSchemes[4] = QStringLiteral("hybrid.day");
189 m_mapSchemes[5] = QStringLiteral("normal.day.transit");
190 m_mapSchemes[6] = QStringLiteral("normal.day.grey");
191 m_mapSchemes[7] = QStringLiteral("normal.day.mobile");
192 m_mapSchemes[8] = QStringLiteral("terrain.day.mobile");
193 m_mapSchemes[9] = QStringLiteral("hybrid.day.mobile");
194 m_mapSchemes[10] = QStringLiteral("normal.day.transit.mobile");
195 m_mapSchemes[11] = QStringLiteral("normal.day.grey.mobile");
196 m_mapSchemes[12] = QStringLiteral("normal.day.custom");
197 m_mapSchemes[13] = QStringLiteral("normal.night");
198 m_mapSchemes[14] = QStringLiteral("normal.night.mobile");
199 m_mapSchemes[15] = QStringLiteral("normal.night.grey");
200 m_mapSchemes[16] = QStringLiteral("normal.night.grey.mobile");
201 m_mapSchemes[17] = QStringLiteral("pedestrian.day");
202 m_mapSchemes[18] = QStringLiteral("pedestrian.day.mobile");
203 m_mapSchemes[19] = QStringLiteral("pedestrian.night");
204 m_mapSchemes[20] = QStringLiteral("pedestrian.night.mobile");
205 m_mapSchemes[21] = QStringLiteral("carnav.day.grey");
206}
207
209{
210 return m_mapSchemes[mapId];
211}
212
214{
215 const QString fullScheme(m_mapSchemes[mapId]);
216
217 return fullScheme.section(QLatin1Char('.'), 0, 0);
218}
219
221{
222 return m_mapVersion.version();
223}
224
225void QGeoTiledMappingManagerEngineNokia::loadCopyrightsDescriptorsFromJson(const QByteArray &jsonData)
226{
227 QJsonDocument doc = QJsonDocument::fromJson(QByteArray(jsonData));
228 if (doc.isNull()) {
229 qDebug() << "QGeoTiledMappingManagerEngineNokia::loadCopyrightsDescriptorsFromJson() Invalid JSon document";
230 return;
231 }
232
233 QJsonObject jsonObj = doc.object();
234
235 m_copyrights.clear();
236 for (auto it = jsonObj.constBegin(), end = jsonObj.constEnd(); it != end; ++it) {
237 QList<CopyrightDesc> copyrightDescList;
238
239 QJsonArray descs = it.value().toArray();
240 for (int descIndex = 0; descIndex < descs.count(); descIndex++) {
241 CopyrightDesc copyrightDesc;
242 QJsonObject desc = descs.at(descIndex).toObject();
243
244 copyrightDesc.minLevel = desc["minLevel"].toDouble();
245 copyrightDesc.maxLevel = desc["maxLevel"].toDouble();
246 copyrightDesc.label = desc["label"].toString();
247 copyrightDesc.alt = desc["alt"].toString();
248
249 QJsonArray coordBoxes = desc["boxes"].toArray();
250 for (int boxIndex = 0; boxIndex < coordBoxes.count(); boxIndex++) {
251 QJsonArray box = coordBoxes[boxIndex].toArray();
252 qreal top = box[0].toDouble();
253 qreal left = box[1].toDouble();
254 qreal bottom = box[2].toDouble();
255 qreal right = box[3].toDouble();
256 QGeoRectangle boundingBox(QGeoCoordinate(top > bottom? top : bottom,
257 left),
258 QGeoCoordinate(top > bottom? bottom : top,
259 right));
260 copyrightDesc.boxes << boundingBox;
261 }
262 copyrightDescList << copyrightDesc;
263 }
264 m_copyrights[it.key()] = copyrightDescList;
265 }
266}
267
269{
270 const QString versionString = QString::fromUtf8(versionData);
271
272 const QStringList versionLines = versionString.split(QLatin1Char('\n'));
273 QJsonObject newVersionData;
274 for (const QString &line : versionLines) {
275 const QStringList versionInfo = line.split(':');
276 if (versionInfo.size() > 1) {
277 const QString versionKey = versionInfo[0].trimmed();
278 const QString versionValue = versionInfo[1].trimmed();
279 if (!versionKey.isEmpty() && !versionValue.isEmpty()) {
280 newVersionData[versionKey] = versionValue;
281 }
282 }
283 }
284
285 updateVersion(newVersionData);
286}
287
288void QGeoTiledMappingManagerEngineNokia::updateVersion(const QJsonObject &newVersionData) {
289
290 if (m_mapVersion.isNewVersion(newVersionData)) {
291
292 m_mapVersion.setVersionData(newVersionData);
293 m_mapVersion.setVersion(m_mapVersion.version() + 1);
294
295 saveMapVersion();
296 setTileVersion(m_mapVersion.version());
297 }
298}
299
300void QGeoTiledMappingManagerEngineNokia::saveMapVersion()
301{
302 QDir saveDir(m_cacheDirectory);
303 QFile saveFile(saveDir.filePath(QStringLiteral("here_version")));
304
305 if (!saveFile.open(QIODevice::WriteOnly)) {
306 qWarning("Failed to write here/nokia map version.");
307 return;
308 }
309
310 saveFile.write(m_mapVersion.toJson());
311 saveFile.close();
312}
313
314void QGeoTiledMappingManagerEngineNokia::loadMapVersion()
315{
316 QDir saveDir(m_cacheDirectory);
317 QFile loadFile(saveDir.filePath(QStringLiteral("here_version")));
318
319 if (!loadFile.open(QIODevice::ReadOnly)) {
320 qWarning("Failed to read here/nokia map version.");
321 return;
322 }
323
324 QByteArray saveData = loadFile.readAll();
325 loadFile.close();
326
327 QJsonDocument doc(QJsonDocument::fromJson(saveData));
328
329 QJsonObject object = doc.object();
330
331 m_mapVersion.setVersion(object[QStringLiteral("version")].toInt());
332 m_mapVersion.setVersionData(object[QStringLiteral("data")].toObject());
333 setTileVersion(m_mapVersion.version());
334}
335
337 qreal zoomLevel,
338 const QSet<QGeoTileSpec> &tiles)
339{
340 static const QChar copyrightSymbol(0x00a9);
341 typedef QSet<QGeoTileSpec>::const_iterator tile_iter;
342 QGeoRectangle viewport;
343 double viewX0, viewY0, viewX1, viewY1;
344
345 tile_iter tile = tiles.constBegin();
346 tile_iter lastTile = tiles.constEnd();
347
348 if (tiles.count()) {
349 double divFactor = qPow(2.0, tile->zoom());
350 viewX0 = viewX1 = tile->x();
351 viewY0 = viewY1 = tile->y();
352
353 // this approach establishes a geo-bounding box from passed tiles to test for intersecition
354 // with copyrights boxes.
355 int numTiles = 0;
356 for (; tile != lastTile; ++tile) {
357 if (tile->x() < viewX0)
358 viewX0 = tile->x();
359 if (tile->x() > viewX1)
360 viewX1 = tile->x();
361 if (tile->y() < viewY0)
362 viewY0 = tile->y();
363 if (tile->y() > viewY1)
364 viewY1 = tile->y();
365 numTiles++;
366 }
367
368 viewX1++;
369 viewY1++;
370
371 QDoubleVector2D pt;
372
373 pt.setX(viewX0 / divFactor);
374 pt.setY(viewY0 / divFactor);
375 viewport.setTopLeft(QWebMercator::mercatorToCoord(pt));
376 pt.setX(viewX1 / divFactor);
377 pt.setY(viewY1 / divFactor);
378 viewport.setBottomRight(QWebMercator::mercatorToCoord(pt));
379 }
380
381 // TODO: the following invalidation detection algorithm may be improved later.
382 QList<CopyrightDesc> descriptorList = m_copyrights[ getBaseScheme(mapType.mapId()) ];
383 CopyrightDesc *descriptor;
384 int descIndex, boxIndex;
385 QString copyrightsText;
386 QSet<QString> copyrightStrings;
387
388 for (descIndex = 0; descIndex < descriptorList.count(); descIndex++) {
389 if (descriptorList[descIndex].minLevel <= zoomLevel && zoomLevel <= descriptorList[descIndex].maxLevel) {
390 descriptor = &descriptorList[descIndex];
391
392 for (boxIndex = 0; boxIndex < descriptor->boxes.count(); boxIndex++) {
393 QGeoRectangle box = descriptor->boxes[boxIndex];
394
395 if (box.intersects(viewport)) {
396 copyrightStrings.insert(descriptor->label);
397 break;
398 }
399 }
400 if (!descriptor->boxes.count())
401 copyrightStrings.insert(descriptor->label);
402 }
403 }
404
405 for (const QString &copyrightString : copyrightStrings) {
406 if (copyrightsText.length())
407 copyrightsText += QLatin1Char('\n');
408 copyrightsText += copyrightSymbol;
409 copyrightsText += copyrightString;
410 }
411
412 return copyrightsText;
413}
414
416{
417 QGeoTiledMap *map = new QGeoTiledMapNokia(this);
418 map->setPrefetchStyle(m_prefetchStyle);
419 return map;
420}
421
422QT_END_NAMESPACE
void parseNewVersionInfo(const QByteArray &versionData)
QString evaluateCopyrightsText(const QGeoMapType &mapType, qreal zoomLevel, const QSet< QGeoTileSpec > &tiles)