1/****************************************************************************
2**
3** Copyright (C) 2016 Aaron McCarthy <mccarthy.aaron@gmail.com>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtLocation module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qgeotiledmappingmanagerengineosm.h"
41#include "qgeotilefetcherosm.h"
42#include "qgeotiledmaposm.h"
43#include "qgeofiletilecacheosm.h"
44
45#include <QtLocation/private/qgeocameracapabilities_p.h>
46#include <QtLocation/private/qgeomaptype_p.h>
47#include <QtLocation/private/qgeotiledmap_p.h>
48#include <QtLocation/private/qgeofiletilecache_p.h>
49
50#include <QtNetwork/QNetworkAccessManager>
51#include <QtNetwork/QNetworkDiskCache>
52
53QT_BEGIN_NAMESPACE
54
55QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString)
56: QGeoTiledMappingManagerEngine()
57{
58 QGeoCameraCapabilities cameraCaps;
59 cameraCaps.setMinimumZoomLevel(0.0);
60 cameraCaps.setMaximumZoomLevel(19.0);
61 cameraCaps.setSupportsBearing(true);
62 cameraCaps.setSupportsTilting(true);
63 cameraCaps.setMinimumTilt(0);
64 cameraCaps.setMaximumTilt(80);
65 cameraCaps.setMinimumFieldOfView(20.0);
66 cameraCaps.setMaximumFieldOfView(120.0);
67 cameraCaps.setOverzoomEnabled(true);
68 setCameraCapabilities(cameraCaps);
69
70 setTileSize(QSize(256, 256));
71
72 const QByteArray pluginName = "osm";
73 if (parameters.contains(QStringLiteral("osm.mapping.cache.directory"))) {
74 m_cacheDirectory = parameters.value(QStringLiteral("osm.mapping.cache.directory")).toString();
75 } else {
76 // managerName() is not yet set, we have to hardcode the plugin name below
77 m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String(pluginName);
78 }
79 QNetworkAccessManager *nmCached = new QNetworkAccessManager(this);
80 QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
81 diskCache->setCacheDirectory(m_cacheDirectory + QLatin1String("/providers"));
82 diskCache->setMaximumCacheSize(100000000000); // enough to prevent diskCache to fiddle with tile cache. it's anyway used only for providers.
83 nmCached->setCache(diskCache);
84
85 QNetworkAccessManager *nm = new QNetworkAccessManager(); // Gets owned by QGeoTileFetcherOsm
86 QString domain = QStringLiteral("http://maps-redirect.qt.io/osm/5.8/");
87 if (parameters.contains(QStringLiteral("osm.mapping.providersrepository.address"))) {
88 QString customAddress = parameters.value(QStringLiteral("osm.mapping.providersrepository.address")).toString();
89 // Allowing some malformed addresses
90 if (customAddress.indexOf(QStringLiteral(":")) < 0) // defaulting to http:// if no prefix is found
91 customAddress = QStringLiteral("http://") + customAddress;
92 if (customAddress[customAddress.length()-1] != QLatin1Char('/'))
93 customAddress += QLatin1Char('/');
94 if (QUrl(customAddress).isValid())
95 domain = customAddress;
96 else
97 qWarning() << "Invalid custom providers repository address: " << customAddress;
98 }
99
100 bool highdpi = false;
101 if (parameters.contains(QStringLiteral("osm.mapping.highdpi_tiles"))) {
102 const QString param = parameters.value(QStringLiteral("osm.mapping.highdpi_tiles")).toString().toLower();
103 if (param == "true")
104 highdpi = true;
105 }
106
107 /* TileProviders setup */
108 QVector<TileProvider *> providers_street;
109 QVector<TileProvider *> providers_satellite;
110 QVector<TileProvider *> providers_cycle;
111 QVector<TileProvider *> providers_transit;
112 QVector<TileProvider *> providers_nighttransit;
113 QVector<TileProvider *> providers_terrain;
114 QVector<TileProvider *> providers_hiking;
115 if (highdpi) {
116 providers_street.push_back(t: new TileProvider(domain + "street-hires", true));
117 providers_satellite.push_back(t: new TileProvider(domain + "satellite-hires", true));
118 providers_cycle.push_back(t: new TileProvider(domain + "cycle-hires", true));
119 providers_transit.push_back(t: new TileProvider(domain + "transit-hires", true));
120 providers_nighttransit.push_back(t: new TileProvider(domain + "night-transit-hires", true));
121 providers_terrain.push_back(t: new TileProvider(domain + "terrain-hires", true));
122 providers_hiking.push_back(t: new TileProvider(domain + "hiking-hires", true));
123 }
124 providers_street.push_back(t: new TileProvider(domain + "street"));
125 providers_satellite.push_back(t: new TileProvider(domain + "satellite"));
126 providers_cycle.push_back(t: new TileProvider(domain + "cycle"));
127 providers_transit.push_back(t: new TileProvider(domain + "transit"));
128 providers_nighttransit.push_back(t: new TileProvider(domain + "night-transit"));
129 providers_terrain.push_back(t: new TileProvider(domain + "terrain"));
130 providers_hiking.push_back(t: new TileProvider(domain + "hiking"));
131 // Backups
132 const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), f: Qt::ISODate);
133 providers_street.push_back(
134 t: new TileProvider(QStringLiteral("http://c.tile.openstreetmap.org/%z/%x/%y.png"),
135 QStringLiteral("png"),
136 QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap.org</a>"),
137 QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")));
138 providers_street.back()->setTimestamp(defaultTs);
139
140 // No available open access satellite backup with satisfactory level of details at the present.
141
142 providers_cycle.push_back(
143 t: new TileProvider(QStringLiteral("http://c.tile.opencyclemap.org/cycle/%z/%x/%y.png"),
144 QStringLiteral("png"),
145 QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
146 QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")));
147 providers_cycle.back()->setTimestamp(defaultTs);
148
149 providers_transit.push_back(
150 t: new TileProvider(QStringLiteral("http://c.tile2.opencyclemap.org/transport/%z/%x/%y.png"),
151 QStringLiteral("png"),
152 QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
153 QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")));
154 providers_transit.back()->setTimestamp(defaultTs);
155
156 providers_nighttransit.push_back(
157 t: new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/transport-dark/%z/%x/%y.png"),
158 QStringLiteral("png"),
159 QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
160 QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")) );
161 providers_nighttransit.back()->setTimestamp(defaultTs);
162
163 providers_terrain.push_back(
164 t: new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/landscape/%z/%x/%y.png"),
165 QStringLiteral("png"),
166 QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
167 QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")));
168 providers_terrain.back()->setTimestamp(defaultTs);
169
170 providers_hiking.push_back(
171 t: new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/outdoors/%z/%x/%y.png"),
172 QStringLiteral("png"),
173 QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"),
174 QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")));
175 providers_hiking.back()->setTimestamp(defaultTs);
176
177
178 /* QGeoTileProviderOsms setup */
179 m_providers.push_back( t: new QGeoTileProviderOsm( nmCached,
180 QGeoMapType(QGeoMapType::StreetMap, tr(s: "Street Map"), tr(s: "Street map view in daylight mode"), false, false, 1, pluginName, cameraCaps),
181 providers_street, cameraCaps ));
182 m_providers.push_back( t: new QGeoTileProviderOsm( nmCached,
183 QGeoMapType(QGeoMapType::SatelliteMapDay, tr(s: "Satellite Map"), tr(s: "Satellite map view in daylight mode"), false, false, 2, pluginName, cameraCaps),
184 providers_satellite, cameraCaps ));
185 m_providers.push_back( t: new QGeoTileProviderOsm( nmCached,
186 QGeoMapType(QGeoMapType::CycleMap, tr(s: "Cycle Map"), tr(s: "Cycle map view in daylight mode"), false, false, 3, pluginName, cameraCaps),
187 providers_cycle, cameraCaps ));
188 m_providers.push_back( t: new QGeoTileProviderOsm( nmCached,
189 QGeoMapType(QGeoMapType::TransitMap, tr(s: "Transit Map"), tr(s: "Public transit map view in daylight mode"), false, false, 4, pluginName, cameraCaps),
190 providers_transit, cameraCaps ));
191 m_providers.push_back( t: new QGeoTileProviderOsm( nmCached,
192 QGeoMapType(QGeoMapType::TransitMap, tr(s: "Night Transit Map"), tr(s: "Public transit map view in night mode"), false, true, 5, pluginName, cameraCaps),
193 providers_nighttransit, cameraCaps ));
194 m_providers.push_back( t: new QGeoTileProviderOsm( nmCached,
195 QGeoMapType(QGeoMapType::TerrainMap, tr(s: "Terrain Map"), tr(s: "Terrain map view"), false, false, 6, pluginName, cameraCaps),
196 providers_terrain, cameraCaps ));
197 m_providers.push_back( t: new QGeoTileProviderOsm( nmCached,
198 QGeoMapType(QGeoMapType::PedestrianMap, tr(s: "Hiking Map"), tr(s: "Hiking map view"), false, false, 7, pluginName, cameraCaps),
199 providers_hiking, cameraCaps ));
200
201 if (parameters.contains(QStringLiteral("osm.mapping.custom.host"))
202 || parameters.contains(QStringLiteral("osm.mapping.host"))) {
203 // Adding a custom provider
204 QString tmsServer;
205 if (parameters.contains(QStringLiteral("osm.mapping.host")))
206 tmsServer = parameters.value(QStringLiteral("osm.mapping.host")).toString();
207 if (parameters.contains(QStringLiteral("osm.mapping.custom.host"))) // priority to the new one
208 tmsServer = parameters.value(QStringLiteral("osm.mapping.custom.host")).toString();
209
210 QString mapCopyright;
211 QString dataCopyright;
212 if (parameters.contains(QStringLiteral("osm.mapping.custom.mapcopyright")))
213 mapCopyright = parameters.value(QStringLiteral("osm.mapping.custom.mapcopyright")).toString();
214 if (parameters.contains(QStringLiteral("osm.mapping.custom.datacopyright")))
215 dataCopyright = parameters.value(QStringLiteral("osm.mapping.custom.datacopyright")).toString();
216
217 if (parameters.contains(QStringLiteral("osm.mapping.copyright")))
218 m_customCopyright = parameters.value(QStringLiteral("osm.mapping.copyright")).toString();
219
220 m_providers.push_back(
221 t: new QGeoTileProviderOsm( nmCached,
222 QGeoMapType(QGeoMapType::CustomMap, tr(s: "Custom URL Map"), tr(s: "Custom url map view set via urlprefix parameter"), false, false, 8, pluginName, cameraCaps),
223 { new TileProvider(tmsServer + QStringLiteral("%z/%x/%y.png"),
224 QStringLiteral("png"),
225 mapCopyright,
226 dataCopyright) }, cameraCaps
227 ));
228
229 m_providers.last()->disableRedirection();
230 }
231
232 bool disableRedirection = false;
233 if (parameters.contains(QStringLiteral("osm.mapping.providersrepository.disabled")))
234 disableRedirection = parameters.value(QStringLiteral("osm.mapping.providersrepository.disabled")).toBool();
235
236 for (QGeoTileProviderOsm * provider: qAsConst(t&: m_providers)) {
237 // Providers are parented inside QGeoFileTileCacheOsm, as they are used in its destructor.
238 if (disableRedirection) {
239 provider->disableRedirection();
240 } else {
241 connect(sender: provider, signal: &QGeoTileProviderOsm::resolutionFinished,
242 receiver: this, slot: &QGeoTiledMappingManagerEngineOsm::onProviderResolutionFinished);
243 connect(sender: provider, signal: &QGeoTileProviderOsm::resolutionError,
244 receiver: this, slot: &QGeoTiledMappingManagerEngineOsm::onProviderResolutionError);
245 }
246 }
247 updateMapTypes();
248
249
250 /* TILE CACHE */
251 if (parameters.contains(QStringLiteral("osm.mapping.offline.directory")))
252 m_offlineDirectory = parameters.value(QStringLiteral("osm.mapping.offline.directory")).toString();
253 QGeoFileTileCacheOsm *tileCache = new QGeoFileTileCacheOsm(m_providers, m_offlineDirectory, m_cacheDirectory);
254
255 /*
256 * Disk cache setup -- defaults to ByteSize (old behavior)
257 */
258 if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.cost_strategy"))) {
259 QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.disk.cost_strategy")).toString().toLower();
260 if (cacheStrategy == QLatin1String("bytesize"))
261 tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize);
262 else
263 tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary);
264 } else {
265 tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize);
266 }
267 if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.size"))) {
268 bool ok = false;
269 int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.disk.size")).toString().toInt(ok: &ok);
270 if (ok)
271 tileCache->setMaxDiskUsage(cacheSize);
272 }
273
274 /*
275 * Memory cache setup -- defaults to ByteSize (old behavior)
276 */
277 if (parameters.contains(QStringLiteral("osm.mapping.cache.memory.cost_strategy"))) {
278 QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.memory.cost_strategy")).toString().toLower();
279 if (cacheStrategy == QLatin1String("bytesize"))
280 tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize);
281 else
282 tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary);
283 } else {
284 tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize);
285 }
286 if (parameters.contains(QStringLiteral("osm.mapping.cache.memory.size"))) {
287 bool ok = false;
288 int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.memory.size")).toString().toInt(ok: &ok);
289 if (ok)
290 tileCache->setMaxMemoryUsage(cacheSize);
291 }
292
293 /*
294 * Texture cache setup -- defaults to ByteSize (old behavior)
295 */
296 if (parameters.contains(QStringLiteral("osm.mapping.cache.texture.cost_strategy"))) {
297 QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.texture.cost_strategy")).toString().toLower();
298 if (cacheStrategy == QLatin1String("bytesize"))
299 tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize);
300 else
301 tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary);
302 } else {
303 tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize);
304 }
305 if (parameters.contains(QStringLiteral("osm.mapping.cache.texture.size"))) {
306 bool ok = false;
307 int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.texture.size")).toString().toInt(ok: &ok);
308 if (ok)
309 tileCache->setExtraTextureUsage(cacheSize);
310 }
311
312
313 setTileCache(tileCache);
314
315
316 /* TILE FETCHER */
317 QGeoTileFetcherOsm *tileFetcher = new QGeoTileFetcherOsm(m_providers, nm, this);
318 if (parameters.contains(QStringLiteral("osm.useragent"))) {
319 const QByteArray ua = parameters.value(QStringLiteral("osm.useragent")).toString().toLatin1();
320 tileFetcher->setUserAgent(ua);
321 }
322 setTileFetcher(tileFetcher);
323
324 /* PREFETCHING */
325 if (parameters.contains(QStringLiteral("osm.mapping.prefetching_style"))) {
326 const QString prefetchingMode = parameters.value(QStringLiteral("osm.mapping.prefetching_style")).toString();
327 if (prefetchingMode == QStringLiteral("TwoNeighbourLayers"))
328 m_prefetchStyle = QGeoTiledMap::PrefetchTwoNeighbourLayers;
329 else if (prefetchingMode == QStringLiteral("OneNeighbourLayer"))
330 m_prefetchStyle = QGeoTiledMap::PrefetchNeighbourLayer;
331 else if (prefetchingMode == QStringLiteral("NoPrefetching"))
332 m_prefetchStyle = QGeoTiledMap::NoPrefetching;
333 }
334
335 *error = QGeoServiceProvider::NoError;
336 errorString->clear();
337}
338
339QGeoTiledMappingManagerEngineOsm::~QGeoTiledMappingManagerEngineOsm()
340{
341}
342
343QGeoMap *QGeoTiledMappingManagerEngineOsm::createMap()
344{
345 QGeoTiledMap *map = new QGeoTiledMapOsm(this);
346 connect(sender: qobject_cast<QGeoFileTileCacheOsm *>(object: tileCache()), signal: &QGeoFileTileCacheOsm::mapDataUpdated
347 , receiver: map, slot: &QGeoTiledMap::clearScene);
348 map->setPrefetchStyle(m_prefetchStyle);
349 return map;
350}
351
352const QVector<QGeoTileProviderOsm *> &QGeoTiledMappingManagerEngineOsm::providers()
353{
354 return m_providers;
355}
356
357QString QGeoTiledMappingManagerEngineOsm::customCopyright() const
358{
359 return m_customCopyright;
360}
361
362void QGeoTiledMappingManagerEngineOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider)
363{
364 if (!provider->isResolved())
365 return;
366 updateMapTypes();
367}
368
369void QGeoTiledMappingManagerEngineOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider)
370{
371 if (!provider->isResolved())
372 return;
373 updateMapTypes();
374}
375
376void QGeoTiledMappingManagerEngineOsm::updateMapTypes()
377{
378 QList<QGeoMapType> mapTypes;
379 foreach (QGeoTileProviderOsm * provider, m_providers) {
380 // assume provider are ok until they have been resolved invalid
381 if (!provider->isResolved() || provider->isValid())
382 mapTypes << provider->mapType();
383 }
384 const QList<QGeoMapType> currentlySupportedMapTypes = supportedMapTypes();
385 if (currentlySupportedMapTypes != mapTypes)
386 // See map type implementations in QGeoTiledMapOsm and QGeoTileFetcherOsm.
387 setSupportedMapTypes(mapTypes);
388}
389
390QT_END_NAMESPACE
391

source code of qtlocation/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp