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:
row(QString name,double screenX,double screenX2,double screenY,double cameraCenterX,double cameraCenterY,double zoom,int tileSize,int screenWidth,int screenHeight,double mercatorX,double mercatorY)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 
screenPositions(QString name,double cameraCenterX,double cameraCenterY,double zoom,int tileSize,int screenWidth,int screenHeight)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(2.0, 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(2.0, zoom) * tileSize)
90             matchingMapEnds = true;
91 
92         // bottom left
93         screenX = screenX2 = 0.0;
94         if (matchingMapEnds)
95             screenX2 = qAbs(screenX - 1.0) * screenWidth;
96         screenY = 1.0 * screenHeight;
97         mercatorX = cameraCenterX - scaledHalfLengthX;
98         mercatorY = cameraCenterY + scaledHalfLengthY;
99         row (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 + 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(screenX - 1.0) * screenWidth;
114         screenY = 1.0 * screenHeight;
115         mercatorX = cameraCenterX + scaledHalfLengthX;
116         mercatorY = cameraCenterY + scaledHalfLengthY;
117         row (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(screenX - 1.0) * screenWidth;
124         screenY = 0.5 * screenHeight;
125         mercatorX = cameraCenterX - scaledHalfLengthX;
126         mercatorY = cameraCenterY;
127         row (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 + 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(screenX - 1.0) * screenWidth;
142         screenY = 0.5 * screenHeight;
143         mercatorX = cameraCenterX + scaledHalfLengthX;
144         mercatorY = cameraCenterY;
145         row (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(screenX - 1.0) * screenWidth;
152         screenY = 0.0;
153         mercatorX = cameraCenterX - scaledHalfLengthX;
154         mercatorY = cameraCenterY - scaledHalfLengthY;
155         row (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 + 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(screenX - 1.0) * screenWidth;
170         screenY = 0.0;
171         mercatorX = cameraCenterX + scaledHalfLengthX;
172         mercatorY = cameraCenterY - scaledHalfLengthY;
173         row (name + QString("_topRightScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY,
174                      zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY);
175 
176     }
177 
screenCameraPositions(QString name,double zoom,int tileSize,int screenWidth,int screenHeight)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 + QString("_bottomLeftCamera"), cameraCenterX, cameraCenterY,
188                         zoom, tileSize, screenWidth, screenHeight);
189 
190         // bottom
191         cameraCenterX = 0.5;
192         cameraCenterY = 1.0;
193         screenPositions(name + QString("_bottomCamera"), cameraCenterX, cameraCenterY,
194                         zoom, tileSize, screenWidth, screenHeight);
195         // bottom right
196         cameraCenterX = 1.0;
197         cameraCenterY = 1.0;
198         screenPositions(name + QString("_bottomRightCamera"), cameraCenterX, cameraCenterY,
199                         zoom, tileSize, screenWidth, screenHeight);
200         // left
201         cameraCenterX = 0.0;
202         cameraCenterY = 0.5;
203         screenPositions(name + QString("_leftCamera"), cameraCenterX, cameraCenterY,
204                         zoom, tileSize, screenWidth, screenHeight);
205         // middle
206         cameraCenterX = 0.5;
207         cameraCenterY = 0.5;
208         screenPositions(name + QString("_middleCamera"), cameraCenterX, cameraCenterY,
209                         zoom, tileSize, screenWidth, screenHeight);
210         // right
211         cameraCenterX = 1.0;
212         cameraCenterY = 0.5;
213         screenPositions(name + QString("_rightCamera"), cameraCenterX, cameraCenterY,
214                         zoom, tileSize, screenWidth, screenHeight);
215         // top left
216         cameraCenterX = 0.0;
217         cameraCenterY = 0.0;
218         screenPositions(name + QString("_topLeftCamera"), cameraCenterX, cameraCenterY,
219                         zoom, tileSize, screenWidth, screenHeight);
220         // top
221         cameraCenterX = 0.5;
222         cameraCenterY = 0.0;
223         screenPositions(name + QString("_topCamera"), cameraCenterX, cameraCenterY,
224                         zoom, tileSize, screenWidth, screenHeight);
225         // top right
226         cameraCenterX = 1.0;
227         cameraCenterY = 0.0;
228         screenPositions(name + QString("_topRightCamera"), cameraCenterX, cameraCenterY,
229                         zoom, tileSize, screenWidth, screenHeight);
230     }
231 
populateScreenMercatorData()232     void populateScreenMercatorData(){
233         QTest::addColumn<double>("screenX");
234         QTest::addColumn<double>("screenX2");
235         QTest::addColumn<double>("screenY");
236         QTest::addColumn<double>("cameraCenterX");
237         QTest::addColumn<double>("cameraCenterY");
238         QTest::addColumn<double>("zoom");
239         QTest::addColumn<int>("tileSize");
240         QTest::addColumn<int>("screenWidth");
241         QTest::addColumn<int>("screenHeight");
242         QTest::addColumn<double>("mercatorX");
243         QTest::addColumn<double>("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
wrappedMercatorDistance(double x1,double x2)292     double wrappedMercatorDistance(double x1, double x2)
293     {
294         return qMin(qMin(qAbs(x1 - 1.0 - x2), qAbs(x1 - x2)), qAbs(x1 + 1.0 - x2));
295     }
296 
297     private slots:
screenToMercatorPositions()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(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(camera);
329 
330             QDoubleVector2D point(screenX,screenY);
331             QDoubleVector2D mercartorPos = projection.unwrapMapProjection(projection.itemPositionToWrappedMapProjection(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 
screenToMercatorPositions_data()344         void screenToMercatorPositions_data()
345         {
346             populateScreenMercatorData();
347         }
348 
mercatorToScreenPositions()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(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(camera);
381 
382             QDoubleVector2D mercatorPos(mercatorX, mercatorY);
383             QPointF point = projection.wrappedMapProjectionToItemPosition(projection.wrapMapProjection(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 
mercatorToScreenPositions_data()393         void mercatorToScreenPositions_data(){
394             populateScreenMercatorData();
395         }
396 
397 };
398 
399 QTEST_GUILESS_MAIN(tst_QGeoTiledMapScene)
400 #include "tst_qgeotiledmapscene.moc"
401