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 #ifndef QGEOPATH_P_H
41 #define QGEOPATH_P_H
42 
43 //
44 //  W A R N I N G
45 //  -------------
46 //
47 // This file is not part of the Qt API.  It exists purely as an
48 // implementation detail.  This header file may change from version to
49 // version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53 
54 #include <QtPositioning/private/qpositioningglobal_p.h>
55 #include "qgeoshape_p.h"
56 #include "qgeocoordinate.h"
57 #include "qlocationutils_p.h"
58 #include <QtPositioning/qgeopath.h>
59 #include <QtCore/QVector>
60 
61 QT_BEGIN_NAMESPACE
62 
computeBBox(const QList<QGeoCoordinate> & m_path,QVector<double> & m_deltaXs,double & m_minX,double & m_maxX,double & m_minLati,double & m_maxLati,QGeoRectangle & m_bbox)63 inline static void computeBBox( const QList<QGeoCoordinate> &m_path,
64                                 QVector<double> &m_deltaXs,
65                                 double &m_minX,
66                                 double &m_maxX,
67                                 double &m_minLati,
68                                 double &m_maxLati,
69                                 QGeoRectangle &m_bbox)
70 {
71     if (m_path.isEmpty()) {
72         m_deltaXs.clear();
73         m_minX = qInf();
74         m_maxX = -qInf();
75         m_minLati = qInf();
76         m_maxLati = -qInf();
77         m_bbox = QGeoRectangle();
78         return;
79     }
80 
81     m_minLati = m_maxLati = m_path.at(0).latitude();
82     int minId = 0;
83     int maxId = 0;
84     m_deltaXs.resize(m_path.size());
85     m_deltaXs[0] = m_minX = m_maxX = 0.0;
86 
87     for (int i = 1; i < m_path.size(); i++) {
88         const QGeoCoordinate &geoFrom = m_path.at(i-1);
89         const QGeoCoordinate &geoTo   = m_path.at(i);
90         double longiFrom    = geoFrom.longitude();
91         double longiTo      = geoTo.longitude();
92         double deltaLongi = longiTo - longiFrom;
93         if (qAbs(deltaLongi) > 180.0) {
94             if (longiTo > 0.0)
95                 longiTo -= 360.0;
96             else
97                 longiTo += 360.0;
98             deltaLongi =  longiTo - longiFrom;
99         }
100         m_deltaXs[i] = m_deltaXs[i-1] + deltaLongi;
101         if (m_deltaXs[i] < m_minX) {
102             m_minX = m_deltaXs[i];
103             minId = i;
104         }
105         if (m_deltaXs[i] > m_maxX) {
106             m_maxX = m_deltaXs[i];
107             maxId = i;
108         }
109         if (geoTo.latitude() > m_maxLati)
110             m_maxLati = geoTo.latitude();
111         if (geoTo.latitude() < m_minLati)
112             m_minLati = geoTo.latitude();
113     }
114 
115     m_bbox = QGeoRectangle(QGeoCoordinate(m_maxLati, m_path.at(minId).longitude()),
116                            QGeoCoordinate(m_minLati, m_path.at(maxId).longitude()));
117 }
118 
updateBBox(const QList<QGeoCoordinate> & m_path,QVector<double> & m_deltaXs,double & m_minX,double & m_maxX,double & m_minLati,double & m_maxLati,QGeoRectangle & m_bbox)119 inline static void updateBBox( const QList<QGeoCoordinate> &m_path,
120                                 QVector<double> &m_deltaXs,
121                                 double &m_minX,
122                                 double &m_maxX,
123                                 double &m_minLati,
124                                 double &m_maxLati,
125                                 QGeoRectangle &m_bbox)
126 {
127     if (m_path.isEmpty()) {
128         m_deltaXs.clear();
129         m_minX = qInf();
130         m_maxX = -qInf();
131         m_minLati = qInf();
132         m_maxLati = -qInf();
133         m_bbox = QGeoRectangle();
134         return;
135     } else if (m_path.size() == 1) { // was 0  now is 1
136         m_deltaXs.resize(1);
137         m_deltaXs[0] = m_minX = m_maxX = 0.0;
138         m_minLati = m_maxLati = m_path.at(0).latitude();
139         m_bbox = QGeoRectangle(QGeoCoordinate(m_maxLati, m_path.at(0).longitude()),
140                                QGeoCoordinate(m_minLati, m_path.at(0).longitude()));
141         return;
142     } else if ( m_path.size() != m_deltaXs.size() + 1 ) {  // this case should not happen
143         computeBBox(m_path, m_deltaXs, m_minX, m_maxX, m_minLati, m_maxLati, m_bbox); // something went wrong
144         return;
145     }
146 
147     const QGeoCoordinate &geoFrom = m_path.at(m_path.size()-2);
148     const QGeoCoordinate &geoTo   = m_path.last();
149     double longiFrom    = geoFrom.longitude();
150     double longiTo      = geoTo.longitude();
151     double deltaLongi = longiTo - longiFrom;
152     if (qAbs(deltaLongi) > 180.0) {
153         if (longiTo > 0.0)
154             longiTo -= 360.0;
155         else
156             longiTo += 360.0;
157         deltaLongi =  longiTo - longiFrom;
158     }
159 
160     m_deltaXs.push_back(m_deltaXs.last() + deltaLongi);
161     double currentMinLongi = m_bbox.topLeft().longitude();
162     double currentMaxLongi = m_bbox.bottomRight().longitude();
163     if (m_deltaXs.last() < m_minX) {
164         m_minX = m_deltaXs.last();
165         currentMinLongi = geoTo.longitude();
166     }
167     if (m_deltaXs.last() > m_maxX) {
168         m_maxX = m_deltaXs.last();
169         currentMaxLongi = geoTo.longitude();
170     }
171     if (geoTo.latitude() > m_maxLati)
172         m_maxLati = geoTo.latitude();
173     if (geoTo.latitude() < m_minLati)
174         m_minLati = geoTo.latitude();
175     m_bbox = QGeoRectangle(QGeoCoordinate(m_maxLati, currentMinLongi),
176                            QGeoCoordinate(m_minLati, currentMaxLongi));
177 }
178 
179 // Lazy by default. Eager, within the module, used only in MapItems/MapObjectsQSG
180 class Q_POSITIONING_PRIVATE_EXPORT QGeoPathPrivate : public QGeoShapePrivate
181 {
182 public:
183     QGeoPathPrivate();
184     QGeoPathPrivate(const QList<QGeoCoordinate> &path, const qreal width = 0.0);
185     ~QGeoPathPrivate();
186 
187 // QGeoShape API
188     virtual QGeoShapePrivate *clone() const override;
189     virtual bool isValid() const override;
190     virtual bool isEmpty() const override;
191     virtual QGeoCoordinate center() const override;
192     virtual bool operator==(const QGeoShapePrivate &other) const override;
193     virtual bool contains(const QGeoCoordinate &coordinate) const override;
194     virtual QGeoRectangle boundingGeoRectangle() const override;
195 
196     virtual void extendShape(const QGeoCoordinate &coordinate) override;
197 
198 // QGeoPathPrivate API
199     virtual const QList<QGeoCoordinate> &path() const;
200     virtual bool lineContains(const QGeoCoordinate &coordinate) const;
201     virtual qreal width() const;
202     virtual double length(int indexFrom, int indexTo) const;
203     virtual int size() const;
204     virtual QGeoCoordinate coordinateAt(int index) const;
205     virtual bool containsCoordinate(const QGeoCoordinate &coordinate) const;
206 
207     virtual void setWidth(const qreal &width);
208     virtual void translate(double degreesLatitude, double degreesLongitude);
209     virtual void setPath(const QList<QGeoCoordinate> &path);
210     virtual void clearPath();
211     virtual void addCoordinate(const QGeoCoordinate &coordinate);
212     virtual void insertCoordinate(int index, const QGeoCoordinate &coordinate);
213     virtual void replaceCoordinate(int index, const QGeoCoordinate &coordinate);
214     virtual void removeCoordinate(const QGeoCoordinate &coordinate);
215     virtual void removeCoordinate(int index);
216     virtual void computeBoundingBox();
217     virtual void markDirty();
218 
219 // data members
220     QList<QGeoCoordinate> m_path;
221     qreal m_width = 0;
222     QGeoRectangle m_bbox; // cached
223     double m_leftBoundWrapped; // cached
224     bool m_bboxDirty = false;
225 };
226 
227 class Q_POSITIONING_PRIVATE_EXPORT QGeoPathPrivateEager : public QGeoPathPrivate
228 {
229 public:
230     QGeoPathPrivateEager();
231     QGeoPathPrivateEager(const QList<QGeoCoordinate> &path, const qreal width = 0.0);
232     ~QGeoPathPrivateEager();
233 
234 // QGeoShapePrivate API
235     virtual QGeoShapePrivate *clone() const override;
236     virtual void translate(double degreesLatitude, double degreesLongitude) override;
237 
238 // QGeoShapePrivate API
239     virtual void markDirty() override;
240     virtual void addCoordinate(const QGeoCoordinate &coordinate) override;
241     virtual void computeBoundingBox() override;
242 
243 // *Eager API
244     void updateBoundingBox();
245 
246 // data members
247     QVector<double> m_deltaXs;      // longitude deltas from m_path[0]
248     double m_minX = 0;              // minimum value inside deltaXs
249     double m_maxX = 0;              // maximum value inside deltaXs
250     double m_minLati = 0;           // minimum latitude. paths do not wrap around through the poles
251     double m_maxLati = 0;           // minimum latitude. paths do not wrap around through the poles
252 };
253 
254 // This is a mean of creating a QGeoPathPrivateEager and injecting it into QGeoPaths via operator=
255 class Q_POSITIONING_PRIVATE_EXPORT QGeoPathEager : public QGeoPath
256 {
257     Q_GADGET
258 public:
259 
260     QGeoPathEager();
261     QGeoPathEager(const QList<QGeoCoordinate> &path, const qreal &width = 0.0);
262     QGeoPathEager(const QGeoPath &other);
263     QGeoPathEager(const QGeoShape &other);
264     ~QGeoPathEager();
265 };
266 
267 QT_END_NAMESPACE
268 
269 #endif // QGEOPATH_P_H
270