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 QtPositioning 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 "qgeopolygon.h" |
41 | #include "qgeopolygon_p.h" |
42 | #include "qgeopath_p.h" |
43 | #include "qgeocircle.h" |
44 | |
45 | #include "qgeocoordinate.h" |
46 | #include "qnumeric.h" |
47 | #include "qlocationutils_p.h" |
48 | #include "qwebmercator_p.h" |
49 | |
50 | #include "qdoublevector2d_p.h" |
51 | #include "qdoublevector3d_p.h" |
52 | #include "qwebmercator_p.h" |
53 | |
54 | QT_BEGIN_NAMESPACE |
55 | |
56 | /*! |
57 | \class QGeoPolygon |
58 | \inmodule QtPositioning |
59 | \ingroup QtPositioning-positioning |
60 | \since 5.10 |
61 | |
62 | \brief The QGeoPolygon class defines a geographic polygon. |
63 | |
64 | The polygon is defined by an ordered list of QGeoCoordinates representing its perimeter. |
65 | |
66 | Each two adjacent elements in this list are intended to be connected |
67 | together by the shortest line segment of constant bearing passing |
68 | through both elements. |
69 | This type of connection can cross the date line in the longitudinal direction, |
70 | but never crosses the poles. |
71 | |
72 | This is relevant for the calculation of the bounding box returned by |
73 | \l QGeoShape::boundingGeoRectangle() for this shape, which will have the latitude of |
74 | the top left corner set to the maximum latitude in the path point set. |
75 | Similarly, the latitude of the bottom right corner will be the minimum latitude |
76 | in the path point set. |
77 | |
78 | This class is a \l Q_GADGET. |
79 | It can be \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. |
80 | */ |
81 | |
82 | /* |
83 | \property QGeoPolygon::path |
84 | \brief This property holds the list of coordinates for the geo polygon. |
85 | |
86 | The polygon is both invalid and empty if it contains no coordinate. |
87 | |
88 | A default constructed QGeoPolygon is therefore invalid. |
89 | */ |
90 | |
91 | inline QGeoPolygonPrivate *QGeoPolygon::d_func() |
92 | { |
93 | return static_cast<QGeoPolygonPrivate *>(d_ptr.data()); |
94 | } |
95 | |
96 | inline const QGeoPolygonPrivate *QGeoPolygon::d_func() const |
97 | { |
98 | return static_cast<const QGeoPolygonPrivate *>(d_ptr.constData()); |
99 | } |
100 | |
101 | struct PolygonVariantConversions |
102 | { |
103 | PolygonVariantConversions() |
104 | { |
105 | QMetaType::registerConverter<QGeoShape, QGeoPolygon>(); |
106 | QMetaType::registerConverter<QGeoPolygon, QGeoShape>(); |
107 | } |
108 | }; |
109 | |
110 | Q_GLOBAL_STATIC(PolygonVariantConversions, initPolygonConversions) |
111 | |
112 | /*! |
113 | Constructs a new, empty geo polygon. |
114 | */ |
115 | QGeoPolygon::QGeoPolygon() |
116 | : QGeoShape(new QGeoPolygonPrivate()) |
117 | { |
118 | initPolygonConversions(); |
119 | } |
120 | |
121 | /*! |
122 | Constructs a new geo polygon from the coordinates specified |
123 | in \a path. |
124 | */ |
125 | QGeoPolygon::QGeoPolygon(const QList<QGeoCoordinate> &path) |
126 | : QGeoShape(new QGeoPolygonPrivate(path)) |
127 | { |
128 | initPolygonConversions(); |
129 | } |
130 | |
131 | /*! |
132 | Constructs a new geo polygon from the contents of \a other. |
133 | */ |
134 | QGeoPolygon::QGeoPolygon(const QGeoPolygon &other) |
135 | : QGeoShape(other) |
136 | { |
137 | initPolygonConversions(); |
138 | } |
139 | |
140 | static void calculatePeripheralPoints(QList<QGeoCoordinate> &path, |
141 | const QGeoCircle &circle, |
142 | int steps) |
143 | { |
144 | const QGeoCoordinate ¢er = circle.center(); |
145 | const qreal distance = circle.radius(); |
146 | // Calculate points based on great-circle distance |
147 | // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function |
148 | // but tweaked here for computing multiple points |
149 | |
150 | // pre-calculations |
151 | steps = qMax(a: steps, b: 3); |
152 | qreal centerLon = center.longitude(); |
153 | qreal latRad = QLocationUtils::radians(degrees: center.latitude()); |
154 | qreal lonRad = QLocationUtils::radians(degrees: centerLon); |
155 | qreal cosLatRad = std::cos(x: latRad); |
156 | qreal sinLatRad = std::sin(x: latRad); |
157 | qreal ratio = (distance / QLocationUtils::earthMeanRadius()); |
158 | qreal cosRatio = std::cos(x: ratio); |
159 | qreal sinRatio = std::sin(x: ratio); |
160 | qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio; |
161 | qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio; |
162 | for (int i = 0; i < steps; ++i) { |
163 | qreal azimuthRad = 2 * M_PI * i / steps; |
164 | qreal resultLatRad = std::asin(x: sinLatRad_x_cosRatio |
165 | + cosLatRad_x_sinRatio * std::cos(x: azimuthRad)); |
166 | qreal resultLonRad = lonRad + std::atan2(y: std::sin(x: azimuthRad) * cosLatRad_x_sinRatio, |
167 | x: cosRatio - sinLatRad * std::sin(x: resultLatRad)); |
168 | qreal lat2 = QLocationUtils::degrees(radians: resultLatRad); |
169 | qreal lon2 = QLocationUtils::wrapLong(lng: QLocationUtils::degrees(radians: resultLonRad)); |
170 | |
171 | path << QGeoCoordinate(lat2, lon2, center.altitude()); |
172 | } |
173 | } |
174 | |
175 | /*! |
176 | Constructs a new geo polygon from the contents of \a other. |
177 | */ |
178 | QGeoPolygon::QGeoPolygon(const QGeoShape &other) |
179 | : QGeoShape(other) |
180 | { |
181 | initPolygonConversions(); |
182 | if (type() != QGeoShape::PolygonType) { |
183 | QGeoPolygonPrivate *poly = new QGeoPolygonPrivate(); |
184 | if (type() == QGeoShape::CircleType) { |
185 | const QGeoCircle &circle = static_cast<const QGeoCircle &>(other); |
186 | QList<QGeoCoordinate> perimeter; |
187 | calculatePeripheralPoints(path&: perimeter, circle, steps: 128); |
188 | poly->setPath(perimeter); |
189 | } else if (type() == QGeoShape::RectangleType) { |
190 | const QGeoRectangle &rect = static_cast<const QGeoRectangle &>(other); |
191 | QList<QGeoCoordinate> perimeter; |
192 | perimeter << rect.topLeft() << rect.topRight() |
193 | << rect.bottomRight() << rect.bottomLeft(); |
194 | poly->setPath(perimeter); |
195 | } |
196 | d_ptr = poly; |
197 | } |
198 | } |
199 | |
200 | /*! |
201 | Destroys this polygon. |
202 | */ |
203 | QGeoPolygon::~QGeoPolygon() {} |
204 | |
205 | /*! |
206 | Assigns \a other to this geo polygon and returns a reference to this geo polygon. |
207 | */ |
208 | QGeoPolygon &QGeoPolygon::operator=(const QGeoPolygon &other) |
209 | { |
210 | QGeoShape::operator=(other); |
211 | return *this; |
212 | } |
213 | |
214 | /*! |
215 | Returns whether this geo polygon is equal to \a other. |
216 | */ |
217 | bool QGeoPolygon::operator==(const QGeoPolygon &other) const |
218 | { |
219 | Q_D(const QGeoPolygon); |
220 | return *d == *other.d_func(); |
221 | } |
222 | |
223 | /*! |
224 | Returns whether this geo polygon is not equal to \a other. |
225 | */ |
226 | bool QGeoPolygon::operator!=(const QGeoPolygon &other) const |
227 | { |
228 | Q_D(const QGeoPolygon); |
229 | return !(*d == *other.d_func()); |
230 | } |
231 | |
232 | /*! |
233 | Sets the \a path for the polygon. |
234 | */ |
235 | void QGeoPolygon::setPath(const QList<QGeoCoordinate> &path) |
236 | { |
237 | Q_D(QGeoPolygon); |
238 | return d->setPath(path); |
239 | } |
240 | |
241 | /*! |
242 | Returns all the elements of the polygon's boundary. |
243 | */ |
244 | const QList<QGeoCoordinate> &QGeoPolygon::path() const |
245 | { |
246 | Q_D(const QGeoPolygon); |
247 | return d->path(); |
248 | } |
249 | |
250 | /*! |
251 | Sets all the elements of the polygon's perimeter |
252 | based on a list of coordinates (\a path). |
253 | . |
254 | |
255 | \since QtPositioning 5.12 |
256 | */ |
257 | void QGeoPolygon::setPerimeter(const QVariantList &path) |
258 | { |
259 | Q_D(QGeoPolygon); |
260 | QList<QGeoCoordinate> p; |
261 | for (const auto &c: path) { |
262 | if (c.canConvert<QGeoCoordinate>()) |
263 | p << c.value<QGeoCoordinate>(); |
264 | } |
265 | d->setPath(p); |
266 | } |
267 | |
268 | /*! |
269 | Returns all the elements of the polygon's perimeter. |
270 | |
271 | \since QtPositioning 5.12 |
272 | */ |
273 | QVariantList QGeoPolygon::perimeter() const |
274 | { |
275 | Q_D(const QGeoPolygon); |
276 | QVariantList p; |
277 | for (const auto &c: d->path()) |
278 | p << QVariant::fromValue(value: c); |
279 | return p; |
280 | } |
281 | |
282 | /*! |
283 | Translates this geo polygon by \a degreesLatitude northwards and \a degreesLongitude eastwards. |
284 | |
285 | Negative values of \a degreesLatitude and \a degreesLongitude correspond to |
286 | southward and westward translation respectively. |
287 | */ |
288 | void QGeoPolygon::translate(double degreesLatitude, double degreesLongitude) |
289 | { |
290 | Q_D(QGeoPolygon); |
291 | d->translate(degreesLatitude, degreesLongitude); |
292 | } |
293 | |
294 | /*! |
295 | Returns a copy of this geo polygon translated by \a degreesLatitude northwards and |
296 | \a degreesLongitude eastwards. |
297 | |
298 | Negative values of \a degreesLatitude and \a degreesLongitude correspond to |
299 | southward and westward translation respectively. |
300 | |
301 | \sa translate() |
302 | */ |
303 | QGeoPolygon QGeoPolygon::translated(double degreesLatitude, double degreesLongitude) const |
304 | { |
305 | QGeoPolygon result(*this); |
306 | result.translate(degreesLatitude, degreesLongitude); |
307 | return result; |
308 | } |
309 | |
310 | /*! |
311 | Returns the length of the polygon's perimeter, in meters, from the element \a indexFrom to the element \a indexTo. |
312 | The length is intended to be the sum of the shortest distances for each pair of adjacent points. |
313 | */ |
314 | double QGeoPolygon::length(int indexFrom, int indexTo) const |
315 | { |
316 | Q_D(const QGeoPolygon); |
317 | return d->length(indexFrom, indexTo); |
318 | } |
319 | |
320 | /*! |
321 | Returns the number of elements in the polygon. |
322 | |
323 | \since 5.10 |
324 | */ |
325 | int QGeoPolygon::size() const |
326 | { |
327 | Q_D(const QGeoPolygon); |
328 | return d->size(); |
329 | } |
330 | |
331 | /*! |
332 | Appends \a coordinate to the polygon. |
333 | */ |
334 | void QGeoPolygon::addCoordinate(const QGeoCoordinate &coordinate) |
335 | { |
336 | Q_D(QGeoPolygon); |
337 | d->addCoordinate(coordinate); |
338 | } |
339 | |
340 | /*! |
341 | Inserts \a coordinate at the specified \a index. |
342 | */ |
343 | void QGeoPolygon::insertCoordinate(int index, const QGeoCoordinate &coordinate) |
344 | { |
345 | Q_D(QGeoPolygon); |
346 | d->insertCoordinate(index, coordinate); |
347 | } |
348 | |
349 | /*! |
350 | Replaces the path element at the specified \a index with \a coordinate. |
351 | */ |
352 | void QGeoPolygon::replaceCoordinate(int index, const QGeoCoordinate &coordinate) |
353 | { |
354 | Q_D(QGeoPolygon); |
355 | d->replaceCoordinate(index, coordinate); |
356 | } |
357 | |
358 | /*! |
359 | Returns the coordinate at \a index . |
360 | */ |
361 | QGeoCoordinate QGeoPolygon::coordinateAt(int index) const |
362 | { |
363 | Q_D(const QGeoPolygon); |
364 | return d->coordinateAt(index); |
365 | } |
366 | |
367 | /*! |
368 | Returns true if the polygon's perimeter contains \a coordinate as one of the elements. |
369 | */ |
370 | bool QGeoPolygon::containsCoordinate(const QGeoCoordinate &coordinate) const |
371 | { |
372 | Q_D(const QGeoPolygon); |
373 | return d->containsCoordinate(coordinate); |
374 | } |
375 | |
376 | /*! |
377 | Removes the last occurrence of \a coordinate from the polygon. |
378 | */ |
379 | void QGeoPolygon::removeCoordinate(const QGeoCoordinate &coordinate) |
380 | { |
381 | Q_D(QGeoPolygon); |
382 | d->removeCoordinate(coordinate); |
383 | } |
384 | |
385 | /*! |
386 | Removes element at position \a index from the polygon. |
387 | */ |
388 | void QGeoPolygon::removeCoordinate(int index) |
389 | { |
390 | Q_D(QGeoPolygon); |
391 | d->removeCoordinate(index); |
392 | } |
393 | |
394 | /*! |
395 | Returns the geo polygon properties as a string. |
396 | */ |
397 | QString QGeoPolygon::toString() const |
398 | { |
399 | if (type() != QGeoShape::PolygonType) { |
400 | qWarning(msg: "Not a polygon" ); |
401 | return QStringLiteral("QGeoPolygon(not a polygon)" ); |
402 | } |
403 | |
404 | QString pathString; |
405 | for (const auto &p : path()) |
406 | pathString += p.toString() + QLatin1Char(','); |
407 | |
408 | return QStringLiteral("QGeoPolygon([ %1 ])" ).arg(a: pathString); |
409 | } |
410 | |
411 | /*! |
412 | Sets the \a holePath for a hole inside the polygon. The hole is a |
413 | QVariant containing a QList<QGeoCoordinate>. |
414 | |
415 | \since 5.12 |
416 | */ |
417 | void QGeoPolygon::addHole(const QVariant &holePath) |
418 | { |
419 | Q_D(QGeoPolygon); |
420 | QList<QGeoCoordinate> qgcHolePath; |
421 | if (holePath.canConvert<QVariantList>()) { |
422 | const QVariantList qvlHolePath = holePath.toList(); |
423 | for (const QVariant &vertex : qvlHolePath) { |
424 | if (vertex.canConvert<QGeoCoordinate>()) |
425 | qgcHolePath << vertex.value<QGeoCoordinate>(); |
426 | } |
427 | } |
428 | //ToDo: add QGeoShape support |
429 | return d->addHole(holePath: qgcHolePath); |
430 | } |
431 | |
432 | /*! |
433 | Overloaded method. Sets the \a holePath for a hole inside the polygon. The hole is a QList<QGeoCoordinate>. |
434 | |
435 | \since 5.12 |
436 | */ |
437 | void QGeoPolygon::addHole(const QList<QGeoCoordinate> &holePath) |
438 | { |
439 | Q_D(QGeoPolygon); |
440 | return d->addHole(holePath); |
441 | } |
442 | |
443 | /*! |
444 | Returns a QVariant containing a QVariant containing a QList<QGeoCoordinate> |
445 | which represents the hole at \a index. |
446 | |
447 | \since 5.12 |
448 | */ |
449 | const QVariantList QGeoPolygon::hole(int index) const |
450 | { |
451 | Q_D(const QGeoPolygon); |
452 | QVariantList holeCoordinates; |
453 | for (const QGeoCoordinate &coords: d->holePath(index)) |
454 | holeCoordinates << QVariant::fromValue(value: coords); |
455 | return holeCoordinates; |
456 | } |
457 | |
458 | /*! |
459 | Returns a QList<QGeoCoordinate> which represents the hole at \a index. |
460 | |
461 | \since 5.12 |
462 | */ |
463 | const QList<QGeoCoordinate> QGeoPolygon::holePath(int index) const |
464 | { |
465 | Q_D(const QGeoPolygon); |
466 | return d->holePath(index); |
467 | } |
468 | |
469 | /*! |
470 | Removes element at position \a index from the holes QList. |
471 | |
472 | \since 5.12 |
473 | */ |
474 | void QGeoPolygon::removeHole(int index) |
475 | { |
476 | Q_D(QGeoPolygon); |
477 | return d->removeHole(index); |
478 | } |
479 | |
480 | /*! |
481 | Returns the number of holes. |
482 | |
483 | \since 5.12 |
484 | */ |
485 | int QGeoPolygon::holesCount() const |
486 | { |
487 | Q_D(const QGeoPolygon); |
488 | return d->holesCount(); |
489 | } |
490 | |
491 | /******************************************************************************* |
492 | * |
493 | * QGeoPathPrivate & friends |
494 | * |
495 | *******************************************************************************/ |
496 | |
497 | QGeoPolygonPrivate::QGeoPolygonPrivate() |
498 | : QGeoPathPrivate() |
499 | { |
500 | type = QGeoShape::PolygonType; |
501 | } |
502 | |
503 | QGeoPolygonPrivate::QGeoPolygonPrivate(const QList<QGeoCoordinate> &path) |
504 | : QGeoPathPrivate(path) |
505 | { |
506 | type = QGeoShape::PolygonType; |
507 | } |
508 | |
509 | QGeoPolygonPrivate::~QGeoPolygonPrivate() {} |
510 | |
511 | QGeoShapePrivate *QGeoPolygonPrivate::clone() const |
512 | { |
513 | return new QGeoPolygonPrivate(*this); |
514 | } |
515 | |
516 | bool QGeoPolygonPrivate::isValid() const |
517 | { |
518 | return path().size() > 2; |
519 | } |
520 | |
521 | bool QGeoPolygonPrivate::contains(const QGeoCoordinate &coordinate) const |
522 | { |
523 | return polygonContains(coordinate); |
524 | } |
525 | |
526 | inline static void translatePoly( QList<QGeoCoordinate> &m_path, |
527 | QList<QList<QGeoCoordinate>> &m_holesList, |
528 | QGeoRectangle &m_bbox, |
529 | double degreesLatitude, |
530 | double degreesLongitude, |
531 | double m_maxLati, |
532 | double m_minLati) |
533 | { |
534 | if (degreesLatitude > 0.0) |
535 | degreesLatitude = qMin(a: degreesLatitude, b: 90.0 - m_maxLati); |
536 | else |
537 | degreesLatitude = qMax(a: degreesLatitude, b: -90.0 - m_minLati); |
538 | for (QGeoCoordinate &p: m_path) { |
539 | p.setLatitude(p.latitude() + degreesLatitude); |
540 | p.setLongitude(QLocationUtils::wrapLong(lng: p.longitude() + degreesLongitude)); |
541 | } |
542 | if (!m_holesList.isEmpty()){ |
543 | for (QList<QGeoCoordinate> &hole: m_holesList){ |
544 | for (QGeoCoordinate &holeVertex: hole){ |
545 | holeVertex.setLatitude(holeVertex.latitude() + degreesLatitude); |
546 | holeVertex.setLongitude(QLocationUtils::wrapLong(lng: holeVertex.longitude() + degreesLongitude)); |
547 | } |
548 | } |
549 | } |
550 | m_bbox.translate(degreesLatitude, degreesLongitude); |
551 | } |
552 | |
553 | void QGeoPolygonPrivate::translate(double degreesLatitude, double degreesLongitude) |
554 | { |
555 | // Need min/maxLati, so update bbox |
556 | QVector<double> m_deltaXs; |
557 | double m_minX, m_maxX, m_minLati, m_maxLati; |
558 | m_bboxDirty = false; // Updated in translatePoly |
559 | computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox); |
560 | translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati); |
561 | m_leftBoundWrapped = QWebMercator::coordToMercator(coord: m_bbox.topLeft()).x(); |
562 | m_clipperDirty = true; |
563 | } |
564 | |
565 | bool QGeoPolygonPrivate::operator==(const QGeoShapePrivate &other) const |
566 | { |
567 | if (!QGeoShapePrivate::operator==(other)) // checks type |
568 | return false; |
569 | |
570 | const QGeoPolygonPrivate &otherPath = static_cast<const QGeoPolygonPrivate &>(other); |
571 | if (m_path.size() != otherPath.m_path.size() |
572 | || m_holesList.size() != otherPath.m_holesList.size()) |
573 | return false; |
574 | return m_path == otherPath.m_path && m_holesList == otherPath.m_holesList; |
575 | } |
576 | |
577 | void QGeoPolygonPrivate::addHole(const QList<QGeoCoordinate> &holePath) |
578 | { |
579 | for (const QGeoCoordinate &holeVertex: holePath) |
580 | if (!holeVertex.isValid()) |
581 | return; |
582 | |
583 | m_holesList << holePath; |
584 | // ToDo: mark clipper dirty when hole caching gets added |
585 | } |
586 | |
587 | const QList<QGeoCoordinate> QGeoPolygonPrivate::holePath(int index) const |
588 | { |
589 | return m_holesList.at(i: index); |
590 | } |
591 | |
592 | void QGeoPolygonPrivate::removeHole(int index) |
593 | { |
594 | if (index < 0 || index >= m_holesList.size()) |
595 | return; |
596 | |
597 | m_holesList.removeAt(i: index); |
598 | // ToDo: mark clipper dirty when hole caching gets added |
599 | } |
600 | |
601 | int QGeoPolygonPrivate::holesCount() const |
602 | { |
603 | return m_holesList.size(); |
604 | } |
605 | |
606 | bool QGeoPolygonPrivate::polygonContains(const QGeoCoordinate &coordinate) const |
607 | { |
608 | if (m_clipperDirty) |
609 | const_cast<QGeoPolygonPrivate *>(this)->updateClipperPath(); // this one updates bbox too if needed |
610 | |
611 | QDoubleVector2D coord = QWebMercator::coordToMercator(coord: coordinate); |
612 | |
613 | if (coord.x() < m_leftBoundWrapped) |
614 | coord.setX(coord.x() + 1.0); |
615 | |
616 | |
617 | IntPoint intCoord = QClipperUtils::toIntPoint(p: coord); |
618 | if (!c2t::clip2tri::pointInPolygon(pt: intCoord, path: m_clipperPath)) |
619 | return false; |
620 | |
621 | // else iterates the holes List checking whether the point is contained inside the holes |
622 | for (const QList<QGeoCoordinate> &holePath : qAsConst(t: m_holesList)) { |
623 | // ToDo: cache these |
624 | QGeoPolygon holePolygon; |
625 | holePolygon.setPath(holePath); |
626 | if (holePolygon.contains(coordinate)) |
627 | return false; |
628 | } |
629 | return true; |
630 | } |
631 | |
632 | void QGeoPolygonPrivate::markDirty() |
633 | { |
634 | m_bboxDirty = m_clipperDirty = true; |
635 | } |
636 | |
637 | void QGeoPolygonPrivate::updateClipperPath() |
638 | { |
639 | if (m_bboxDirty) |
640 | computeBoundingBox(); |
641 | m_clipperDirty = false; |
642 | |
643 | QList<QDoubleVector2D> preservedPath; |
644 | for (const QGeoCoordinate &c : m_path) { |
645 | QDoubleVector2D crd = QWebMercator::coordToMercator(coord: c); |
646 | if (crd.x() < m_leftBoundWrapped) |
647 | crd.setX(crd.x() + 1.0); |
648 | preservedPath << crd; |
649 | } |
650 | m_clipperPath = QClipperUtils::qListToPath(list: preservedPath); |
651 | } |
652 | |
653 | QGeoPolygonPrivateEager::QGeoPolygonPrivateEager() : QGeoPolygonPrivate() |
654 | { |
655 | m_bboxDirty = false; // never dirty on the eager version |
656 | } |
657 | |
658 | QGeoPolygonPrivateEager::QGeoPolygonPrivateEager(const QList<QGeoCoordinate> &path) : QGeoPolygonPrivate(path) |
659 | { |
660 | m_bboxDirty = false; // never dirty on the eager version |
661 | } |
662 | |
663 | QGeoPolygonPrivateEager::~QGeoPolygonPrivateEager() |
664 | { |
665 | |
666 | } |
667 | |
668 | QGeoShapePrivate *QGeoPolygonPrivateEager::clone() const |
669 | { |
670 | return new QGeoPolygonPrivate(*this); |
671 | } |
672 | |
673 | void QGeoPolygonPrivateEager::translate(double degreesLatitude, double degreesLongitude) |
674 | { |
675 | translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati); |
676 | m_leftBoundWrapped = QWebMercator::coordToMercator(coord: m_bbox.topLeft()).x(); |
677 | m_clipperDirty = true; |
678 | } |
679 | |
680 | void QGeoPolygonPrivateEager::markDirty() |
681 | { |
682 | m_clipperDirty = true; |
683 | computeBoundingBox(); |
684 | } |
685 | |
686 | void QGeoPolygonPrivateEager::addCoordinate(const QGeoCoordinate &coordinate) |
687 | { |
688 | if (!coordinate.isValid()) |
689 | return; |
690 | m_path.append(t: coordinate); |
691 | m_clipperDirty = true; |
692 | updateBoundingBox(); // do not markDirty as it uses computeBoundingBox instead |
693 | } |
694 | |
695 | void QGeoPolygonPrivateEager::computeBoundingBox() |
696 | { |
697 | computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox); |
698 | m_leftBoundWrapped = QWebMercator::coordToMercator(coord: m_bbox.topLeft()).x(); |
699 | } |
700 | |
701 | void QGeoPolygonPrivateEager::updateBoundingBox() |
702 | { |
703 | updateBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox); |
704 | } |
705 | |
706 | QGeoPolygonEager::QGeoPolygonEager() : QGeoPolygon() |
707 | { |
708 | initPolygonConversions(); |
709 | d_ptr = new QGeoPolygonPrivateEager; |
710 | } |
711 | |
712 | QGeoPolygonEager::QGeoPolygonEager(const QList<QGeoCoordinate> &path) : QGeoPolygon() |
713 | { |
714 | initPolygonConversions(); |
715 | d_ptr = new QGeoPolygonPrivateEager(path); |
716 | } |
717 | |
718 | QGeoPolygonEager::QGeoPolygonEager(const QGeoPolygon &other) : QGeoPolygon() |
719 | { |
720 | initPolygonConversions(); |
721 | // without being able to dynamic_cast the d_ptr, only way to be sure is to reconstruct a new QGeoPolygonPrivateEager |
722 | d_ptr = new QGeoPolygonPrivateEager; |
723 | setPath(other.path()); |
724 | for (int i = 0; i < other.holesCount(); i++) |
725 | addHole(holePath: other.holePath(index: i)); |
726 | } |
727 | |
728 | QGeoPolygonEager::QGeoPolygonEager(const QGeoShape &other) : QGeoPolygon() |
729 | { |
730 | initPolygonConversions(); |
731 | if (other.type() == QGeoShape::PolygonType) |
732 | *this = QGeoPolygonEager(QGeoPolygon(other)); |
733 | else |
734 | d_ptr = new QGeoPolygonPrivateEager; |
735 | } |
736 | |
737 | QGeoPolygonEager::~QGeoPolygonEager() |
738 | { |
739 | |
740 | } |
741 | |
742 | QT_END_NAMESPACE |
743 | |