1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
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 General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | //TESTED_COMPONENT=src/location/maps |
30 | |
31 | #include "qgeotilespec_p.h" |
32 | #include "qgeotiledmapscene_p.h" |
33 | #include "qgeocameratiles_p.h" |
34 | #include "qgeocameradata_p.h" |
35 | #include "qabstractgeotilecache_p.h" |
36 | #include <QtLocation/private/qgeoprojection_p.h> |
37 | #include <QtPositioning/private/qwebmercator_p.h> |
38 | #include <QtPositioning/private/qdoublevector2d_p.h> |
39 | |
40 | #include <qtest.h> |
41 | |
42 | #include <QList> |
43 | #include <QPair> |
44 | #include <QDebug> |
45 | |
46 | #include <cmath> |
47 | |
48 | QT_USE_NAMESPACE |
49 | |
50 | class tst_QGeoTiledMapScene : public QObject |
51 | { |
52 | Q_OBJECT |
53 | |
54 | private: |
55 | void row(QString name, double screenX, double screenX2, double screenY, double cameraCenterX, double cameraCenterY, |
56 | double zoom, int tileSize, int screenWidth, int screenHeight, double mercatorX, double mercatorY){ |
57 | |
58 | // expected behaviour of wrapping |
59 | if (mercatorX <= 0.0) |
60 | mercatorX += 1.0; |
61 | else if (mercatorX > 1.0) |
62 | mercatorX -= 1.0; |
63 | |
64 | QTest::newRow(qPrintable(name)) |
65 | << screenX << screenX2 << screenY |
66 | << cameraCenterX << cameraCenterY |
67 | << zoom << tileSize |
68 | << screenWidth << screenHeight |
69 | << mercatorX |
70 | << mercatorY; |
71 | } |
72 | |
73 | void screenPositions(QString name, double cameraCenterX, double cameraCenterY, double zoom, |
74 | int tileSize, int screenWidth, int screenHeight) |
75 | { |
76 | double screenX; |
77 | double screenX2; |
78 | double screenY; |
79 | double mercatorX; |
80 | double mercatorY; |
81 | |
82 | double halfLength = 1 / (std::pow(x: 2.0, y: zoom) * 2); |
83 | double scaleX = screenWidth / tileSize; |
84 | double scaleY = screenHeight / tileSize; |
85 | double scaledHalfLengthX = halfLength * scaleX; |
86 | double scaledHalfLengthY = halfLength * scaleY; |
87 | |
88 | bool matchingMapEnds = false; |
89 | if (screenWidth == std::pow(x: 2.0, y: zoom) * tileSize) |
90 | matchingMapEnds = true; |
91 | |
92 | // bottom left |
93 | screenX = screenX2 = 0.0; |
94 | if (matchingMapEnds) |
95 | screenX2 = qAbs(t: screenX - 1.0) * screenWidth; |
96 | screenY = 1.0 * screenHeight; |
97 | mercatorX = cameraCenterX - scaledHalfLengthX; |
98 | mercatorY = cameraCenterY + scaledHalfLengthY; |
99 | row (name: name + QString("_bottomLeftScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
100 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
101 | |
102 | // bottom |
103 | screenX = screenX2 = 0.5 * screenWidth; |
104 | screenY = 1.0 * screenHeight; |
105 | mercatorX = cameraCenterX; |
106 | mercatorY = cameraCenterY + scaledHalfLengthY; |
107 | row (name: name + QString("_bottomScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
108 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
109 | |
110 | // bottom right |
111 | screenX = screenX2 = 1.0 * screenWidth; |
112 | if (matchingMapEnds) |
113 | screenX2 = qAbs(t: screenX - 1.0) * screenWidth; |
114 | screenY = 1.0 * screenHeight; |
115 | mercatorX = cameraCenterX + scaledHalfLengthX; |
116 | mercatorY = cameraCenterY + scaledHalfLengthY; |
117 | row (name: name + QString("_bottomRightScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
118 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
119 | |
120 | // left |
121 | screenX = screenX2 = 0.0 * screenWidth; |
122 | if (matchingMapEnds) |
123 | screenX2 = qAbs(t: screenX - 1.0) * screenWidth; |
124 | screenY = 0.5 * screenHeight; |
125 | mercatorX = cameraCenterX - scaledHalfLengthX; |
126 | mercatorY = cameraCenterY; |
127 | row (name: name + QString("_leftScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
128 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
129 | |
130 | // center |
131 | screenX = screenX2 = 0.5 * screenWidth; |
132 | screenY = 0.5 * screenHeight; |
133 | mercatorX = cameraCenterX; |
134 | mercatorY = cameraCenterY; |
135 | row (name: name + QString("_centerScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
136 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
137 | |
138 | // right |
139 | screenX = screenX2 = 1.0 * screenWidth; |
140 | if (matchingMapEnds) |
141 | screenX2 = qAbs(t: screenX - 1.0) * screenWidth; |
142 | screenY = 0.5 * screenHeight; |
143 | mercatorX = cameraCenterX + scaledHalfLengthX; |
144 | mercatorY = cameraCenterY; |
145 | row (name: name + QString("_rightScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
146 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
147 | |
148 | // top left |
149 | screenX = screenX2 = 0.0; |
150 | if (matchingMapEnds) |
151 | screenX2 = qAbs(t: screenX - 1.0) * screenWidth; |
152 | screenY = 0.0; |
153 | mercatorX = cameraCenterX - scaledHalfLengthX; |
154 | mercatorY = cameraCenterY - scaledHalfLengthY; |
155 | row (name: name + QString("_topLeftScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
156 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
157 | |
158 | // top |
159 | screenX = screenX2 = 0.5 * screenWidth; |
160 | screenY = 0.0; |
161 | mercatorX = cameraCenterX; |
162 | mercatorY = cameraCenterY - scaledHalfLengthY; |
163 | row (name: name + QString("_topScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
164 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
165 | |
166 | // top right |
167 | screenX = screenX2 = 1.0 * screenWidth; |
168 | if (matchingMapEnds) |
169 | screenX2 = qAbs(t: screenX - 1.0) * screenWidth; |
170 | screenY = 0.0; |
171 | mercatorX = cameraCenterX + scaledHalfLengthX; |
172 | mercatorY = cameraCenterY - scaledHalfLengthY; |
173 | row (name: name + QString("_topRightScreen" ), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, |
174 | zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); |
175 | |
176 | } |
177 | |
178 | void screenCameraPositions(QString name, double zoom, int tileSize, |
179 | int screenWidth, int screenHeight) |
180 | { |
181 | double cameraCenterX; |
182 | double cameraCenterY; |
183 | |
184 | // bottom left |
185 | cameraCenterX = 0; |
186 | cameraCenterY = 1.0; |
187 | screenPositions(name: name + QString("_bottomLeftCamera" ), cameraCenterX, cameraCenterY, |
188 | zoom, tileSize, screenWidth, screenHeight); |
189 | |
190 | // bottom |
191 | cameraCenterX = 0.5; |
192 | cameraCenterY = 1.0; |
193 | screenPositions(name: name + QString("_bottomCamera" ), cameraCenterX, cameraCenterY, |
194 | zoom, tileSize, screenWidth, screenHeight); |
195 | // bottom right |
196 | cameraCenterX = 1.0; |
197 | cameraCenterY = 1.0; |
198 | screenPositions(name: name + QString("_bottomRightCamera" ), cameraCenterX, cameraCenterY, |
199 | zoom, tileSize, screenWidth, screenHeight); |
200 | // left |
201 | cameraCenterX = 0.0; |
202 | cameraCenterY = 0.5; |
203 | screenPositions(name: name + QString("_leftCamera" ), cameraCenterX, cameraCenterY, |
204 | zoom, tileSize, screenWidth, screenHeight); |
205 | // middle |
206 | cameraCenterX = 0.5; |
207 | cameraCenterY = 0.5; |
208 | screenPositions(name: name + QString("_middleCamera" ), cameraCenterX, cameraCenterY, |
209 | zoom, tileSize, screenWidth, screenHeight); |
210 | // right |
211 | cameraCenterX = 1.0; |
212 | cameraCenterY = 0.5; |
213 | screenPositions(name: name + QString("_rightCamera" ), cameraCenterX, cameraCenterY, |
214 | zoom, tileSize, screenWidth, screenHeight); |
215 | // top left |
216 | cameraCenterX = 0.0; |
217 | cameraCenterY = 0.0; |
218 | screenPositions(name: name + QString("_topLeftCamera" ), cameraCenterX, cameraCenterY, |
219 | zoom, tileSize, screenWidth, screenHeight); |
220 | // top |
221 | cameraCenterX = 0.5; |
222 | cameraCenterY = 0.0; |
223 | screenPositions(name: name + QString("_topCamera" ), cameraCenterX, cameraCenterY, |
224 | zoom, tileSize, screenWidth, screenHeight); |
225 | // top right |
226 | cameraCenterX = 1.0; |
227 | cameraCenterY = 0.0; |
228 | screenPositions(name: name + QString("_topRightCamera" ), cameraCenterX, cameraCenterY, |
229 | zoom, tileSize, screenWidth, screenHeight); |
230 | } |
231 | |
232 | void populateScreenMercatorData(){ |
233 | QTest::addColumn<double>(name: "screenX" ); |
234 | QTest::addColumn<double>(name: "screenX2" ); |
235 | QTest::addColumn<double>(name: "screenY" ); |
236 | QTest::addColumn<double>(name: "cameraCenterX" ); |
237 | QTest::addColumn<double>(name: "cameraCenterY" ); |
238 | QTest::addColumn<double>(name: "zoom" ); |
239 | QTest::addColumn<int>(name: "tileSize" ); |
240 | QTest::addColumn<int>(name: "screenWidth" ); |
241 | QTest::addColumn<int>(name: "screenHeight" ); |
242 | QTest::addColumn<double>(name: "mercatorX" ); |
243 | QTest::addColumn<double>(name: "mercatorY" ); |
244 | |
245 | int tileSize; |
246 | double zoom; |
247 | int screenWidth; |
248 | int screenHeight; |
249 | QString name; |
250 | tileSize = 256; |
251 | zoom = 1.0; // 4 tiles in the map. map size = 2*tileSize x 2*tileSize |
252 | |
253 | /* |
254 | ScreenWidth = t |
255 | ScreenHeight = t |
256 | */ |
257 | screenWidth = tileSize; |
258 | screenHeight = tileSize; |
259 | name = QString("_(t x t)" ); |
260 | screenCameraPositions(name, zoom, tileSize, screenWidth, screenHeight); |
261 | |
262 | /* |
263 | ScreenWidth = t * 2 |
264 | ScreenHeight = t |
265 | */ |
266 | screenWidth = tileSize * 2; |
267 | screenHeight = tileSize; |
268 | name = QString("_(2t x t)" ); |
269 | screenCameraPositions(name, zoom, tileSize, screenWidth, screenHeight); |
270 | |
271 | /* |
272 | ScreenWidth = t |
273 | ScreenHeight = t * 2 |
274 | */ |
275 | screenWidth = tileSize; |
276 | screenHeight = tileSize * 2; |
277 | name = QString("_(2t x t)" ); |
278 | screenCameraPositions(name, zoom, tileSize, screenWidth, screenHeight); |
279 | |
280 | |
281 | /* |
282 | Screen Width = t * 2 |
283 | Screen Height = t * 2 |
284 | */ |
285 | screenWidth = tileSize * 2; |
286 | screenHeight = tileSize * 2; |
287 | name = QString("_(2t x 2t)" ); |
288 | screenCameraPositions(name, zoom, tileSize, screenWidth, screenHeight); |
289 | } |
290 | |
291 | // Calculates the distance in mercator space of 2 x coordinates, assuming that 1 == 0 |
292 | double wrappedMercatorDistance(double x1, double x2) |
293 | { |
294 | return qMin(a: qMin(a: qAbs(t: x1 - 1.0 - x2), b: qAbs(t: x1 - x2)), b: qAbs(t: x1 + 1.0 - x2)); |
295 | } |
296 | |
297 | private slots: |
298 | void screenToMercatorPositions(){ |
299 | QFETCH(double, screenX); |
300 | QFETCH(double, screenY); |
301 | QFETCH(double, cameraCenterX); |
302 | QFETCH(double, cameraCenterY); |
303 | QFETCH(double, zoom); |
304 | QFETCH(int, tileSize); |
305 | QFETCH(int, screenWidth); |
306 | QFETCH(int, screenHeight); |
307 | QFETCH(double, mercatorX); |
308 | QFETCH(double, mercatorY); |
309 | |
310 | QGeoCameraData camera; |
311 | camera.setZoomLevel(zoom); |
312 | QGeoCoordinate centerCoordinate = QWebMercator::mercatorToCoord(mercator: QDoubleVector2D(cameraCenterX, cameraCenterY)); |
313 | camera.setCenter(centerCoordinate); |
314 | |
315 | QGeoCameraTiles ct; |
316 | ct.setTileSize(tileSize); |
317 | ct.setCameraData(camera); |
318 | ct.setScreenSize(QSize(screenWidth,screenHeight)); |
319 | |
320 | QGeoTiledMapScene mapGeometry; |
321 | mapGeometry.setTileSize(tileSize); |
322 | mapGeometry.setScreenSize(QSize(screenWidth,screenHeight)); |
323 | mapGeometry.setCameraData(camera); |
324 | mapGeometry.setVisibleTiles(ct.createTiles()); |
325 | |
326 | QGeoProjectionWebMercator projection; |
327 | projection.setViewportSize(QSize(screenWidth,screenHeight)); |
328 | projection.setCameraData(cameraData: camera); |
329 | |
330 | QDoubleVector2D point(screenX,screenY); |
331 | QDoubleVector2D mercartorPos = projection.unwrapMapProjection(wrappedProjection: projection.itemPositionToWrappedMapProjection(itemPosition: point)); |
332 | |
333 | const double tolerance = 0.00000000001; // FuzzyCompare is too strict here |
334 | QVERIFY2(wrappedMercatorDistance(mercartorPos.x(), mercatorX) < tolerance, |
335 | qPrintable(QString("Accepted: %1 , Actual: %2" ) |
336 | .arg(QString::number(mercatorX)) |
337 | .arg(QString::number(mercartorPos.x())))); |
338 | QVERIFY2(qAbs(mercartorPos.y() - mercatorY) < tolerance, |
339 | qPrintable(QString("Accepted: %1 , Actual: %2" ) |
340 | .arg(QString::number(mercatorY)) |
341 | .arg(QString::number(mercartorPos.y())))); |
342 | } |
343 | |
344 | void screenToMercatorPositions_data() |
345 | { |
346 | populateScreenMercatorData(); |
347 | } |
348 | |
349 | void mercatorToScreenPositions(){ |
350 | QFETCH(double, screenX); |
351 | QFETCH(double, screenX2); |
352 | QFETCH(double, screenY); |
353 | QFETCH(double, cameraCenterX); |
354 | QFETCH(double, cameraCenterY); |
355 | QFETCH(double, zoom); |
356 | QFETCH(int, tileSize); |
357 | QFETCH(int, screenWidth); |
358 | QFETCH(int, screenHeight); |
359 | QFETCH(double, mercatorX); |
360 | QFETCH(double, mercatorY); |
361 | |
362 | QGeoCameraData camera; |
363 | camera.setZoomLevel(zoom); |
364 | QGeoCoordinate coord = QWebMercator::mercatorToCoord(mercator: QDoubleVector2D(cameraCenterX, cameraCenterY)); |
365 | camera.setCenter(coord); |
366 | |
367 | QGeoCameraTiles ct; |
368 | ct.setTileSize(tileSize); |
369 | ct.setCameraData(camera); |
370 | ct.setScreenSize(QSize(screenWidth,screenHeight)); |
371 | |
372 | QGeoTiledMapScene mapGeometry; |
373 | mapGeometry.setTileSize(tileSize); |
374 | mapGeometry.setScreenSize(QSize(screenWidth,screenHeight)); |
375 | mapGeometry.setCameraData(camera); |
376 | mapGeometry.setVisibleTiles(ct.createTiles()); |
377 | |
378 | QGeoProjectionWebMercator projection; |
379 | projection.setViewportSize(QSize(screenWidth,screenHeight)); |
380 | projection.setCameraData(cameraData: camera); |
381 | |
382 | QDoubleVector2D mercatorPos(mercatorX, mercatorY); |
383 | QPointF point = projection.wrappedMapProjectionToItemPosition(wrappedProjection: projection.wrapMapProjection(projection: mercatorPos)).toPointF(); |
384 | |
385 | QVERIFY2((point.x() == screenX) || (point.x() == screenX2), |
386 | qPrintable(QString("Accepted: { %1 , %2 } Actual: %3" ) |
387 | .arg(QString::number(screenX)) |
388 | .arg(QString::number(screenX2)) |
389 | .arg(QString::number(point.x())))); |
390 | QCOMPARE(point.y(), screenY); |
391 | } |
392 | |
393 | void mercatorToScreenPositions_data(){ |
394 | populateScreenMercatorData(); |
395 | } |
396 | |
397 | }; |
398 | |
399 | QTEST_GUILESS_MAIN(tst_QGeoTiledMapScene) |
400 | #include "tst_qgeotiledmapscene.moc" |
401 | |