Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgeocameratiles.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
9
10#include <QtGui/QMatrix4x4>
11#include <QList>
12#include <QMap>
13#include <QPair>
14#include <QSet>
15#include <QSize>
16
17#include <QtPositioning/private/qwebmercator_p.h>
18#include <QtPositioning/private/qdoublevector2d_p.h>
19#include <QtPositioning/private/qdoublevector3d_p.h>
20#include <QtPositioning/private/qlocationutils_p.h>
21
22#include <cmath>
23#include <limits>
24
25static QVector3D toVector3D(const QDoubleVector3D& in)
26{
27 return QVector3D(in.x(), in.y(), in.z());
28}
29
30static QDoubleVector3D toDoubleVector3D(const QVector3D& in)
31{
32 return QDoubleVector3D(in.x(), in.y(), in.z());
33}
34
35QT_BEGIN_NAMESPACE
36
37QGeoCameraTiles::QGeoCameraTiles()
38 : d_ptr(new QGeoCameraTilesPrivate()) {}
39
40QGeoCameraTiles::~QGeoCameraTiles()
41{
42}
43
44void QGeoCameraTiles::setCameraData(const QGeoCameraData &camera)
45{
46 if (d_ptr->m_camera == camera)
47 return;
48
49 d_ptr->m_dirtyGeometry = true;
50 d_ptr->m_camera = camera;
51 d_ptr->m_intZoomLevel = static_cast<int>(std::floor(d_ptr->m_camera.zoomLevel()));
52 d_ptr->m_sideLength = 1 << d_ptr->m_intZoomLevel;
53}
54
55QGeoCameraData QGeoCameraTiles::cameraData() const
56{
57 return d_ptr->m_camera;
58}
59
60void QGeoCameraTiles::setVisibleArea(const QRectF &visibleArea)
61{
62 if (d_ptr->m_visibleArea == visibleArea)
63 return;
64
65 d_ptr->m_visibleArea = visibleArea;
66 d_ptr->m_dirtyGeometry = true;
67}
68
69void QGeoCameraTiles::setScreenSize(const QSize &size)
70{
71 if (d_ptr->m_screenSize == size)
72 return;
73
74 d_ptr->m_dirtyGeometry = true;
75 d_ptr->m_screenSize = size;
76}
77
78void QGeoCameraTiles::setPluginString(const QString &pluginString)
79{
80 if (d_ptr->m_pluginString == pluginString)
81 return;
82
83 d_ptr->m_dirtyMetadata = true;
84 d_ptr->m_pluginString = pluginString;
85}
86
87void QGeoCameraTiles::setMapType(const QGeoMapType &mapType)
88{
89 if (d_ptr->m_mapType == mapType)
90 return;
91
92 d_ptr->m_dirtyMetadata = true;
93 d_ptr->m_mapType = mapType;
94}
95
96QGeoMapType QGeoCameraTiles::activeMapType() const
97{
98 return d_ptr->m_mapType;
99}
100
101void QGeoCameraTiles::setMapVersion(int mapVersion)
102{
103 if (d_ptr->m_mapVersion == mapVersion)
104 return;
105
106 d_ptr->m_dirtyMetadata = true;
107 d_ptr->m_mapVersion = mapVersion;
108}
109
110void QGeoCameraTiles::setTileSize(int tileSize)
111{
112 if (d_ptr->m_tileSize == tileSize)
113 return;
114
115 d_ptr->m_dirtyGeometry = true;
116 d_ptr->m_tileSize = tileSize;
117}
118
119void QGeoCameraTiles::setViewExpansion(double viewExpansion)
120{
121 d_ptr->m_viewExpansion = viewExpansion;
122 d_ptr->m_dirtyGeometry = true;
123}
124
125int QGeoCameraTiles::tileSize() const
126{
127 return d_ptr->m_tileSize;
128}
129
130const QSet<QGeoTileSpec>& QGeoCameraTiles::createTiles()
131{
132 if (d_ptr->m_dirtyGeometry) {
133 d_ptr->m_tiles.clear();
134 d_ptr->updateGeometry();
135 d_ptr->m_dirtyGeometry = false;
136 }
137
138 if (d_ptr->m_dirtyMetadata) {
139 d_ptr->updateMetadata();
140 d_ptr->m_dirtyMetadata = false;
141 }
142
143 return d_ptr->m_tiles;
144}
145
146
147void QGeoCameraTilesPrivate::updateMetadata()
148{
149 typedef QSet<QGeoTileSpec>::const_iterator iter;
150
151 QSet<QGeoTileSpec> newTiles;
152
153 iter i = m_tiles.constBegin();
154 iter end = m_tiles.constEnd();
155
156 for (; i != end; ++i) {
157 QGeoTileSpec tile = *i;
158 newTiles.insert(QGeoTileSpec(m_pluginString, m_mapType.mapId(), tile.zoom(), tile.x(), tile.y(), m_mapVersion));
159 }
160
161 m_tiles = newTiles;
162}
163
164void QGeoCameraTilesPrivate::updateGeometry()
165{
166 // Find the frustum from the camera / screen / viewport information
167 // The larger frustum when stationary is a form of prefetching
168 Frustum f = createFrustum(m_viewExpansion);
169#ifdef QT_LOCATION_DEBUG
170 m_frustum = f;
171#endif
172
173 // Find the polygon where the frustum intersects the plane of the map
174 PolygonVector footprint = frustumFootprint(f);
175#ifdef QT_LOCATION_DEBUG
176 m_frustumFootprint = footprint;
177#endif
178
179 // Clip the polygon to the map, split it up if it cross the dateline
180 ClippedFootprint polygons = clipFootprintToMap(footprint);
181#ifdef QT_LOCATION_DEBUG
182 m_clippedFootprint = polygons;
183#endif
184
185
186 if (!polygons.left.isEmpty()) {
187 QSet<QGeoTileSpec> tilesLeft = tilesFromPolygon(polygons.left);
188 m_tiles.unite(tilesLeft);
189 }
190
191 if (!polygons.right.isEmpty()) {
192 QSet<QGeoTileSpec> tilesRight = tilesFromPolygon(polygons.right);
193 m_tiles.unite(tilesRight);
194 }
195
196 if (!polygons.mid.isEmpty()) {
197 QSet<QGeoTileSpec> tilesRight = tilesFromPolygon(polygons.mid);
198 m_tiles.unite(tilesRight);
199 }
200}
201
202Frustum QGeoCameraTilesPrivate::createFrustum(double viewExpansion) const
203{
204 double apertureSize = 1.0;
205 if (m_camera.fieldOfView() != 90.0) //aperture(90 / 2) = 1
206 apertureSize = tan(QLocationUtils::radians(m_camera.fieldOfView()) * 0.5);
207 QDoubleVector3D center = m_sideLength * QWebMercator::coordToMercator(m_camera.center());
208#ifdef QT_LOCATION_DEBUG
209 m_createFrustum_center = center;
210#endif
211
212
213 double f = m_screenSize.height();
214
215 double z = std::pow(2.0, m_camera.zoomLevel() - m_intZoomLevel) * m_tileSize; // between 1 and 2 * m_tileSize
216
217 double altitude = (f / (2.0 * z)) / apertureSize;
218 QDoubleVector3D eye = center;
219 eye.setZ(altitude);
220
221 QDoubleVector3D view = eye - center;
222 QDoubleVector3D side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
223 QDoubleVector3D up = QDoubleVector3D::normal(side, view);
224
225 QMatrix4x4 mBearing;
226 // The rotation direction here is the opposite of QGeoTiledMapScene::setupCamera,
227 // as this is basically rotating the map against a fixed view frustum.
228 mBearing.rotate(1.0 * m_camera.bearing(), toVector3D(view));
229 up = toDoubleVector3D(mBearing.map(toVector3D(up)));
230
231 // same for tilting
232 QDoubleVector3D side2 = QDoubleVector3D::normal(up, view);
233 QMatrix4x4 mTilt;
234 mTilt.rotate(-1.0 * m_camera.tilt(), toVector3D(side2));
235 eye = toDoubleVector3D((mTilt.map(toVector3D(view))) + toVector3D(center));
236
237 view = eye - center;
238 side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
239 up = QDoubleVector3D::normal(view, side2);
240
241 double nearPlane = 1.0 / 32.0; // The denominator used to be (4.0 * m_tileSize ), which produces an extremely narrow and tiny near plane.
242 // farPlane plays a role on how much gets clipped when the map gets tilted. It used to be altitude + 1.0
243 // The value of 8.0 has been chosen as an acceptable compromise.
244 // TODO: use m_camera.clipDistance(); when this will be introduced
245 double farPlane = altitude + 8.0;
246
247 double aspectRatio = 1.0 * m_screenSize.width() / m_screenSize.height();
248
249 // Half values. Half width near, far, height near, far.
250 double hhn,hwn,hhf,hwf = 0.0;
251
252 // This used to fix the (half) field of view at 45 degrees
253 // half because this assumed that viewSize = 2*nearPlane x 2*nearPlane
254 viewExpansion *= apertureSize;
255
256 hhn = viewExpansion * nearPlane;
257 hwn = hhn * aspectRatio;
258
259 hhf = viewExpansion * farPlane;
260 hwf = hhf * aspectRatio;
261
262 QDoubleVector3D d = center - eye;
263 d.normalize();
264 up.normalize();
265 QDoubleVector3D right = QDoubleVector3D::normal(d, up);
266
267 QDoubleVector3D cf = eye + d * farPlane;
268 QDoubleVector3D cn = eye + d * nearPlane;
269
270 Frustum frustum;
271
272 frustum.apex = eye;
273#ifdef QT_LOCATION_DEBUG
274 m_createFrustum_eye = eye;
275#endif
276
277 QRectF va = m_visibleArea;
278 if (va.isNull())
279 va = QRectF(0, 0, m_screenSize.width(), m_screenSize.height());
280 QRectF screen = QRectF(QPointF(0,0),m_screenSize);
281 QPointF vaCenter = va.center();
282 QPointF screenCenter = screen.center();
283 QPointF diff = screenCenter - vaCenter;
284 double xdiffpct = diff.x() / m_screenSize.width();
285 double ydiffpct = -(diff.y() / m_screenSize.height());
286
287 double wn = (2 * hwn) * xdiffpct;
288 double hn = (2 * hhn) * ydiffpct;
289 double wf = (2 * hwf) * xdiffpct;
290 double hf = (2 * hhf) * ydiffpct;
291
292 // TODO: fix eye
293
294 frustum.topLeftFar = cf - (up * (hhf + hf)) - (right * (hwf + wf));
295 frustum.topRightFar = cf - (up * (hhf + hf)) + (right * (hwf + wf));
296 frustum.bottomLeftFar = cf + (up * (hhf + hf)) - (right * (hwf + wf));
297 frustum.bottomRightFar = cf + (up * (hhf + hf)) + (right * (hwf + wf));
298
299 frustum.topLeftNear = cn - (up * (hhn + hn)) - (right * (hwn + wn));
300 frustum.topRightNear = cn - (up * (hhn + hn)) + (right * (hwn + wn));
301 frustum.bottomLeftNear = cn + (up * (hhn + hn)) - (right * (hwn + wn));
302 frustum.bottomRightNear = cn + (up * (hhn + hn)) + (right * (hwn + wn));
303
304 return frustum;
305}
306
307static bool appendZIntersects(const QDoubleVector3D &start, const QDoubleVector3D &end, double z,
308 QList<QDoubleVector3D> &results)
309{
310 if (start.z() == end.z()) {
311 return false;
312 } else {
313 double f = (start.z() - z) / (start.z() - end.z());
314 if ((f >= 0) && (f <= 1.0)) {
315 results.append((1 - f) * start + f * end);
316 return true;
317 }
318 }
319 return false;
320}
321
322// Returns the intersection of the plane of the map and the camera frustum as a right handed polygon
323PolygonVector QGeoCameraTilesPrivate::frustumFootprint(const Frustum &frustum) const
324{
325 PolygonVector points;
326 points.reserve(4);
327
328 // The camera is always upright. Tilting angle never reach 90degrees.
329 // Meaning: bottom frustum edges always intersect the map plane, top ones may not.
330
331 // Top Right
332 if (!appendZIntersects(frustum.apex, frustum.topRightFar, 0.0, points))
333 appendZIntersects(frustum.topRightFar, frustum.bottomRightFar, 0.0, points);
334
335 // Bottom Right
336 appendZIntersects(frustum.apex, frustum.bottomRightFar, 0.0, points);
337
338 // Bottom Left
339 appendZIntersects(frustum.apex, frustum.bottomLeftFar, 0.0, points);
340
341 // Top Left
342 if (!appendZIntersects(frustum.apex, frustum.topLeftFar, 0.0, points))
343 appendZIntersects(frustum.topLeftFar, frustum.bottomLeftFar, 0.0, points);
344
345 return points;
346}
347
348QPair<PolygonVector, PolygonVector> QGeoCameraTilesPrivate::splitPolygonAtAxisValue(const PolygonVector &polygon, int axis, double value) const
349{
350 PolygonVector polygonBelow;
351 PolygonVector polygonAbove;
352
353 const qsizetype size = polygon.size();
354
355 if (size == 0)
356 return QPair<PolygonVector, PolygonVector>(polygonBelow, polygonAbove);
357
358 QList<int> comparisons(polygon.size());
359
360 for (qsizetype i = 0; i < size; ++i) {
361 const double v = polygon.at(i).get(axis);
362 if (qFuzzyCompare(v - value + 1.0, 1.0)) {
363 comparisons[i] = 0;
364 } else {
365 if (v < value) {
366 comparisons[i] = -1;
367 } else if (value < v) {
368 comparisons[i] = 1;
369 }
370 }
371 }
372
373 for (qsizetype index = 0; index < size; ++index) {
374 qsizetype prevIndex = index - 1;
375 if (prevIndex < 0)
376 prevIndex += size;
377 const qsizetype nextIndex = (index + 1) % size;
378
379 int prevComp = comparisons[prevIndex];
380 int comp = comparisons[index];
381 int nextComp = comparisons[nextIndex];
382
383 if (comp == 0) {
384 if (prevComp == -1) {
385 polygonBelow.append(polygon.at(index));
386 if (nextComp == 1) {
387 polygonAbove.append(polygon.at(index));
388 }
389 } else if (prevComp == 1) {
390 polygonAbove.append(polygon.at(index));
391 if (nextComp == -1) {
392 polygonBelow.append(polygon.at(index));
393 }
394 } else if (prevComp == 0) {
395 if (nextComp == -1) {
396 polygonBelow.append(polygon.at(index));
397 } else if (nextComp == 1) {
398 polygonAbove.append(polygon.at(index));
399 } else if (nextComp == 0) {
400 // do nothing
401 }
402 }
403 } else {
404 if (comp == -1) {
405 polygonBelow.append(polygon.at(index));
406 } else if (comp == 1) {
407 polygonAbove.append(polygon.at(index));
408 }
409
410 // there is a point between this and the next point
411 // on the polygon that lies on the splitting line
412 // and should be added to both the below and above
413 // polygons
414 if ((nextComp != 0) && (nextComp != comp)) {
415 QDoubleVector3D p1 = polygon.at(index);
416 QDoubleVector3D p2 = polygon.at(nextIndex);
417
418 double p1v = p1.get(axis);
419 double p2v = p2.get(axis);
420
421 double f = (p1v - value) / (p1v - p2v);
422
423 if (((0 <= f) && (f <= 1.0))
424 || qFuzzyCompare(f + 1.0, 1.0)
425 || qFuzzyCompare(f + 1.0, 2.0) ) {
426 QDoubleVector3D midPoint = (1.0 - f) * p1 + f * p2;
427 polygonBelow.append(midPoint);
428 polygonAbove.append(midPoint);
429 }
430 }
431 }
432 }
433
434 return QPair<PolygonVector, PolygonVector>(polygonBelow, polygonAbove);
435}
436
437static void addXOffset(PolygonVector &footprint, double xoff)
438{
439 for (QDoubleVector3D &v: footprint)
440 v.setX(v.x() + xoff);
441}
442
443QGeoCameraTilesPrivate::ClippedFootprint QGeoCameraTilesPrivate::clipFootprintToMap(const PolygonVector &footprint) const
444{
445 bool clipX0 = false;
446 bool clipX1 = false;
447 bool clipY0 = false;
448 bool clipY1 = false;
449
450 double side = 1.0 * m_sideLength;
451 double minX = std::numeric_limits<double>::max();
452 double maxX = std::numeric_limits<double>::lowest();
453
454 for (const QDoubleVector3D &p: footprint) {
455 if (p.y() < 0.0)
456 clipY0 = true;
457 if (p.y() > side)
458 clipY1 = true;
459 }
460
461 PolygonVector results = footprint;
462
463 if (clipY0)
464 results = splitPolygonAtAxisValue(results, 1, 0.0).second;
465
466 if (clipY1)
467 results = splitPolygonAtAxisValue(results, 1, side).first;
468
469 for (const QDoubleVector3D &p : std::as_const(results)) {
470 if ((p.x() < 0.0) || (qFuzzyIsNull(p.x())))
471 clipX0 = true;
472 if ((p.x() > side) || (qFuzzyCompare(side, p.x())))
473 clipX1 = true;
474 }
475
476 for (const QDoubleVector3D &v : std::as_const(results)) {
477 minX = qMin(v.x(), minX);
478 maxX = qMax(v.x(), maxX);
479 }
480
481 double footprintWidth = maxX - minX;
482
483 if (clipX0) {
484 if (clipX1) {
485 if (footprintWidth > side) {
486 PolygonVector rightPart = splitPolygonAtAxisValue(results, 0, side).second;
487 addXOffset(rightPart, -side);
488 rightPart = splitPolygonAtAxisValue(rightPart, 0, side).first; // clip it again, should it tend to infinite or so
489
490 PolygonVector leftPart = splitPolygonAtAxisValue(results, 0, 0).first;
491 addXOffset(leftPart, side);
492 leftPart = splitPolygonAtAxisValue(leftPart, 0, 0).second; // same here
493
494 results = splitPolygonAtAxisValue(results, 0, 0.0).second;
495 results = splitPolygonAtAxisValue(results, 0, side).first;
496 return ClippedFootprint(leftPart, results, rightPart);
497 } else { // fitting the WebMercator square exactly?
498 results = splitPolygonAtAxisValue(results, 0, 0.0).second;
499 results = splitPolygonAtAxisValue(results, 0, side).first;
500 return ClippedFootprint(PolygonVector(), results, PolygonVector());
501 }
502 } else {
503 QPair<PolygonVector, PolygonVector> pair = splitPolygonAtAxisValue(results, 0, 0.0);
504 if (pair.first.isEmpty()) {
505 // if we touched the line but didn't cross it...
506 for (const auto &v : std::as_const(pair.second)) {
507 if (qFuzzyIsNull(v.x()))
508 pair.first.append(v);
509 }
510 if (pair.first.size() == 2) {
511 double y0 = pair.first[0].y();
512 double y1 = pair.first[1].y();
513 pair.first.clear();
514 pair.first.append(QDoubleVector3D(side, y0, 0.0));
515 pair.first.append(QDoubleVector3D(side - 0.001, y0, 0.0));
516 pair.first.append(QDoubleVector3D(side - 0.001, y1, 0.0));
517 pair.first.append(QDoubleVector3D(side, y1, 0.0));
518 } else if (pair.first.size() == 1) {
519 // FIXME this is trickier
520 // - touching at one point on the tile boundary
521 // - probably need to build a triangular polygon across the edge
522 // - don't want to add another y tile if we can help it
523 // - initial version doesn't care
524 double y = pair.first.at(0).y();
525 pair.first.clear();
526 pair.first.append(QDoubleVector3D(side - 0.001, y, 0.0));
527 pair.first.append(QDoubleVector3D(side, y + 0.001, 0.0));
528 pair.first.append(QDoubleVector3D(side, y - 0.001, 0.0));
529 }
530 } else {
531 addXOffset(pair.first, side);
532 if (footprintWidth > side)
533 pair.first = splitPolygonAtAxisValue(pair.first, 0, 0).second;
534 }
535 return ClippedFootprint(pair.first, pair.second, PolygonVector());
536 }
537 } else {
538 if (clipX1) {
539 QPair<PolygonVector, PolygonVector> pair = splitPolygonAtAxisValue(results, 0, side);
540 if (pair.second.isEmpty()) {
541 // if we touched the line but didn't cross it...
542 for (const auto &v : std::as_const(pair.first)) {
543 if (qFuzzyCompare(side, v.x()))
544 pair.second.append(v);
545 }
546 if (pair.second.size() == 2) {
547 double y0 = pair.second[0].y();
548 double y1 = pair.second[1].y();
549 pair.second.clear();
550 pair.second.append(QDoubleVector3D(0, y0, 0.0));
551 pair.second.append(QDoubleVector3D(0.001, y0, 0.0));
552 pair.second.append(QDoubleVector3D(0.001, y1, 0.0));
553 pair.second.append(QDoubleVector3D(0, y1, 0.0));
554 } else if (pair.second.size() == 1) {
555 // FIXME this is trickier
556 // - touching at one point on the tile boundary
557 // - probably need to build a triangular polygon across the edge
558 // - don't want to add another y tile if we can help it
559 // - initial version doesn't care
560 double y = pair.second.at(0).y();
561 pair.second.clear();
562 pair.second.append(QDoubleVector3D(0.001, y, 0.0));
563 pair.second.append(QDoubleVector3D(0.0, y - 0.001, 0.0));
564 pair.second.append(QDoubleVector3D(0.0, y + 0.001, 0.0));
565 }
566 } else {
567 addXOffset(pair.second, -side);
568 if (footprintWidth > side)
569 pair.second = splitPolygonAtAxisValue(pair.second, 0, side).first;
570 }
571 return ClippedFootprint(PolygonVector(), pair.first, pair.second);
572 } else {
573 return ClippedFootprint(PolygonVector(), results, PolygonVector());
574 }
575 }
576
577}
578
579QList<QPair<double, int>> QGeoCameraTilesPrivate::tileIntersections(double p1, int t1, double p2, int t2) const
580{
581 if (t1 == t2) {
582 QList<QPair<double, int>> results = QList<QPair<double, int>>();
583 results.append(QPair<double, int>(0.0, t1));
584 return results;
585 }
586
587 int step = 1;
588 if (t1 > t2)
589 step = -1;
590
591 qsizetype size = 1 + ((t2 - t1) / step);
592
593 QList<QPair<double, int>> results = { QPair<double, int>(0.0, t1) };
594
595 if (step == 1) {
596 for (qsizetype i = 1; i < size; ++i) {
597 double f = (t1 + i - p1) / (p2 - p1);
598 results.append(QPair<double, int>(f, t1 + i));
599 }
600 } else {
601 for (qsizetype i = 1; i < size; ++i) {
602 double f = (t1 - i + 1 - p1) / (p2 - p1);
603 results.append(QPair<double, int>(f, t1 - i));
604 }
605 }
606
607 return results;
608}
609
610QSet<QGeoTileSpec> QGeoCameraTilesPrivate::tilesFromPolygon(const PolygonVector &polygon) const
611{
612 const qsizetype numPoints = polygon.size();
613
614 if (numPoints == 0)
615 return QSet<QGeoTileSpec>();
616
617 QList<int> tilesX(polygon.size());
618 QList<int> tilesY(polygon.size());
619
620 // grab tiles at the corners of the polygon
621 for (qsizetype i = 0; i < numPoints; ++i) {
622
623 const QDoubleVector2D p = polygon.at(i).toVector2D();
624
625 int x = 0;
626 int y = 0;
627
628 if (qFuzzyCompare(p.x(), m_sideLength * 1.0))
629 x = m_sideLength - 1;
630 else {
631 x = static_cast<int>(p.x()) % m_sideLength;
632 if (!QtPrivate::fuzzyCompare(p.x(), 1.0 * x) && qFuzzyCompare(p.x(), 1.0 * (x + 1)))
633 x++;
634 }
635
636 if (qFuzzyCompare(p.y(), m_sideLength * 1.0))
637 y = m_sideLength - 1;
638 else {
639 y = static_cast<int>(p.y()) % m_sideLength;
640 if (!QtPrivate::fuzzyCompare(p.y(), 1.0 * y) && qFuzzyCompare(p.y(), 1.0 * (y + 1)))
641 y++;
642 }
643
644 tilesX[i] = x;
645 tilesY[i] = y;
646 }
647
648 QGeoCameraTilesPrivate::TileMap map;
649
650 // walk along the edges of the polygon and add all tiles covered by them
651 for (qsizetype i1 = 0; i1 < numPoints; ++i1) {
652 const qsizetype i2 = (i1 + 1) % numPoints;
653
654 const double x1 = polygon.at(i1).get(0);
655 const double x2 = polygon.at(i2).get(0);
656
657 const bool xFixed = QtPrivate::fuzzyCompare(x1, x2);
658 const bool xIntegral = qFuzzyIsNull(x1) || qFuzzyCompare(x1, std::round(x1));
659
660 QList<QPair<double, int> > xIntersects
661 = tileIntersections(x1,
662 tilesX.at(i1),
663 x2,
664 tilesX.at(i2));
665
666 const double y1 = polygon.at(i1).get(1);
667 const double y2 = polygon.at(i2).get(1);
668
669 const bool yFixed = QtPrivate::fuzzyCompare(y1, y2);
670 const bool yIntegral = qFuzzyIsNull(y1) || qFuzzyCompare(y1, std::round(y1));
671
672 QList<QPair<double, int> > yIntersects
673 = tileIntersections(y1,
674 tilesY.at(i1),
675 y2,
676 tilesY.at(i2));
677
678 int x = xIntersects.takeFirst().second;
679 int y = yIntersects.takeFirst().second;
680
681
682 /*
683 If the polygon coincides with the tile edges we must be
684 inclusive and grab all tiles on both sides. We also need
685 to handle tiles with corners coindent with the
686 corners of the polygon.
687 e.g. all tiles marked with 'x' will be added
688
689 "+" - tile boundaries
690 "O" - polygon boundary
691
692 + + + + + + + + + + + + + + + + + + + + +
693 + + + + + +
694 + + x + x + x + +
695 + + + + + +
696 + + + + + + + + O O O O O + + + + + + + +
697 + + O 0 + +
698 + + x O x 0 x + +
699 + + O 0 + +
700 + + + + + + + + O 0 0 0 0 + + + + + + + +
701 + + + + + +
702 + + x + x + x + +
703 + + + + + +
704 + + + + + + + + + + + + + + + + + + + + +
705 */
706
707
708 int xOther = x;
709 int yOther = y;
710
711 if (xFixed && xIntegral) {
712 if (y2 < y1) {
713 xOther = qMax(0, x - 1);
714 }
715 }
716
717 if (yFixed && yIntegral) {
718 if (x1 < x2) {
719 yOther = qMax(0, y - 1);
720
721 }
722 }
723
724 if (xIntegral) {
725 map.add(xOther, y);
726 if (yIntegral)
727 map.add(xOther, yOther);
728
729 }
730
731 if (yIntegral)
732 map.add(x, yOther);
733
734 map.add(x,y);
735
736 // top left corner
737 const qsizetype iPrev = (i1 + numPoints - 1) % numPoints;
738 const double xPrevious = polygon.at(iPrev).get(0);
739 const double yPrevious = polygon.at(iPrev).get(1);
740 const bool xPreviousFixed = QtPrivate::fuzzyCompare(xPrevious, x1);
741 if (xIntegral && xPreviousFixed && yIntegral && yFixed) {
742 if ((x2 > x1) && (yPrevious > y1)) {
743 if ((x - 1) > 0 && (y - 1) > 0)
744 map.add(x - 1, y - 1);
745 } else if ((x2 < x1) && (yPrevious < y1)) {
746 // what?
747 }
748 }
749
750 // for the simple case where intersections do not coincide with
751 // the boundaries, we move along the edge and add tiles until
752 // the x and y intersection lists are exhausted
753
754 while (!xIntersects.isEmpty() && !yIntersects.isEmpty()) {
755 const QPair<double, int> nextX = xIntersects.first();
756 const QPair<double, int> nextY = yIntersects.first();
757 if (nextX.first < nextY.first) {
758 x = nextX.second;
759 map.add(x, y);
760 xIntersects.removeFirst();
761
762 } else if (nextX.first > nextY.first) {
763 y = nextY.second;
764 map.add(x, y);
765 yIntersects.removeFirst();
766
767 } else {
768 map.add(x, nextY.second);
769 map.add(nextX.second, y);
770 x = nextX.second;
771 y = nextY.second;
772 map.add(x, y);
773 xIntersects.removeFirst();
774 yIntersects.removeFirst();
775 }
776 }
777
778 while (!xIntersects.isEmpty()) {
779 x = xIntersects.takeFirst().second;
780 map.add(x, y);
781 if (yIntegral && yFixed)
782 map.add(x, yOther);
783
784 }
785
786 while (!yIntersects.isEmpty()) {
787 y = yIntersects.takeFirst().second;
788 map.add(x, y);
789 if (xIntegral && xFixed)
790 map.add(xOther, y);
791 }
792 }
793
794 QSet<QGeoTileSpec> results;
795
796 const int z = m_intZoomLevel;
797 for (auto i = map.data.constBegin(); i != map.data.constEnd(); ++i) {
798 int y = i.key();
799 int minX = i->first;
800 int maxX = i->second;
801 for (int x = minX; x <= maxX; ++x)
802 results.insert(QGeoTileSpec(m_pluginString, m_mapType.mapId(), z, x, y, m_mapVersion));
803 }
804
805 return results;
806}
807
808QGeoCameraTilesPrivate::TileMap::TileMap() {}
809
810void QGeoCameraTilesPrivate::TileMap::add(int tileX, int tileY)
811{
812 if (data.contains(tileY)) {
813 int oldMinX = data.value(tileY).first;
814 int oldMaxX = data.value(tileY).second;
815 data.insert(tileY, QPair<int, int>(qMin(tileX, oldMinX), qMax(tileX, oldMaxX)));
816 } else {
817 data.insert(tileY, QPair<int, int>(tileX, tileX));
818 }
819}
820
821QT_END_NAMESPACE
static QDoubleVector3D toDoubleVector3D(const QVector3D &in)
static bool appendZIntersects(const QDoubleVector3D &start, const QDoubleVector3D &end, double z, QList< QDoubleVector3D > &results)
static QVector3D toVector3D(const QDoubleVector3D &in)
static void addXOffset(PolygonVector &footprint, double xoff)
QList< QDoubleVector3D > PolygonVector