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 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVariantMap ¶meters, 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 | |
339 | QGeoTiledMappingManagerEngineOsm::~QGeoTiledMappingManagerEngineOsm() |
340 | { |
341 | } |
342 | |
343 | QGeoMap *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 | |
352 | const QVector<QGeoTileProviderOsm *> &QGeoTiledMappingManagerEngineOsm::providers() |
353 | { |
354 | return m_providers; |
355 | } |
356 | |
357 | QString QGeoTiledMappingManagerEngineOsm::customCopyright() const |
358 | { |
359 | return m_customCopyright; |
360 | } |
361 | |
362 | void QGeoTiledMappingManagerEngineOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider) |
363 | { |
364 | if (!provider->isResolved()) |
365 | return; |
366 | updateMapTypes(); |
367 | } |
368 | |
369 | void QGeoTiledMappingManagerEngineOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider) |
370 | { |
371 | if (!provider->isResolved()) |
372 | return; |
373 | updateMapTypes(); |
374 | } |
375 | |
376 | void 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 | |
390 | QT_END_NAMESPACE |
391 | |