1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtLocation module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
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 http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include <QtCore/QTimerEvent> |
38 | |
39 | #include "qgeomappingmanagerengine_p.h" |
40 | #include "qgeotilefetcher_p.h" |
41 | #include "qgeotilefetcher_p_p.h" |
42 | #include "qgeotiledmapreply_p.h" |
43 | #include "qgeotilespec_p.h" |
44 | #include "qgeotiledmap_p.h" |
45 | |
46 | #include <algorithm> |
47 | #include <iterator> |
48 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | QGeoTileFetcher::QGeoTileFetcher(QGeoMappingManagerEngine *parent) |
52 | : QObject(*new QGeoTileFetcherPrivate(), parent) |
53 | { |
54 | Q_D(QGeoTileFetcher); |
55 | |
56 | d->enabled_ = true; |
57 | d->engine_ = parent; |
58 | } |
59 | |
60 | QGeoTileFetcher::QGeoTileFetcher(QGeoTileFetcherPrivate &dd, QGeoMappingManagerEngine *parent) |
61 | : QObject(dd,parent) |
62 | { |
63 | Q_D(QGeoTileFetcher); |
64 | d->enabled_ = true; |
65 | d->engine_ = parent; |
66 | } |
67 | |
68 | QGeoTileFetcher::~QGeoTileFetcher() |
69 | { |
70 | } |
71 | |
72 | void QGeoTileFetcher::updateTileRequests(const QSet<QGeoTileSpec> &tilesAdded, |
73 | const QSet<QGeoTileSpec> &tilesRemoved) |
74 | { |
75 | Q_D(QGeoTileFetcher); |
76 | |
77 | QMutexLocker ml(&d->queueMutex_); |
78 | |
79 | cancelTileRequests(tiles: tilesRemoved); |
80 | |
81 | std::copy(first: tilesAdded.cbegin(), last: tilesAdded.cend(), result: std::back_inserter(x&: d->queue_)); |
82 | |
83 | if (d->enabled_ && initialized() && !d->queue_.isEmpty() && !d->timer_.isActive()) |
84 | d->timer_.start(msec: 0, obj: this); |
85 | } |
86 | |
87 | void QGeoTileFetcher::cancelTileRequests(const QSet<QGeoTileSpec> &tiles) |
88 | { |
89 | Q_D(QGeoTileFetcher); |
90 | |
91 | typedef QSet<QGeoTileSpec>::const_iterator tile_iter; |
92 | // No need to lock: called only in updateTileRequests |
93 | tile_iter tile = tiles.constBegin(); |
94 | tile_iter end = tiles.constEnd(); |
95 | for (; tile != end; ++tile) { |
96 | QGeoTiledMapReply *reply = d->invmap_.value(akey: *tile, adefaultValue: 0); |
97 | if (reply) { |
98 | d->invmap_.remove(akey: *tile); |
99 | reply->abort(); |
100 | if (reply->isFinished()) |
101 | reply->deleteLater(); |
102 | } |
103 | d->queue_.removeAll(t: *tile); |
104 | } |
105 | } |
106 | |
107 | void QGeoTileFetcher::requestNextTile() |
108 | { |
109 | Q_D(QGeoTileFetcher); |
110 | |
111 | QMutexLocker ml(&d->queueMutex_); |
112 | |
113 | if (!d->enabled_) |
114 | return; |
115 | |
116 | if (d->queue_.isEmpty()) |
117 | return; |
118 | |
119 | QGeoTileSpec ts = d->queue_.takeFirst(); |
120 | if (d->queue_.isEmpty()) |
121 | d->timer_.stop(); |
122 | |
123 | // Check against min/max zoom to prevent sending requests for not existing objects |
124 | const QGeoCameraCapabilities & cameraCaps = d->engine_->cameraCapabilities(mapId: ts.mapId()); |
125 | // the ZL in QGeoTileSpec is relative to the native tile size of the provider. |
126 | // It gets denormalized in QGeoTiledMap. |
127 | if (ts.zoom() < cameraCaps.minimumZoomLevel() || ts.zoom() > cameraCaps.maximumZoomLevel() || !fetchingEnabled()) |
128 | return; |
129 | |
130 | QGeoTiledMapReply *reply = getTileImage(spec: ts); |
131 | if (!reply) |
132 | return; |
133 | |
134 | if (reply->isFinished()) { |
135 | handleReply(reply, spec: ts); |
136 | } else { |
137 | connect(sender: reply, |
138 | SIGNAL(finished()), |
139 | receiver: this, |
140 | SLOT(finished()), |
141 | Qt::QueuedConnection); |
142 | |
143 | d->invmap_.insert(akey: ts, avalue: reply); |
144 | } |
145 | } |
146 | |
147 | void QGeoTileFetcher::finished() |
148 | { |
149 | Q_D(QGeoTileFetcher); |
150 | |
151 | QMutexLocker ml(&d->queueMutex_); |
152 | |
153 | QGeoTiledMapReply *reply = qobject_cast<QGeoTiledMapReply *>(object: sender()); |
154 | if (!reply) |
155 | return; |
156 | |
157 | QGeoTileSpec spec = reply->tileSpec(); |
158 | |
159 | if (!d->invmap_.contains(akey: spec)) { |
160 | reply->deleteLater(); |
161 | return; |
162 | } |
163 | |
164 | d->invmap_.remove(akey: spec); |
165 | |
166 | handleReply(reply, spec); |
167 | } |
168 | |
169 | void QGeoTileFetcher::timerEvent(QTimerEvent *event) |
170 | { |
171 | Q_D(QGeoTileFetcher); |
172 | if (event->timerId() != d->timer_.timerId()) { |
173 | QObject::timerEvent(event); |
174 | return; |
175 | } |
176 | |
177 | QMutexLocker ml(&d->queueMutex_); |
178 | if (d->queue_.isEmpty() || !initialized()) { |
179 | d->timer_.stop(); |
180 | return; |
181 | } |
182 | ml.unlock(); |
183 | |
184 | requestNextTile(); |
185 | } |
186 | |
187 | bool QGeoTileFetcher::initialized() const |
188 | { |
189 | return true; |
190 | } |
191 | |
192 | bool QGeoTileFetcher::fetchingEnabled() const |
193 | { |
194 | return true; |
195 | } |
196 | |
197 | void QGeoTileFetcher::handleReply(QGeoTiledMapReply *reply, const QGeoTileSpec &spec) |
198 | { |
199 | Q_D(QGeoTileFetcher); |
200 | |
201 | if (!d->enabled_) { |
202 | reply->deleteLater(); |
203 | return; |
204 | } |
205 | |
206 | if (reply->error() == QGeoTiledMapReply::NoError) { |
207 | emit tileFinished(spec, bytes: reply->mapImageData(), format: reply->mapImageFormat()); |
208 | } else { |
209 | emit tileError(spec, errorString: reply->errorString()); |
210 | } |
211 | |
212 | reply->deleteLater(); |
213 | } |
214 | |
215 | /******************************************************************************* |
216 | *******************************************************************************/ |
217 | |
218 | QGeoTileFetcherPrivate::QGeoTileFetcherPrivate() |
219 | : QObjectPrivate(), enabled_(false), engine_(0) |
220 | { |
221 | } |
222 | |
223 | QGeoTileFetcherPrivate::~QGeoTileFetcherPrivate() |
224 | { |
225 | } |
226 | |
227 | QT_END_NAMESPACE |
228 | |