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
qgeocircle.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
4
5#include "qgeocircle.h"
6#include "qgeocircle_p.h"
7
9#include "qnumeric.h"
11
14#include <cmath>
16
17QT_IMPL_METATYPE_EXTERN(QGeoCircle)
18
19/*!
20 \class QGeoCircle
21 \inmodule QtPositioning
22 \ingroup QtPositioning-positioning
23 \since 5.2
24
25 \brief The QGeoCircle class defines a circular geographic area.
26
27 The circle is defined in terms of a QGeoCoordinate which specifies the
28 center of the circle and a qreal which specifies the radius of the circle
29 in meters.
30
31 The circle is considered invalid if the center coordinate is invalid
32 or if the radius is less than zero.
33
34 This class is also accessible in QML as \l[QML]{geoCircle}.
35*/
36
37/*!
38 \property QGeoCircle::center
39 \brief This property holds the center coordinate for the geo circle.
40
41 The circle is considered invalid if this property contains an invalid
42 coordinate.
43
44 A default constructed QGeoCircle uses an invalid \l QGeoCoordinate
45 as center.
46
47 While this property is introduced in Qt 5.5, the related accessor functions
48 exist since the first version of this class.
49
50 \since 5.5
51*/
52
53/*!
54 \property QGeoCircle::radius
55 \brief This property holds the circle radius in meters.
56
57 The circle is considered invalid if this property is negative.
58
59 By default, the radius is initialized with \c -1.
60
61 While this property is introduced in Qt 5.5, the related accessor functions
62 exist since the first version of this class.
63
64 \since 5.5
65*/
66
67inline QGeoCirclePrivate *QGeoCircle::d_func()
68{
69 return static_cast<QGeoCirclePrivate *>(d_ptr.data());
70}
71
72inline const QGeoCirclePrivate *QGeoCircle::d_func() const
73{
74 return static_cast<const QGeoCirclePrivate *>(d_ptr.constData());
75}
76
77/*!
78 Constructs a new, invalid geo circle.
79*/
80QGeoCircle::QGeoCircle()
81: QGeoShape(new QGeoCirclePrivate)
82{
83}
84
85/*!
86 Constructs a new geo circle centered at \a center and with a radius of \a radius meters.
87*/
88QGeoCircle::QGeoCircle(const QGeoCoordinate &center, qreal radius)
89{
90 d_ptr = new QGeoCirclePrivate(center, radius);
91}
92
93/*!
94 Constructs a new geo circle from the contents of \a other.
95*/
96QGeoCircle::QGeoCircle(const QGeoCircle &other)
97: QGeoShape(other)
98{
99}
100
101/*!
102 Constructs a new geo circle from the contents of \a other.
103*/
104QGeoCircle::QGeoCircle(const QGeoShape &other)
105: QGeoShape(other)
106{
107 if (type() != QGeoShape::CircleType)
108 d_ptr = new QGeoCirclePrivate;
109}
110
111/*!
112 Destroys this geo circle.
113*/
114QGeoCircle::~QGeoCircle() {}
115
116/*!
117 Assigns \a other to this geo circle and returns a reference to this geo circle.
118*/
119QGeoCircle &QGeoCircle::operator=(const QGeoCircle &other)
120{
121 QGeoShape::operator=(other);
122 return *this;
123}
124/*!
125 * \class QGeoCirclePrivate
126 * \inmodule QtPositioning
127 * \internal
128 */
129bool QGeoCirclePrivate::isValid() const
130{
131 return m_center.isValid() && !qIsNaN(m_radius) && m_radius >= -1e-7;
132}
133
134bool QGeoCirclePrivate::isEmpty() const
135{
136 return !isValid() || m_radius <= 1e-7;
137}
138
139/*!
140 Sets the center coordinate of this geo circle to \a center.
141*/
142void QGeoCircle::setCenter(const QGeoCoordinate &center)
143{
144 Q_D(QGeoCircle);
145
146 d->setCenter(center);
147}
148
149/*!
150 Returns the center coordinate of this geo circle. Equivalent to QGeoShape::center().
151*/
152QGeoCoordinate QGeoCircle::center() const
153{
154 Q_D(const QGeoCircle);
155
156 return d->center();
157}
158
159/*!
160 Sets the radius in meters of this geo circle to \a radius.
161*/
162void QGeoCircle::setRadius(qreal radius)
163{
164 Q_D(QGeoCircle);
165
166 d->setRadius(radius);
167}
168
169/*!
170 Returns the radius in meters of this geo circle.
171*/
172qreal QGeoCircle::radius() const
173{
174 Q_D(const QGeoCircle);
175
176 return d->m_radius;
177}
178
179bool QGeoCirclePrivate::contains(const QGeoCoordinate &coordinate) const
180{
181 if (!isValid() || !coordinate.isValid())
182 return false;
183
184 // see QTBUG-41447 for details
185 qreal distance = m_center.distanceTo(coordinate);
186 if (distance <= m_radius || QtPrivate::fuzzyCompare(distance, m_radius))
187 return true;
188
189 return false;
190}
191
192QGeoCoordinate QGeoCirclePrivate::center() const
193{
194 return m_center;
195}
196
197QGeoRectangle QGeoCirclePrivate::boundingGeoRectangle() const
198{
199 return m_bbox;
200}
201
202void QGeoCirclePrivate::updateBoundingBox()
203{
204 if (isEmpty()) {
205 if (m_center.isValid()) {
206 m_bbox.setTopLeft(m_center);
207 m_bbox.setBottomRight(m_center);
208 }
209 return;
210 }
211
212 bool crossNorth = crossNorthPole();
213 bool crossSouth = crossSouthPole();
214
215 if (crossNorth && crossSouth) {
216 // Circle crossing both poles fills the whole map
217 m_bbox = QGeoRectangle(QGeoCoordinate(90.0, -180.0), QGeoCoordinate(-90.0, 180.0));
218 } else if (crossNorth) {
219 // Circle crossing one pole fills the map in the longitudinal direction
220 m_bbox = QGeoRectangle(QGeoCoordinate(90.0, -180.0), QGeoCoordinate(m_center.atDistanceAndAzimuth(m_radius, 180.0).latitude(), 180.0));
221 } else if (crossSouth) {
222 m_bbox = QGeoRectangle(QGeoCoordinate(m_center.atDistanceAndAzimuth(m_radius, 0.0).latitude(), -180.0), QGeoCoordinate(-90, 180.0));
223 } else {
224 // Regular circle not crossing anything
225
226 // Calculate geo bounding box of the circle
227 //
228 // A circle tangential point with a meridian, together with pole and
229 // the circle center create a spherical triangle.
230 // Finding the tangential point with the spherical law of sines:
231 //
232 // * lon_delta_in_rad : delta between the circle center and a tangential
233 // point (absolute value).
234 // * r_in_rad : angular radius of the circle
235 // * lat_in_rad : latitude of the circle center
236 // * alpha_in_rad : angle between meridian and radius of the circle.
237 // At the tangential point, sin(alpha_in_rad) == 1.
238 // * lat_delta_in_rad - absolute delta of latitudes between the circle center and
239 // any of the two points where the great circle going through the circle
240 // center and the pole crosses the circle. In other words, the points
241 // on the circle with azimuth 0 or 180.
242 //
243 // Using:
244 // sin(lon_delta_in_rad)/sin(r_in_rad) = sin(alpha_in_rad)/sin(pi/2 - lat_in_rad)
245
246 double r_in_rad = m_radius / QLocationUtils::earthMeanRadius(); // angular r
247 double lat_delta_in_deg = QLocationUtils::degrees(r_in_rad);
248 double lon_delta_in_deg = QLocationUtils::degrees(std::asin(
249 std::sin(r_in_rad) /
250 std::cos(QLocationUtils::radians(m_center.latitude()))
251 ));
252
253 QGeoCoordinate topLeft;
254 topLeft.setLatitude(QLocationUtils::clipLat(m_center.latitude() + lat_delta_in_deg));
255 topLeft.setLongitude(QLocationUtils::wrapLong(m_center.longitude() - lon_delta_in_deg));
256 QGeoCoordinate bottomRight;
257 bottomRight.setLatitude(QLocationUtils::clipLat(m_center.latitude() - lat_delta_in_deg));
258 bottomRight.setLongitude(QLocationUtils::wrapLong(m_center.longitude() + lon_delta_in_deg));
259
260 m_bbox = QGeoRectangle(topLeft, bottomRight);
261 }
262}
263
264void QGeoCirclePrivate::setCenter(const QGeoCoordinate &c)
265{
266 m_center = c;
267 updateBoundingBox();
268}
269
270void QGeoCirclePrivate::setRadius(const qreal r)
271{
272 m_radius = r;
273 updateBoundingBox();
274}
275
276bool QGeoCirclePrivate::crossNorthPole() const
277{
278 const QGeoCoordinate northPole(90.0, m_center.longitude());
279 qreal distanceToPole = m_center.distanceTo(northPole);
280 if (distanceToPole < m_radius)
281 return true;
282 return false;
283}
284
285bool QGeoCirclePrivate::crossSouthPole() const
286{
287 const QGeoCoordinate southPole(-90.0, m_center.longitude());
288 qreal distanceToPole = m_center.distanceTo(southPole);
289 if (distanceToPole < m_radius)
290 return true;
291 return false;
292}
293
294/*
295 Extends the circle to include \a coordinate.
296*/
297void QGeoCirclePrivate::extendCircle(const QGeoCoordinate &coordinate)
298{
299 if (!isValid() || !coordinate.isValid() || contains(coordinate))
300 return;
301
302 setRadius(m_center.distanceTo(coordinate));
303}
304
305/*!
306 Translates this geo circle by \a degreesLatitude northwards and \a degreesLongitude eastwards.
307
308 Negative values of \a degreesLatitude and \a degreesLongitude correspond to
309 southward and westward translation respectively.
310*/
311void QGeoCircle::translate(double degreesLatitude, double degreesLongitude)
312{
313 // TODO handle dlat, dlon larger than 360 degrees
314
315 Q_D(QGeoCircle);
316
317 double lat = d->m_center.latitude();
318 double lon = d->m_center.longitude();
319
320 lat += degreesLatitude;
321 lon += degreesLongitude;
322 lon = QLocationUtils::wrapLong(lon);
323
324 // TODO: remove this and simply clip latitude.
325 if (lat > 90.0) {
326 lat = 180.0 - lat;
327 if (lon < 0.0)
328 lon = 180.0;
329 else
330 lon -= 180;
331 }
332
333 if (lat < -90.0) {
334 lat = 180.0 + lat;
335 if (lon < 0.0)
336 lon = 180.0;
337 else
338 lon -= 180;
339 }
340
341 d->setCenter(QGeoCoordinate(lat, lon));
342}
343
344/*!
345 Returns a copy of this geo circle translated by \a degreesLatitude northwards and
346 \a degreesLongitude eastwards.
347
348 Negative values of \a degreesLatitude and \a degreesLongitude correspond to
349 southward and westward translation respectively.
350
351 \sa translate()
352*/
353QGeoCircle QGeoCircle::translated(double degreesLatitude, double degreesLongitude) const
354{
355 QGeoCircle result(*this);
356 result.translate(degreesLatitude, degreesLongitude);
357 return result;
358}
359
360/*!
361 Extends the geo circle to also cover the coordinate \a coordinate
362
363 \since 5.9
364*/
365void QGeoCircle::extendCircle(const QGeoCoordinate &coordinate)
366{
367 Q_D(QGeoCircle);
368 d->extendCircle(coordinate);
369}
370
371/*!
372 Returns the geo circle properties as a string.
373
374 \since 5.5
375*/
376
377QString QGeoCircle::toString() const
378{
379 if (type() != QGeoShape::CircleType) {
380 qWarning("Not a circle");
381 return QStringLiteral("QGeoCircle(not a circle)");
382 }
383
384 return QStringLiteral("QGeoCircle({%1, %2}, %3)")
385 .arg(center().latitude())
386 .arg(center().longitude())
387 .arg(radius());
388}
389
390/*******************************************************************************
391*******************************************************************************/
392
393QGeoCirclePrivate::QGeoCirclePrivate()
394: QGeoShapePrivate(QGeoShape::CircleType), m_radius(-1.0)
395{
396}
397
398QGeoCirclePrivate::QGeoCirclePrivate(const QGeoCoordinate &center, qreal radius)
399: QGeoShapePrivate(QGeoShape::CircleType), m_center(center), m_radius(radius)
400{
401 updateBoundingBox();
402}
403
404QGeoCirclePrivate::QGeoCirclePrivate(const QGeoCirclePrivate &other)
405: QGeoShapePrivate(QGeoShape::CircleType), m_center(other.m_center),
406 m_radius(other.m_radius), m_bbox(other.m_bbox)
407{
408}
409
410QGeoCirclePrivate::~QGeoCirclePrivate() {}
411
412QGeoShapePrivate *QGeoCirclePrivate::clone() const
413{
414 return new QGeoCirclePrivate(*this);
415}
416
417bool QGeoCirclePrivate::operator==(const QGeoShapePrivate &other) const
418{
419 if (!QGeoShapePrivate::operator==(other))
420 return false;
421
422 const QGeoCirclePrivate &otherCircle = static_cast<const QGeoCirclePrivate &>(other);
423
424 return m_radius == otherCircle.m_radius && m_center == otherCircle.m_center;
425}
426
427size_t QGeoCirclePrivate::hash(size_t seed) const
428{
429 return qHashMulti(seed, m_center, m_radius);
430}
431
432QT_END_NAMESPACE
433
434#include "moc_qgeocircle.cpp"
Combined button and popup list for selecting options.