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 "qgeopath.h" |
41 | #include "qgeopolygon.h" |
42 | #include "qgeopath_p.h" |
43 | |
44 | #include "qgeocoordinate.h" |
45 | #include "qnumeric.h" |
46 | #include "qlocationutils_p.h" |
47 | #include "qwebmercator_p.h" |
48 | |
49 | #include "qdoublevector2d_p.h" |
50 | #include "qdoublevector3d_p.h" |
51 | QT_BEGIN_NAMESPACE |
52 | |
53 | /*! |
54 | \class QGeoPath |
55 | \inmodule QtPositioning |
56 | \ingroup QtPositioning-positioning |
57 | \since 5.9 |
58 | |
59 | \brief The QGeoPath class defines a geographic path. |
60 | |
61 | The path is defined by an ordered list of QGeoCoordinates. |
62 | |
63 | Each two adjacent elements in the path are intended to be connected |
64 | together by the shortest line segment of constant bearing passing |
65 | through both elements. |
66 | This type of connection can cross the dateline in the longitudinal direction, |
67 | but never crosses the poles. |
68 | |
69 | This is relevant for the calculation of the bounding box returned by |
70 | \l QGeoShape::boundingGeoRectangle() for this shape, which will have the latitude of |
71 | the top left corner set to the maximum latitude in the path point set. |
72 | Similarly, the latitude of the bottom right corner will be the minimum latitude |
73 | in the path point set. |
74 | |
75 | This class is a \l Q_GADGET. |
76 | It can be \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. |
77 | |
78 | A QGeoPath is both invalid and empty if it contains no coordinate. |
79 | |
80 | \note A default constructed QGeoPath is both invalid and empty as it does not contain any coordinates. |
81 | */ |
82 | |
83 | /*! |
84 | \property QGeoPath::path |
85 | \brief This property holds the list of coordinates for the geo path. |
86 | |
87 | \note The coordinates cannot be processed in place. To change the value |
88 | of this property, retrieve the complete list of coordinates, process them, |
89 | and assign the new value to the property. |
90 | */ |
91 | |
92 | inline QGeoPathPrivate *QGeoPath::d_func() |
93 | { |
94 | return static_cast<QGeoPathPrivate *>(d_ptr.data()); |
95 | } |
96 | |
97 | inline const QGeoPathPrivate *QGeoPath::d_func() const |
98 | { |
99 | return static_cast<const QGeoPathPrivate *>(d_ptr.constData()); |
100 | } |
101 | |
102 | struct PathVariantConversions |
103 | { |
104 | PathVariantConversions() |
105 | { |
106 | QMetaType::registerConverter<QGeoShape, QGeoPath>(); |
107 | QMetaType::registerConverter<QGeoPath, QGeoShape>(); |
108 | } |
109 | }; |
110 | |
111 | Q_GLOBAL_STATIC(PathVariantConversions, initPathConversions) |
112 | |
113 | /*! |
114 | Constructs a new, empty geo path. |
115 | */ |
116 | QGeoPath::QGeoPath() |
117 | : QGeoShape(new QGeoPathPrivate()) |
118 | { |
119 | initPathConversions(); |
120 | } |
121 | |
122 | /*! |
123 | Constructs a new geo path from a list of coordinates |
124 | (\a path and \a width). |
125 | */ |
126 | QGeoPath::QGeoPath(const QList<QGeoCoordinate> &path, const qreal &width) |
127 | : QGeoShape(new QGeoPathPrivate(path, width)) |
128 | { |
129 | initPathConversions(); |
130 | } |
131 | |
132 | /*! |
133 | Constructs a new geo path from the contents of \a other. |
134 | */ |
135 | QGeoPath::QGeoPath(const QGeoPath &other) |
136 | : QGeoShape(other) |
137 | { |
138 | initPathConversions(); |
139 | } |
140 | |
141 | /*! |
142 | Constructs a new geo path from the contents of \a other. |
143 | */ |
144 | QGeoPath::QGeoPath(const QGeoShape &other) |
145 | : QGeoShape(other) |
146 | { |
147 | initPathConversions(); |
148 | if (type() != QGeoShape::PathType) |
149 | d_ptr = new QGeoPathPrivate(); |
150 | } |
151 | |
152 | /*! |
153 | Destroys this path. |
154 | */ |
155 | QGeoPath::~QGeoPath() {} |
156 | |
157 | /*! |
158 | Assigns \a other to this geo path and returns a reference to this geo path. |
159 | */ |
160 | QGeoPath &QGeoPath::operator=(const QGeoPath &other) |
161 | { |
162 | QGeoShape::operator=(other); |
163 | return *this; |
164 | } |
165 | |
166 | /*! |
167 | Returns whether this geo path is equal to \a other. |
168 | */ |
169 | bool QGeoPath::operator==(const QGeoPath &other) const |
170 | { |
171 | Q_D(const QGeoPath); |
172 | return *d == *other.d_func(); |
173 | } |
174 | |
175 | /*! |
176 | Returns whether this geo path is not equal to \a other. |
177 | */ |
178 | bool QGeoPath::operator!=(const QGeoPath &other) const |
179 | { |
180 | Q_D(const QGeoPath); |
181 | return !(*d == *other.d_func()); |
182 | } |
183 | |
184 | /*! |
185 | Sets all the elements of the \a path. |
186 | */ |
187 | void QGeoPath::setPath(const QList<QGeoCoordinate> &path) |
188 | { |
189 | Q_D(QGeoPath); |
190 | return d->setPath(path); |
191 | } |
192 | |
193 | /*! |
194 | Returns all the elements of the path. |
195 | */ |
196 | const QList<QGeoCoordinate> &QGeoPath::path() const |
197 | { |
198 | Q_D(const QGeoPath); |
199 | return d->path(); |
200 | } |
201 | |
202 | /*! |
203 | Clears the path. |
204 | |
205 | \since 5.12 |
206 | */ |
207 | void QGeoPath::clearPath() |
208 | { |
209 | Q_D(QGeoPath); |
210 | d->clearPath(); |
211 | } |
212 | |
213 | /*! |
214 | Sets all the elements of the path. |
215 | |
216 | \internal |
217 | */ |
218 | void QGeoPath::setVariantPath(const QVariantList &path) |
219 | { |
220 | Q_D(QGeoPath); |
221 | QList<QGeoCoordinate> p; |
222 | for (const auto &c: path) { |
223 | if (c.canConvert<QGeoCoordinate>()) |
224 | p << c.value<QGeoCoordinate>(); |
225 | } |
226 | d->setPath(p); |
227 | } |
228 | /*! |
229 | Returns all the elements of the path. |
230 | |
231 | \internal |
232 | */ |
233 | QVariantList QGeoPath::variantPath() const |
234 | { |
235 | Q_D(const QGeoPath); |
236 | QVariantList p; |
237 | for (const auto &c: d->path()) |
238 | p << QVariant::fromValue(value: c); |
239 | return p; |
240 | } |
241 | |
242 | |
243 | /*! |
244 | \property QGeoPath::width |
245 | |
246 | \brief the width of the path in meters. |
247 | */ |
248 | void QGeoPath::setWidth(const qreal &width) |
249 | { |
250 | Q_D(QGeoPath); |
251 | d->setWidth(width); |
252 | } |
253 | |
254 | /*! |
255 | Returns the width of the path, in meters. This information is used in the \l contains method. |
256 | The default value is 0. |
257 | */ |
258 | qreal QGeoPath::width() const |
259 | { |
260 | Q_D(const QGeoPath); |
261 | return d->width(); |
262 | } |
263 | |
264 | /*! |
265 | Translates this geo path by \a degreesLatitude northwards and \a degreesLongitude eastwards. |
266 | |
267 | Negative values of \a degreesLatitude and \a degreesLongitude correspond to |
268 | southward and westward translation respectively. |
269 | */ |
270 | void QGeoPath::translate(double degreesLatitude, double degreesLongitude) |
271 | { |
272 | Q_D(QGeoPath); |
273 | d->translate(degreesLatitude, degreesLongitude); |
274 | } |
275 | |
276 | /*! |
277 | Returns a copy of this geo path translated by \a degreesLatitude northwards and |
278 | \a degreesLongitude eastwards. |
279 | |
280 | Negative values of \a degreesLatitude and \a degreesLongitude correspond to |
281 | southward and westward translation respectively. |
282 | |
283 | \sa translate() |
284 | */ |
285 | QGeoPath QGeoPath::translated(double degreesLatitude, double degreesLongitude) const |
286 | { |
287 | QGeoPath result(*this); |
288 | result.translate(degreesLatitude, degreesLongitude); |
289 | return result; |
290 | } |
291 | |
292 | /*! |
293 | Returns the length of the path, in meters, from the element \a indexFrom to the element \a indexTo. |
294 | The length is intended to be the sum of the shortest distances for each pair of adjacent points. |
295 | |
296 | If \a indexTo is -1 (the default value), the length will be including the distance between last coordinate |
297 | and the first (closed loop). |
298 | To retrieve the length for the path, use 0 for \a indexFrom and \l QGeoPath::size() - 1 for \a indexTo. |
299 | */ |
300 | double QGeoPath::length(int indexFrom, int indexTo) const |
301 | { |
302 | Q_D(const QGeoPath); |
303 | return d->length(indexFrom, indexTo); |
304 | } |
305 | |
306 | /*! |
307 | Returns the number of elements in the path. |
308 | |
309 | \since 5.10 |
310 | */ |
311 | int QGeoPath::size() const |
312 | { |
313 | Q_D(const QGeoPath); |
314 | return d->size(); |
315 | } |
316 | |
317 | /*! |
318 | Appends \a coordinate to the path. |
319 | */ |
320 | void QGeoPath::addCoordinate(const QGeoCoordinate &coordinate) |
321 | { |
322 | Q_D(QGeoPath); |
323 | d->addCoordinate(coordinate); |
324 | } |
325 | |
326 | /*! |
327 | Inserts \a coordinate at the specified \a index. |
328 | */ |
329 | void QGeoPath::insertCoordinate(int index, const QGeoCoordinate &coordinate) |
330 | { |
331 | Q_D(QGeoPath); |
332 | d->insertCoordinate(index, coordinate); |
333 | } |
334 | |
335 | /*! |
336 | Replaces the path element at the specified \a index with \a coordinate. |
337 | */ |
338 | void QGeoPath::replaceCoordinate(int index, const QGeoCoordinate &coordinate) |
339 | { |
340 | Q_D(QGeoPath); |
341 | d->replaceCoordinate(index, coordinate); |
342 | } |
343 | |
344 | /*! |
345 | Returns the coordinate at \a index . |
346 | */ |
347 | QGeoCoordinate QGeoPath::coordinateAt(int index) const |
348 | { |
349 | Q_D(const QGeoPath); |
350 | return d->coordinateAt(index); |
351 | } |
352 | |
353 | /*! |
354 | Returns true if the path contains \a coordinate as one of the elements. |
355 | */ |
356 | bool QGeoPath::containsCoordinate(const QGeoCoordinate &coordinate) const |
357 | { |
358 | Q_D(const QGeoPath); |
359 | return d->containsCoordinate(coordinate); |
360 | } |
361 | |
362 | /*! |
363 | Removes the last occurrence of \a coordinate from the path. |
364 | */ |
365 | void QGeoPath::removeCoordinate(const QGeoCoordinate &coordinate) |
366 | { |
367 | Q_D(QGeoPath); |
368 | d->removeCoordinate(coordinate); |
369 | } |
370 | |
371 | /*! |
372 | Removes element at position \a index from the path. |
373 | */ |
374 | void QGeoPath::removeCoordinate(int index) |
375 | { |
376 | Q_D(QGeoPath); |
377 | d->removeCoordinate(index); |
378 | } |
379 | |
380 | /*! |
381 | Returns the geo path properties as a string. |
382 | */ |
383 | QString QGeoPath::toString() const |
384 | { |
385 | if (type() != QGeoShape::PathType) { |
386 | qWarning(msg: "Not a path" ); |
387 | return QStringLiteral("QGeoPath(not a path)" ); |
388 | } |
389 | |
390 | QString pathString; |
391 | for (const auto &p : path()) |
392 | pathString += p.toString() + QLatin1Char(','); |
393 | |
394 | return QStringLiteral("QGeoPath([ %1 ])" ).arg(a: pathString); |
395 | } |
396 | |
397 | /******************************************************************************* |
398 | * |
399 | * QGeoPathPrivate & friends |
400 | * |
401 | *******************************************************************************/ |
402 | |
403 | QGeoPathPrivate::QGeoPathPrivate() |
404 | : QGeoShapePrivate(QGeoShape::PathType) |
405 | { |
406 | |
407 | } |
408 | |
409 | QGeoPathPrivate::QGeoPathPrivate(const QList<QGeoCoordinate> &path, const qreal width) |
410 | : QGeoShapePrivate(QGeoShape::PathType) |
411 | { |
412 | setPath(path); |
413 | setWidth(width); |
414 | } |
415 | |
416 | QGeoPathPrivate::~QGeoPathPrivate() |
417 | { |
418 | |
419 | } |
420 | |
421 | QGeoShapePrivate *QGeoPathPrivate::clone() const |
422 | { |
423 | return new QGeoPathPrivate(*this); |
424 | } |
425 | |
426 | bool QGeoPathPrivate::isValid() const |
427 | { |
428 | return !isEmpty(); |
429 | } |
430 | |
431 | bool QGeoPathPrivate::isEmpty() const |
432 | { |
433 | return path().isEmpty(); // this should perhaps return geometric emptiness, less than 2 points for line, or empty polygon for polygons |
434 | } |
435 | |
436 | QGeoCoordinate QGeoPathPrivate::center() const |
437 | { |
438 | return boundingGeoRectangle().center(); |
439 | } |
440 | |
441 | void QGeoPathPrivate::extendShape(const QGeoCoordinate &coordinate) |
442 | { |
443 | if (!coordinate.isValid() || contains(coordinate)) |
444 | return; |
445 | addCoordinate(coordinate); |
446 | } |
447 | |
448 | bool QGeoPathPrivate::operator==(const QGeoShapePrivate &other) const |
449 | { |
450 | if (!QGeoShapePrivate::operator==(other)) |
451 | return false; |
452 | |
453 | const QGeoPathPrivate &otherPath = static_cast<const QGeoPathPrivate &>(other); |
454 | if (m_path.size() != otherPath.m_path.size()) |
455 | return false; |
456 | return m_width == otherPath.m_width && m_path == otherPath.m_path; |
457 | } |
458 | |
459 | const QList<QGeoCoordinate> &QGeoPathPrivate::path() const |
460 | { |
461 | return m_path; |
462 | } |
463 | |
464 | bool QGeoPathPrivate::lineContains(const QGeoCoordinate &coordinate) const |
465 | { |
466 | // Unoptimized approach: |
467 | // - consider each segment of the path |
468 | // - project it into mercator space (rhumb lines are straight in mercator space) |
469 | // - find closest point to coordinate |
470 | // - unproject the closest point |
471 | // - calculate coordinate to closest point distance with distanceTo() |
472 | // - if not within lineRadius, advance |
473 | // |
474 | // To keep wrapping into the equation: |
475 | // If the mercator x value of a coordinate of the line, or the coordinate parameter, is less |
476 | // than mercator(m_bbox).x, add that to the conversion. |
477 | |
478 | if (m_bboxDirty) |
479 | const_cast<QGeoPathPrivate &>(*this).computeBoundingBox(); |
480 | |
481 | double lineRadius = qMax(a: width() * 0.5, b: 0.2); // minimum radius: 20cm |
482 | |
483 | if (!m_path.size()) |
484 | return false; |
485 | else if (m_path.size() == 1) |
486 | return (m_path[0].distanceTo(other: coordinate) <= lineRadius); |
487 | |
488 | QDoubleVector2D p = QWebMercator::coordToMercator(coord: coordinate); |
489 | if (p.x() < m_leftBoundWrapped) |
490 | p.setX(p.x() + m_leftBoundWrapped); // unwrap X |
491 | |
492 | QDoubleVector2D a; |
493 | QDoubleVector2D b; |
494 | if (m_path.size()) { |
495 | a = QWebMercator::coordToMercator(coord: m_path[0]); |
496 | if (a.x() < m_leftBoundWrapped) |
497 | a.setX(a.x() + m_leftBoundWrapped); // unwrap X |
498 | } |
499 | for (int i = 1; i < m_path.size(); i++) { |
500 | b = QWebMercator::coordToMercator(coord: m_path[i]); |
501 | if (b.x() < m_leftBoundWrapped) |
502 | b.setX(b.x() + m_leftBoundWrapped); // unwrap X |
503 | if (b == a) |
504 | continue; |
505 | |
506 | double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y()) ) / (b - a).lengthSquared(); |
507 | QDoubleVector2D intersection(a.x() + u * (b.x() - a.x()) , a.y() + u * (b.y() - a.y()) ); |
508 | |
509 | QDoubleVector2D candidate = ( (p-a).length() < (p-b).length() ) ? a : b; |
510 | |
511 | if (u > 0 && u < 1 |
512 | && (p-intersection).length() < (p-candidate).length() ) // And it falls in the segment |
513 | candidate = intersection; |
514 | |
515 | |
516 | if (candidate.x() > 1.0) |
517 | candidate.setX(candidate.x() - m_leftBoundWrapped); // wrap X |
518 | |
519 | QGeoCoordinate closest = QWebMercator::mercatorToCoord(mercator: candidate); |
520 | |
521 | double distanceMeters = coordinate.distanceTo(other: closest); |
522 | if (distanceMeters <= lineRadius) |
523 | return true; |
524 | |
525 | // swap |
526 | a = b; |
527 | } |
528 | |
529 | // Last check if the coordinate is on the left of leftBoundMercator, but close enough to |
530 | // m_path[0] |
531 | return (m_path[0].distanceTo(other: coordinate) <= lineRadius); |
532 | } |
533 | |
534 | bool QGeoPathPrivate::contains(const QGeoCoordinate &coordinate) const |
535 | { |
536 | return lineContains(coordinate); |
537 | } |
538 | |
539 | qreal QGeoPathPrivate::width() const |
540 | { |
541 | return m_width; |
542 | } |
543 | |
544 | void QGeoPathPrivate::setWidth(const qreal &width) |
545 | { |
546 | if (qIsNaN(d: width) || width < 0.0) |
547 | return; |
548 | m_width = width; |
549 | } |
550 | |
551 | double QGeoPathPrivate::length(int indexFrom, int indexTo) const |
552 | { |
553 | if (path().isEmpty()) |
554 | return 0.0; |
555 | |
556 | bool wrap = indexTo == -1; |
557 | if (indexTo < 0 || indexTo >= path().size()) |
558 | indexTo = path().size() - 1; |
559 | double len = 0.0; |
560 | // TODO: consider calculating the length of the actual rhumb line segments |
561 | // instead of the shortest path from A to B. |
562 | for (int i = indexFrom; i < indexTo; i++) |
563 | len += m_path[i].distanceTo(other: m_path[i+1]); |
564 | if (wrap) |
565 | len += m_path.last().distanceTo(other: m_path.first()); |
566 | return len; |
567 | } |
568 | |
569 | int QGeoPathPrivate::size() const |
570 | { |
571 | return m_path.size(); |
572 | } |
573 | |
574 | QGeoCoordinate QGeoPathPrivate::coordinateAt(int index) const |
575 | { |
576 | if (index < 0 || index >= m_path.size()) |
577 | return QGeoCoordinate(); |
578 | |
579 | return m_path.at(i: index); |
580 | } |
581 | |
582 | bool QGeoPathPrivate::containsCoordinate(const QGeoCoordinate &coordinate) const |
583 | { |
584 | return m_path.indexOf(t: coordinate) > -1; |
585 | } |
586 | |
587 | void QGeoPathPrivate::translate(double degreesLatitude, double degreesLongitude) |
588 | { |
589 | // Need min/maxLati, so update bbox |
590 | QVector<double> m_deltaXs; |
591 | double m_minX, m_maxX, m_minLati, m_maxLati; |
592 | m_bboxDirty = false; |
593 | computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox); |
594 | |
595 | if (degreesLatitude > 0.0) |
596 | degreesLatitude = qMin(a: degreesLatitude, b: 90.0 - m_maxLati); |
597 | else |
598 | degreesLatitude = qMax(a: degreesLatitude, b: -90.0 - m_minLati); |
599 | for (QGeoCoordinate &p: m_path) { |
600 | p.setLatitude(p.latitude() + degreesLatitude); |
601 | p.setLongitude(QLocationUtils::wrapLong(lng: p.longitude() + degreesLongitude)); |
602 | } |
603 | m_bbox.translate(degreesLatitude, degreesLongitude); |
604 | m_leftBoundWrapped = QWebMercator::coordToMercator(coord: m_bbox.topLeft()).x(); |
605 | } |
606 | |
607 | QGeoRectangle QGeoPathPrivate::boundingGeoRectangle() const |
608 | { |
609 | if (m_bboxDirty) |
610 | const_cast<QGeoPathPrivate &>(*this).computeBoundingBox(); |
611 | return m_bbox; |
612 | } |
613 | |
614 | void QGeoPathPrivate::setPath(const QList<QGeoCoordinate> &path) |
615 | { |
616 | for (const QGeoCoordinate &c: path) |
617 | if (!c.isValid()) |
618 | return; |
619 | m_path = path; |
620 | markDirty(); |
621 | } |
622 | |
623 | void QGeoPathPrivate::clearPath() |
624 | { |
625 | m_path.clear(); |
626 | markDirty(); |
627 | } |
628 | |
629 | void QGeoPathPrivate::addCoordinate(const QGeoCoordinate &coordinate) |
630 | { |
631 | if (!coordinate.isValid()) |
632 | return; |
633 | m_path.append(t: coordinate); |
634 | markDirty(); |
635 | } |
636 | |
637 | void QGeoPathPrivate::insertCoordinate(int index, const QGeoCoordinate &coordinate) |
638 | { |
639 | if (index < 0 || index > m_path.size() || !coordinate.isValid()) |
640 | return; |
641 | m_path.insert(i: index, t: coordinate); |
642 | markDirty(); |
643 | } |
644 | |
645 | void QGeoPathPrivate::replaceCoordinate(int index, const QGeoCoordinate &coordinate) |
646 | { |
647 | if (index < 0 || index >= m_path.size() || !coordinate.isValid()) |
648 | return; |
649 | m_path[index] = coordinate; |
650 | markDirty(); |
651 | } |
652 | |
653 | void QGeoPathPrivate::removeCoordinate(const QGeoCoordinate &coordinate) |
654 | { |
655 | int index = m_path.lastIndexOf(t: coordinate); |
656 | removeCoordinate(index); |
657 | } |
658 | |
659 | void QGeoPathPrivate::removeCoordinate(int index) |
660 | { |
661 | if (index < 0 || index >= m_path.size()) |
662 | return; |
663 | m_path.removeAt(i: index); |
664 | markDirty(); |
665 | } |
666 | |
667 | void QGeoPathPrivate::markDirty() |
668 | { |
669 | m_bboxDirty = true; |
670 | } |
671 | |
672 | void QGeoPathPrivate::computeBoundingBox() |
673 | { |
674 | QVector<double> m_deltaXs; |
675 | double m_minX, m_maxX, m_minLati, m_maxLati; |
676 | m_bboxDirty = false; |
677 | computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox); |
678 | m_leftBoundWrapped = QWebMercator::coordToMercator(coord: m_bbox.topLeft()).x(); |
679 | } |
680 | |
681 | QGeoPathPrivateEager::QGeoPathPrivateEager() |
682 | : QGeoPathPrivate() |
683 | { |
684 | m_bboxDirty = false; // never dirty on the eager version |
685 | } |
686 | |
687 | QGeoPathPrivateEager::QGeoPathPrivateEager(const QList<QGeoCoordinate> &path, const qreal width) |
688 | : QGeoPathPrivate(path, width) |
689 | { |
690 | m_bboxDirty = false; // never dirty on the eager version |
691 | } |
692 | |
693 | QGeoPathPrivateEager::~QGeoPathPrivateEager() |
694 | { |
695 | |
696 | } |
697 | |
698 | QGeoShapePrivate *QGeoPathPrivateEager::clone() const |
699 | { |
700 | return new QGeoPathPrivateEager(*this); |
701 | } |
702 | |
703 | void QGeoPathPrivateEager::markDirty() |
704 | { |
705 | computeBoundingBox(); |
706 | } |
707 | |
708 | void QGeoPathPrivateEager::translate(double degreesLatitude, double degreesLongitude) |
709 | { |
710 | if (degreesLatitude > 0.0) |
711 | degreesLatitude = qMin(a: degreesLatitude, b: 90.0 - m_maxLati); |
712 | else |
713 | degreesLatitude = qMax(a: degreesLatitude, b: -90.0 - m_minLati); |
714 | for (QGeoCoordinate &p: m_path) { |
715 | p.setLatitude(p.latitude() + degreesLatitude); |
716 | p.setLongitude(QLocationUtils::wrapLong(lng: p.longitude() + degreesLongitude)); |
717 | } |
718 | m_bbox.translate(degreesLatitude, degreesLongitude); |
719 | m_minLati += degreesLatitude; |
720 | m_maxLati += degreesLatitude; |
721 | m_leftBoundWrapped = QWebMercator::coordToMercator(coord: m_bbox.topLeft()).x(); |
722 | } |
723 | |
724 | void QGeoPathPrivateEager::addCoordinate(const QGeoCoordinate &coordinate) |
725 | { |
726 | if (!coordinate.isValid()) |
727 | return; |
728 | m_path.append(t: coordinate); |
729 | //m_clipperDirty = true; // clipper not used in polylines |
730 | updateBoundingBox(); |
731 | } |
732 | |
733 | void QGeoPathPrivateEager::QGeoPathPrivateEager::computeBoundingBox() |
734 | { |
735 | computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox); |
736 | m_leftBoundWrapped = QWebMercator::coordToMercator(coord: m_bbox.topLeft()).x(); |
737 | } |
738 | |
739 | void QGeoPathPrivateEager::QGeoPathPrivateEager::updateBoundingBox() |
740 | { |
741 | updateBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox); |
742 | m_leftBoundWrapped = QWebMercator::coordToMercator(coord: m_bbox.topLeft()).x(); |
743 | } |
744 | |
745 | QGeoPathEager::QGeoPathEager() : QGeoPath() |
746 | { |
747 | initPathConversions(); |
748 | d_ptr = new QGeoPathPrivateEager; |
749 | } |
750 | |
751 | QGeoPathEager::QGeoPathEager(const QList<QGeoCoordinate> &path, const qreal &width) : QGeoPath() |
752 | { |
753 | initPathConversions(); |
754 | d_ptr = new QGeoPathPrivateEager(path, width); |
755 | } |
756 | |
757 | QGeoPathEager::QGeoPathEager(const QGeoPath &other) : QGeoPath() |
758 | { |
759 | initPathConversions(); |
760 | d_ptr = new QGeoPathPrivateEager; |
761 | setPath(other.path()); |
762 | setWidth(other.width()); |
763 | } |
764 | |
765 | QGeoPathEager::QGeoPathEager(const QGeoShape &other) : QGeoPath() |
766 | { |
767 | initPathConversions(); |
768 | if (other.type() == QGeoShape::PathType) |
769 | *this = QGeoPathEager(QGeoPath(other)); |
770 | else |
771 | d_ptr = new QGeoPathPrivateEager; |
772 | } |
773 | |
774 | QGeoPathEager::~QGeoPathEager() {} |
775 | |
776 | QT_END_NAMESPACE |
777 | |
778 | |
779 | |
780 | |
781 | |
782 | |
783 | |
784 | |
785 | |