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"
51QT_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
92inline QGeoPathPrivate *QGeoPath::d_func()
93{
94 return static_cast<QGeoPathPrivate *>(d_ptr.data());
95}
96
97inline const QGeoPathPrivate *QGeoPath::d_func() const
98{
99 return static_cast<const QGeoPathPrivate *>(d_ptr.constData());
100}
101
102struct PathVariantConversions
103{
104 PathVariantConversions()
105 {
106 QMetaType::registerConverter<QGeoShape, QGeoPath>();
107 QMetaType::registerConverter<QGeoPath, QGeoShape>();
108 }
109};
110
111Q_GLOBAL_STATIC(PathVariantConversions, initPathConversions)
112
113/*!
114 Constructs a new, empty geo path.
115*/
116QGeoPath::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*/
126QGeoPath::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*/
135QGeoPath::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*/
144QGeoPath::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*/
155QGeoPath::~QGeoPath() {}
156
157/*!
158 Assigns \a other to this geo path and returns a reference to this geo path.
159*/
160QGeoPath &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*/
169bool 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*/
178bool 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*/
187void 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*/
196const 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*/
207void 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*/
218void 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*/
233QVariantList 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*/
248void 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*/
258qreal 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*/
270void 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*/
285QGeoPath 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*/
300double 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*/
311int QGeoPath::size() const
312{
313 Q_D(const QGeoPath);
314 return d->size();
315}
316
317/*!
318 Appends \a coordinate to the path.
319*/
320void 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*/
329void 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*/
338void 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*/
347QGeoCoordinate 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*/
356bool 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*/
365void 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*/
374void 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*/
383QString 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
403QGeoPathPrivate::QGeoPathPrivate()
404: QGeoShapePrivate(QGeoShape::PathType)
405{
406
407}
408
409QGeoPathPrivate::QGeoPathPrivate(const QList<QGeoCoordinate> &path, const qreal width)
410: QGeoShapePrivate(QGeoShape::PathType)
411{
412 setPath(path);
413 setWidth(width);
414}
415
416QGeoPathPrivate::~QGeoPathPrivate()
417{
418
419}
420
421QGeoShapePrivate *QGeoPathPrivate::clone() const
422{
423 return new QGeoPathPrivate(*this);
424}
425
426bool QGeoPathPrivate::isValid() const
427{
428 return !isEmpty();
429}
430
431bool 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
436QGeoCoordinate QGeoPathPrivate::center() const
437{
438 return boundingGeoRectangle().center();
439}
440
441void QGeoPathPrivate::extendShape(const QGeoCoordinate &coordinate)
442{
443 if (!coordinate.isValid() || contains(coordinate))
444 return;
445 addCoordinate(coordinate);
446}
447
448bool 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
459const QList<QGeoCoordinate> &QGeoPathPrivate::path() const
460{
461 return m_path;
462}
463
464bool 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
534bool QGeoPathPrivate::contains(const QGeoCoordinate &coordinate) const
535{
536 return lineContains(coordinate);
537}
538
539qreal QGeoPathPrivate::width() const
540{
541 return m_width;
542}
543
544void QGeoPathPrivate::setWidth(const qreal &width)
545{
546 if (qIsNaN(d: width) || width < 0.0)
547 return;
548 m_width = width;
549}
550
551double 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
569int QGeoPathPrivate::size() const
570{
571 return m_path.size();
572}
573
574QGeoCoordinate 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
582bool QGeoPathPrivate::containsCoordinate(const QGeoCoordinate &coordinate) const
583{
584 return m_path.indexOf(t: coordinate) > -1;
585}
586
587void 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
607QGeoRectangle QGeoPathPrivate::boundingGeoRectangle() const
608{
609 if (m_bboxDirty)
610 const_cast<QGeoPathPrivate &>(*this).computeBoundingBox();
611 return m_bbox;
612}
613
614void 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
623void QGeoPathPrivate::clearPath()
624{
625 m_path.clear();
626 markDirty();
627}
628
629void QGeoPathPrivate::addCoordinate(const QGeoCoordinate &coordinate)
630{
631 if (!coordinate.isValid())
632 return;
633 m_path.append(t: coordinate);
634 markDirty();
635}
636
637void 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
645void 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
653void QGeoPathPrivate::removeCoordinate(const QGeoCoordinate &coordinate)
654{
655 int index = m_path.lastIndexOf(t: coordinate);
656 removeCoordinate(index);
657}
658
659void 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
667void QGeoPathPrivate::markDirty()
668{
669 m_bboxDirty = true;
670}
671
672void 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
681QGeoPathPrivateEager::QGeoPathPrivateEager()
682: QGeoPathPrivate()
683{
684 m_bboxDirty = false; // never dirty on the eager version
685}
686
687QGeoPathPrivateEager::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
693QGeoPathPrivateEager::~QGeoPathPrivateEager()
694{
695
696}
697
698QGeoShapePrivate *QGeoPathPrivateEager::clone() const
699{
700 return new QGeoPathPrivateEager(*this);
701}
702
703void QGeoPathPrivateEager::markDirty()
704{
705 computeBoundingBox();
706}
707
708void 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
724void 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
733void 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
739void 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
745QGeoPathEager::QGeoPathEager() : QGeoPath()
746{
747 initPathConversions();
748 d_ptr = new QGeoPathPrivateEager;
749}
750
751QGeoPathEager::QGeoPathEager(const QList<QGeoCoordinate> &path, const qreal &width) : QGeoPath()
752{
753 initPathConversions();
754 d_ptr = new QGeoPathPrivateEager(path, width);
755}
756
757QGeoPathEager::QGeoPathEager(const QGeoPath &other) : QGeoPath()
758{
759 initPathConversions();
760 d_ptr = new QGeoPathPrivateEager;
761 setPath(other.path());
762 setWidth(other.width());
763}
764
765QGeoPathEager::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
774QGeoPathEager::~QGeoPathEager() {}
775
776QT_END_NAMESPACE
777
778
779
780
781
782
783
784
785

source code of qtlocation/src/positioning/qgeopath.cpp