| /**************************************************************************** |
| ** |
| ** Copyright (C) 2015 The Qt Company Ltd. |
| ** Contact: http://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtLocation module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL3$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see http://www.qt.io/terms-conditions. For further |
| ** information use the contact form at http://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or later as published by the Free |
| ** Software Foundation and appearing in the file LICENSE.GPL included in |
| ** the packaging of this file. Please review the following information to |
| ** ensure the GNU General Public License version 2.0 requirements will be |
| ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <QtPositioning/private/qwebmercator_p.h> |
| #include "qgeocameracapabilities_p.h" |
| #include "qgeotiledmappingmanagerengine_nokia.h" |
| #include "qgeotiledmap_nokia.h" |
| #include "qgeotilefetcher_nokia.h" |
| #include "qgeotilespec_p.h" |
| #include "qgeofiletilecachenokia.h" |
| |
| #include <QDebug> |
| #include <QDir> |
| #include <QVariant> |
| #include <QtCore/QJsonArray> |
| #include <QtCore/QJsonObject> |
| #include <QtCore/QJsonDocument> |
| #include <QtCore/qmath.h> |
| #include <QtCore/qstandardpaths.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| QGeoTiledMappingManagerEngineNokia::QGeoTiledMappingManagerEngineNokia( |
| QGeoNetworkAccessManager *networkManager, |
| const QVariantMap ¶meters, |
| QGeoServiceProvider::Error *error, |
| QString *errorString) |
| : QGeoTiledMappingManagerEngine() |
| { |
| Q_UNUSED(error); |
| Q_UNUSED(errorString); |
| |
| int ppi = 72; |
| if (parameters.contains(QStringLiteral("here.mapping.highdpi_tiles"))) { |
| const QString param = parameters.value(QStringLiteral("here.mapping.highdpi_tiles")).toString().toLower(); |
| if (param == "true") |
| ppi = 250; |
| } |
| |
| QGeoCameraCapabilities capabilities; |
| |
| capabilities.setMinimumZoomLevel(0.0); |
| capabilities.setMaximumZoomLevel(20.0); |
| if (ppi > 72) { |
| // Zoom levels 0 and 20 are not supported for 512x512 tiles. |
| capabilities.setMinimumZoomLevel(1.0); |
| capabilities.setMaximumZoomLevel(19.0); |
| } |
| capabilities.setSupportsBearing(true); |
| capabilities.setSupportsTilting(true); |
| capabilities.setMinimumTilt(0); |
| capabilities.setMaximumTilt(80); |
| capabilities.setMinimumFieldOfView(20.0); |
| capabilities.setMaximumFieldOfView(120.0); |
| capabilities.setOverzoomEnabled(true); |
| setCameraCapabilities(capabilities); |
| |
| setTileSize(QSize(256, 256)); |
| |
| int mapId = 0; |
| const QByteArray pluginName = "here"; |
| QList<QGeoMapType> types; |
| types << QGeoMapType(QGeoMapType::StreetMap, tr("Street Map"), tr("Normal map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Satellite Map"), tr("Satellite map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::TerrainMap, tr("Terrain Map"), tr("Terrain map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::HybridMap, tr("Hybrid Map"), tr("Satellite map view with streets in daylight mode"), false, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::TransitMap, tr("Transit Map"), tr("Color-reduced map view with public transport scheme in daylight mode"), false, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Gray Street Map"), tr("Color-reduced map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::StreetMap, tr("Mobile Street Map"), tr("Mobile normal map view in daylight mode"), true, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::TerrainMap, tr("Mobile Terrain Map"), tr("Mobile terrain map view in daylight mode"), true, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::HybridMap, tr("Mobile Hybrid Map"), tr("Mobile satellite map view with streets in daylight mode"), true, false, ++mapId, pluginName, capabilities); |
| 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); |
| types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Mobile Gray Street Map"), tr("Mobile color-reduced map view in daylight mode"), true, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::StreetMap, tr("Custom Street Map"), tr("Normal map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::StreetMap, tr("Night Street Map"), tr("Normal map view in night mode"), false, true, ++mapId, pluginName, capabilities); |
| types << QGeoMapType(QGeoMapType::StreetMap, tr("Mobile Night Street Map"), tr("Mobile normal map view in night mode"), true, true, ++mapId, pluginName, capabilities); |
| 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); |
| 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); |
| types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Pedestrian Street Map"), tr("Pedestrian map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); |
| 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); |
| types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Pedestrian Night Street Map"), tr("Pedestrian map view in night mode"), false, true, ++mapId, pluginName, capabilities); |
| 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); |
| types << QGeoMapType(QGeoMapType::CarNavigationMap, tr("Car Navigation Map"), tr("Normal map view in daylight mode for car navigation"), false, false, ++mapId, pluginName, capabilities); |
| setSupportedMapTypes(types); |
| |
| QGeoTileFetcherNokia *fetcher = new QGeoTileFetcherNokia(parameters, networkManager, this, tileSize(), ppi); |
| setTileFetcher(fetcher); |
| |
| /* TILE CACHE */ |
| // TODO: do this in a plugin-neutral way so that other tiled map plugins |
| // don't need this boilerplate or hardcode plugin name |
| if (parameters.contains(QStringLiteral("here.mapping.cache.directory"))) { |
| m_cacheDirectory = parameters.value(QStringLiteral("here.mapping.cache.directory")).toString(); |
| } else { |
| // managerName() is not yet set, we have to hardcode the plugin name below |
| m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String(pluginName); |
| } |
| |
| QGeoFileTileCache *tileCache = new QGeoFileTileCacheNokia(ppi, m_cacheDirectory); |
| |
| /* |
| * Disk cache setup -- defaults to ByteSize (old behavior) |
| */ |
| if (parameters.contains(QStringLiteral("here.mapping.cache.disk.cost_strategy"))) { |
| QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.disk.cost_strategy")).toString().toLower(); |
| if (cacheStrategy == QLatin1String("bytesize")) |
| tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); |
| else |
| tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); |
| } else { |
| tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); |
| } |
| if (parameters.contains(QStringLiteral("here.mapping.cache.disk.size"))) { |
| bool ok = false; |
| int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.disk.size")).toString().toInt(&ok); |
| if (ok) |
| tileCache->setMaxDiskUsage(cacheSize); |
| } |
| |
| /* |
| * Memory cache setup -- defaults to ByteSize (old behavior) |
| */ |
| if (parameters.contains(QStringLiteral("here.mapping.cache.memory.cost_strategy"))) { |
| QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.memory.cost_strategy")).toString().toLower(); |
| if (cacheStrategy == QLatin1String("bytesize")) |
| tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); |
| else |
| tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); |
| } else { |
| tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); |
| } |
| if (parameters.contains(QStringLiteral("here.mapping.cache.memory.size"))) { |
| bool ok = false; |
| int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.memory.size")).toString().toInt(&ok); |
| if (ok) |
| tileCache->setMaxMemoryUsage(cacheSize); |
| } |
| |
| /* |
| * Texture cache setup -- defaults to ByteSize (old behavior) |
| */ |
| if (parameters.contains(QStringLiteral("here.mapping.cache.texture.cost_strategy"))) { |
| QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.texture.cost_strategy")).toString().toLower(); |
| if (cacheStrategy == QLatin1String("bytesize")) |
| tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); |
| else |
| tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); |
| } else { |
| tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); |
| } |
| if (parameters.contains(QStringLiteral("here.mapping.cache.texture.size"))) { |
| bool ok = false; |
| int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.texture.size")).toString().toInt(&ok); |
| if (ok) |
| tileCache->setExtraTextureUsage(cacheSize); |
| } |
| |
| /* PREFETCHING */ |
| if (parameters.contains(QStringLiteral("here.mapping.prefetching_style"))) { |
| const QString prefetchingMode = parameters.value(QStringLiteral("here.mapping.prefetching_style")).toString(); |
| if (prefetchingMode == QStringLiteral("TwoNeighbourLayers")) |
| m_prefetchStyle = QGeoTiledMap::PrefetchTwoNeighbourLayers; |
| else if (prefetchingMode == QStringLiteral("OneNeighbourLayer")) |
| m_prefetchStyle = QGeoTiledMap::PrefetchNeighbourLayer; |
| else if (prefetchingMode == QStringLiteral("NoPrefetching")) |
| m_prefetchStyle = QGeoTiledMap::NoPrefetching; |
| } |
| |
| setTileCache(tileCache); |
| populateMapSchemes(); |
| loadMapVersion(); |
| QMetaObject::invokeMethod(fetcher, "fetchCopyrightsData", Qt::QueuedConnection); |
| QMetaObject::invokeMethod(fetcher, "fetchVersionData", Qt::QueuedConnection); |
| } |
| |
| QGeoTiledMappingManagerEngineNokia::~QGeoTiledMappingManagerEngineNokia() |
| { |
| } |
| |
| void QGeoTiledMappingManagerEngineNokia::populateMapSchemes() |
| { |
| m_mapSchemes[0] = QStringLiteral("normal.day"); |
| m_mapSchemes[1] = QStringLiteral("normal.day"); |
| m_mapSchemes[2] = QStringLiteral("satellite.day"); |
| m_mapSchemes[3] = QStringLiteral("terrain.day"); |
| m_mapSchemes[4] = QStringLiteral("hybrid.day"); |
| m_mapSchemes[5] = QStringLiteral("normal.day.transit"); |
| m_mapSchemes[6] = QStringLiteral("normal.day.grey"); |
| m_mapSchemes[7] = QStringLiteral("normal.day.mobile"); |
| m_mapSchemes[8] = QStringLiteral("terrain.day.mobile"); |
| m_mapSchemes[9] = QStringLiteral("hybrid.day.mobile"); |
| m_mapSchemes[10] = QStringLiteral("normal.day.transit.mobile"); |
| m_mapSchemes[11] = QStringLiteral("normal.day.grey.mobile"); |
| m_mapSchemes[12] = QStringLiteral("normal.day.custom"); |
| m_mapSchemes[13] = QStringLiteral("normal.night"); |
| m_mapSchemes[14] = QStringLiteral("normal.night.mobile"); |
| m_mapSchemes[15] = QStringLiteral("normal.night.grey"); |
| m_mapSchemes[16] = QStringLiteral("normal.night.grey.mobile"); |
| m_mapSchemes[17] = QStringLiteral("pedestrian.day"); |
| m_mapSchemes[18] = QStringLiteral("pedestrian.day.mobile"); |
| m_mapSchemes[19] = QStringLiteral("pedestrian.night"); |
| m_mapSchemes[20] = QStringLiteral("pedestrian.night.mobile"); |
| m_mapSchemes[21] = QStringLiteral("carnav.day.grey"); |
| } |
| |
| QString QGeoTiledMappingManagerEngineNokia::getScheme(int mapId) |
| { |
| return m_mapSchemes[mapId]; |
| } |
| |
| QString QGeoTiledMappingManagerEngineNokia::getBaseScheme(int mapId) |
| { |
| QString fullScheme(m_mapSchemes[mapId]); |
| |
| return fullScheme.section(QLatin1Char('.'), 0, 0); |
| } |
| |
| int QGeoTiledMappingManagerEngineNokia::mapVersion() |
| { |
| return m_mapVersion.version(); |
| } |
| |
| void QGeoTiledMappingManagerEngineNokia::loadCopyrightsDescriptorsFromJson(const QByteArray &jsonData) |
| { |
| QJsonDocument doc = QJsonDocument::fromJson(QByteArray(jsonData)); |
| if (doc.isNull()) { |
| qDebug() << "QGeoTiledMappingManagerEngineNokia::loadCopyrightsDescriptorsFromJson() Invalid JSon document"; |
| return; |
| } |
| |
| QJsonObject jsonObj = doc.object(); |
| |
| m_copyrights.clear(); |
| for (auto it = jsonObj.constBegin(), end = jsonObj.constEnd(); it != end; ++it) { |
| QList<CopyrightDesc> copyrightDescList; |
| |
| QJsonArray descs = it.value().toArray(); |
| for (int descIndex = 0; descIndex < descs.count(); descIndex++) { |
| CopyrightDesc copyrightDesc; |
| QJsonObject desc = descs.at(descIndex).toObject(); |
| |
| copyrightDesc.minLevel = desc["minLevel"].toDouble(); |
| copyrightDesc.maxLevel = desc["maxLevel"].toDouble(); |
| copyrightDesc.label = desc["label"].toString(); |
| copyrightDesc.alt = desc["alt"].toString(); |
| |
| QJsonArray coordBoxes = desc["boxes"].toArray(); |
| for (int boxIndex = 0; boxIndex < coordBoxes.count(); boxIndex++) { |
| QJsonArray box = coordBoxes[boxIndex].toArray(); |
| qreal top = box[0].toDouble(); |
| qreal left = box[1].toDouble(); |
| qreal bottom = box[2].toDouble(); |
| qreal right = box[3].toDouble(); |
| QGeoRectangle boundingBox(QGeoCoordinate(top > bottom? top : bottom, |
| left), |
| QGeoCoordinate(top > bottom? bottom : top, |
| right)); |
| copyrightDesc.boxes << boundingBox; |
| } |
| copyrightDescList << copyrightDesc; |
| } |
| m_copyrights[it.key()] = copyrightDescList; |
| } |
| } |
| |
| void QGeoTiledMappingManagerEngineNokia::parseNewVersionInfo(const QByteArray &versionData) |
| { |
| const QString versionString = QString::fromUtf8(versionData); |
| |
| const QStringList versionLines = versionString.split(QLatin1Char('\n')); |
| QJsonObject newVersionData; |
| foreach (const QString &line, versionLines) { |
| const QStringList versionInfo = line.split(':'); |
| if (versionInfo.size() > 1) { |
| const QString versionKey = versionInfo[0].trimmed(); |
| const QString versionValue = versionInfo[1].trimmed(); |
| if (!versionKey.isEmpty() && !versionValue.isEmpty()) { |
| newVersionData[versionKey] = versionValue; |
| } |
| } |
| } |
| |
| updateVersion(newVersionData); |
| } |
| |
| void QGeoTiledMappingManagerEngineNokia::updateVersion(const QJsonObject &newVersionData) { |
| |
| if (m_mapVersion.isNewVersion(newVersionData)) { |
| |
| m_mapVersion.setVersionData(newVersionData); |
| m_mapVersion.setVersion(m_mapVersion.version() + 1); |
| |
| saveMapVersion(); |
| setTileVersion(m_mapVersion.version()); |
| } |
| } |
| |
| void QGeoTiledMappingManagerEngineNokia::saveMapVersion() |
| { |
| QDir saveDir(m_cacheDirectory); |
| QFile saveFile(saveDir.filePath(QStringLiteral("here_version"))); |
| |
| if (!saveFile.open(QIODevice::WriteOnly)) { |
| qWarning("Failed to write here/nokia map version."); |
| return; |
| } |
| |
| saveFile.write(m_mapVersion.toJson()); |
| saveFile.close(); |
| } |
| |
| void QGeoTiledMappingManagerEngineNokia::loadMapVersion() |
| { |
| QDir saveDir(m_cacheDirectory); |
| QFile loadFile(saveDir.filePath(QStringLiteral("here_version"))); |
| |
| if (!loadFile.open(QIODevice::ReadOnly)) { |
| qWarning("Failed to read here/nokia map version."); |
| return; |
| } |
| |
| QByteArray saveData = loadFile.readAll(); |
| loadFile.close(); |
| |
| QJsonDocument doc(QJsonDocument::fromJson(saveData)); |
| |
| QJsonObject object = doc.object(); |
| |
| m_mapVersion.setVersion(object[QStringLiteral("version")].toInt()); |
| m_mapVersion.setVersionData(object[QStringLiteral("data")].toObject()); |
| setTileVersion(m_mapVersion.version()); |
| } |
| |
| QString QGeoTiledMappingManagerEngineNokia::evaluateCopyrightsText(const QGeoMapType mapType, |
| const qreal zoomLevel, |
| const QSet<QGeoTileSpec> &tiles) |
| { |
| static const QChar copyrightSymbol(0x00a9); |
| typedef QSet<QGeoTileSpec>::const_iterator tile_iter; |
| QGeoRectangle viewport; |
| double viewX0, viewY0, viewX1, viewY1; |
| |
| tile_iter tile = tiles.constBegin(); |
| tile_iter lastTile = tiles.constEnd(); |
| |
| if (tiles.count()) { |
| double divFactor = qPow(2.0, tile->zoom()); |
| viewX0 = viewX1 = tile->x(); |
| viewY0 = viewY1 = tile->y(); |
| |
| // this approach establishes a geo-bounding box from passed tiles to test for intersecition |
| // with copyrights boxes. |
| int numTiles = 0; |
| for (; tile != lastTile; ++tile) { |
| if (tile->x() < viewX0) |
| viewX0 = tile->x(); |
| if (tile->x() > viewX1) |
| viewX1 = tile->x(); |
| if (tile->y() < viewY0) |
| viewY0 = tile->y(); |
| if (tile->y() > viewY1) |
| viewY1 = tile->y(); |
| numTiles++; |
| } |
| |
| viewX1++; |
| viewY1++; |
| |
| QDoubleVector2D pt; |
| |
| pt.setX(viewX0 / divFactor); |
| pt.setY(viewY0 / divFactor); |
| viewport.setTopLeft(QWebMercator::mercatorToCoord(pt)); |
| pt.setX(viewX1 / divFactor); |
| pt.setY(viewY1 / divFactor); |
| viewport.setBottomRight(QWebMercator::mercatorToCoord(pt)); |
| } |
| |
| // TODO: the following invalidation detection algorithm may be improved later. |
| QList<CopyrightDesc> descriptorList = m_copyrights[ getBaseScheme(mapType.mapId()) ]; |
| CopyrightDesc *descriptor; |
| int descIndex, boxIndex; |
| QString copyrightsText; |
| QSet<QString> copyrightStrings; |
| |
| for (descIndex = 0; descIndex < descriptorList.count(); descIndex++) { |
| if (descriptorList[descIndex].minLevel <= zoomLevel && zoomLevel <= descriptorList[descIndex].maxLevel) { |
| descriptor = &descriptorList[descIndex]; |
| |
| for (boxIndex = 0; boxIndex < descriptor->boxes.count(); boxIndex++) { |
| QGeoRectangle box = descriptor->boxes[boxIndex]; |
| |
| if (box.intersects(viewport)) { |
| copyrightStrings.insert(descriptor->label); |
| break; |
| } |
| } |
| if (!descriptor->boxes.count()) |
| copyrightStrings.insert(descriptor->label); |
| } |
| } |
| |
| foreach (const QString ©rightString, copyrightStrings) { |
| if (copyrightsText.length()) |
| copyrightsText += QLatin1Char('\n'); |
| copyrightsText += copyrightSymbol; |
| copyrightsText += copyrightString; |
| } |
| |
| return copyrightsText; |
| } |
| |
| QGeoMap *QGeoTiledMappingManagerEngineNokia::createMap() |
| { |
| QGeoTiledMap *map = new QGeoTiledMapNokia(this); |
| map->setPrefetchStyle(m_prefetchStyle); |
| return map; |
| } |
| |
| QT_END_NAMESPACE |
| |