summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@qt.io>2017-07-11 15:55:27 +0200
committerPaolo Angelelli <paolo.angelelli@qt.io>2017-08-04 11:19:14 +0000
commit92d8e4dc93400250b47f8dbe96ec7e2f748d8d4b (patch)
tree0545f115d23f74be1979ec483a63f19bbc9b1d55
parent4511bc9b9bc85a0021ce59feab1a28c711dec691 (diff)
downloadqtlocation-92d8e4dc93400250b47f8dbe96ec7e2f748d8d4b.tar.gz
Add QGeoPolygon to QtPositioning
This patch introduces a new QGeoShape, QGeoPolygon, together with helper functions in the location singleton (QtPositioning.*) to create and convert geopolygons from QML. [ChangeLog][QtPositioning][QGeoPolygon] Added QGeoPolygon shape. Change-Id: I111c576d7428f2a953f0459d16c25eea7ab2bd7c Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
-rw-r--r--src/3rdparty/clip2tri/clip2tri.cpp5
-rw-r--r--src/3rdparty/clip2tri/clip2tri.h2
-rw-r--r--src/imports/positioning/locationsingleton.cpp43
-rw-r--r--src/imports/positioning/locationsingleton.h5
-rw-r--r--src/positioning/positioning.pro3
-rw-r--r--src/positioning/qgeopath.cpp73
-rw-r--r--src/positioning/qgeopath_p.h11
-rw-r--r--src/positioning/qgeopolygon.cpp326
-rw-r--r--src/positioning/qgeopolygon.h99
-rw-r--r--src/positioning/qgeoshape.cpp23
-rw-r--r--src/positioning/qgeoshape.h3
-rw-r--r--tests/auto/auto.pro1
-rw-r--r--tests/auto/qgeopolygon/qgeopolygon.pro8
-rw-r--r--tests/auto/qgeopolygon/tst_qgeopolygon.cpp402
14 files changed, 988 insertions, 16 deletions
diff --git a/src/3rdparty/clip2tri/clip2tri.cpp b/src/3rdparty/clip2tri/clip2tri.cpp
index e715d1c7..db4911c1 100644
--- a/src/3rdparty/clip2tri/clip2tri.cpp
+++ b/src/3rdparty/clip2tri/clip2tri.cpp
@@ -173,6 +173,11 @@ Paths clip2tri::execute(const clip2tri::Operation op, const PolyFillType subjFil
return solution;
}
+int clip2tri::pointInPolygon(const IntPoint &pt, const Path &path)
+{
+ return PointInPolygon(pt, path);
+}
+
Path clip2tri::upscaleClipperPoints(const vector<Point> &inputPolygon)
{
Path outputPolygon;
diff --git a/src/3rdparty/clip2tri/clip2tri.h b/src/3rdparty/clip2tri/clip2tri.h
index 61c8a0f5..3848d009 100644
--- a/src/3rdparty/clip2tri/clip2tri.h
+++ b/src/3rdparty/clip2tri/clip2tri.h
@@ -91,6 +91,8 @@ public:
const PolyFillType subjFillType = pftNonZero,
const PolyFillType clipFillType = pftNonZero);
+ static int pointInPolygon(const IntPoint &pt, const Path &path);
+
Clipper clipper;
bool openSubject;
};
diff --git a/src/imports/positioning/locationsingleton.cpp b/src/imports/positioning/locationsingleton.cpp
index 19b05761..e9b58834 100644
--- a/src/imports/positioning/locationsingleton.cpp
+++ b/src/imports/positioning/locationsingleton.cpp
@@ -234,6 +234,37 @@ QGeoPath LocationSingleton::path(const QJSValue &value, qreal width) const
}
/*!
+ \qmlmethod geopolygon QtPositioning::polygon() const
+
+ Constructs an empty geopolygon.
+
+ \sa {geopolygon}
+ \since 5.10
+*/
+QGeoPath LocationSingleton::polygon() const
+{
+ return QGeoPolygon();
+}
+
+/*!
+ \qmlmethod geopolygon QtPositioning::polygon(list<coordinate> coordinates) const
+
+ Constructs a geopolygon from coordinates.
+
+ \sa {geopolygon}
+ \since 5.10
+*/
+QGeoPath LocationSingleton::polygon(const QVariantList &coordinates) const
+{
+ QList<QGeoCoordinate> internalCoordinates;
+ for (int i = 0; i < coordinates.size(); i++) {
+ if (coordinates.at(i).canConvert<QGeoCoordinate>())
+ internalCoordinates << coordinates.at(i).value<QGeoCoordinate>();
+ }
+ return QGeoPolygon(internalCoordinates);
+}
+
+/*!
\qmlmethod geocircle QtPositioning::shapeToCircle(geoshape shape) const
Converts \a shape to a geocircle.
@@ -272,3 +303,15 @@ QGeoPath LocationSingleton::shapeToPath(const QGeoShape &shape) const
return QGeoPath(shape);
}
+/*!
+ \qmlmethod geopath QtPositioning::shapeToPolygon(geoshape shape) const
+
+ Converts \a shape to a geopolygon.
+
+ \sa {geopolygon}
+ \since 5.10
+*/
+QGeoPolygon LocationSingleton::shapeToPolygon(const QGeoShape &shape) const
+{
+ return QGeoPolygon(shape);
+}
diff --git a/src/imports/positioning/locationsingleton.h b/src/imports/positioning/locationsingleton.h
index 4faf2738..6a560fa8 100644
--- a/src/imports/positioning/locationsingleton.h
+++ b/src/imports/positioning/locationsingleton.h
@@ -47,6 +47,7 @@
#include <QtPositioning/QGeoRectangle>
#include <QtPositioning/QGeoCircle>
#include <QtPositioning/QGeoPath>
+#include <QtPositioning/QGeoPolygon>
#include <QtQml/QJSValue>
#include <QVariant>
@@ -76,9 +77,13 @@ public:
Q_INVOKABLE QGeoPath path() const;
Q_INVOKABLE QGeoPath path(const QJSValue &value, qreal width = 0.0) const;
+ Q_INVOKABLE QGeoPath polygon() const;
+ Q_INVOKABLE QGeoPath polygon(const QVariantList &value) const;
+
Q_INVOKABLE QGeoCircle shapeToCircle(const QGeoShape &shape) const;
Q_INVOKABLE QGeoRectangle shapeToRectangle(const QGeoShape &shape) const;
Q_INVOKABLE QGeoPath shapeToPath(const QGeoShape &shape) const;
+ Q_INVOKABLE QGeoPolygon shapeToPolygon(const QGeoShape &shape) const;
};
#endif // LOCATIONSINGLETON_H
diff --git a/src/positioning/positioning.pro b/src/positioning/positioning.pro
index 32f3c05c..974a8d48 100644
--- a/src/positioning/positioning.pro
+++ b/src/positioning/positioning.pro
@@ -38,6 +38,7 @@ PUBLIC_HEADERS += \
qnmeapositioninfosource.h \
qgeopositioninfosourcefactory.h \
qpositioningglobal.h \
+ qgeopolygon.h \
qgeopath.h \
PRIVATE_HEADERS += \
@@ -59,6 +60,7 @@ PRIVATE_HEADERS += \
qlocationdata_simulator_p.h \
qdoublematrix4x4_p.h \
qgeopath_p.h \
+ qgeopolygon_p.h \
qclipperutils_p.h
SOURCES += \
@@ -82,6 +84,7 @@ SOURCES += \
qdoublevector2d.cpp \
qdoublevector3d.cpp \
qgeopath.cpp \
+ qgeopolygon.cpp \
qlocationdata_simulator.cpp \
qwebmercator.cpp \
qdoublematrix4x4.cpp \
diff --git a/src/positioning/qgeopath.cpp b/src/positioning/qgeopath.cpp
index 858dc0bd..5e7a4077 100644
--- a/src/positioning/qgeopath.cpp
+++ b/src/positioning/qgeopath.cpp
@@ -109,7 +109,7 @@ Q_GLOBAL_STATIC(PathVariantConversions, initPathConversions)
Constructs a new, empty geo path.
*/
QGeoPath::QGeoPath()
-: QGeoShape(new QGeoPathPrivate)
+: QGeoShape(new QGeoPathPrivate(QGeoShape::PathType))
{
initPathConversions();
}
@@ -119,7 +119,7 @@ QGeoPath::QGeoPath()
(\a path and \a width).
*/
QGeoPath::QGeoPath(const QList<QGeoCoordinate> &path, const qreal &width)
-: QGeoShape(new QGeoPathPrivate(path, width))
+: QGeoShape(new QGeoPathPrivate(QGeoShape::PathType, path, width))
{
initPathConversions();
}
@@ -141,7 +141,7 @@ QGeoPath::QGeoPath(const QGeoShape &other)
{
initPathConversions();
if (type() != QGeoShape::PathType)
- d_ptr = new QGeoPathPrivate;
+ d_ptr = new QGeoPathPrivate(QGeoShape::PathType);
}
/*!
@@ -341,22 +341,22 @@ QString QGeoPath::toString() const
* QGeoPathPrivate
*******************************************************************************/
-QGeoPathPrivate::QGeoPathPrivate()
-: QGeoShapePrivate(QGeoShape::PathType), m_width(0)
+QGeoPathPrivate::QGeoPathPrivate(QGeoShape::ShapeType type)
+: QGeoShapePrivate(type), m_width(0), m_clipperDirty(true)
{
}
-QGeoPathPrivate::QGeoPathPrivate(const QList<QGeoCoordinate> &path, const qreal width)
-: QGeoShapePrivate(QGeoShape::PathType), m_width(0)
+QGeoPathPrivate::QGeoPathPrivate(QGeoShape::ShapeType type, const QList<QGeoCoordinate> &path, const qreal width)
+: QGeoShapePrivate(type), m_width(0), m_clipperDirty(true)
{
setPath(path);
setWidth(width);
}
QGeoPathPrivate::QGeoPathPrivate(const QGeoPathPrivate &other)
-: QGeoShapePrivate(QGeoShape::PathType), m_path(other.m_path),
+: QGeoShapePrivate(other.type), m_path(other.m_path),
m_deltaXs(other.m_deltaXs), m_minX(other.m_minX), m_maxX(other.m_maxX), m_minLati(other.m_minLati),
- m_maxLati(other.m_maxLati), m_bbox(other.m_bbox), m_width(other.m_width)
+ m_maxLati(other.m_maxLati), m_bbox(other.m_bbox), m_width(other.m_width), m_clipperDirty(true)
{
}
@@ -375,17 +375,25 @@ bool QGeoPathPrivate::operator==(const QGeoShapePrivate &other) const
const QGeoPathPrivate &otherPath = static_cast<const QGeoPathPrivate &>(other);
if (m_path.size() != otherPath.m_path.size())
return false;
- return m_width == otherPath.m_width && m_path == otherPath.m_path;
+
+ if (type == QGeoShape::PathType)
+ return m_width == otherPath.m_width && m_path == otherPath.m_path;
+ else
+ return m_path == otherPath.m_path;
}
bool QGeoPathPrivate::isValid() const
{
- return !isEmpty();
+ if (type == QGeoShape::PathType)
+ return !isEmpty();
+ else
+ return m_path.size() > 2;
+
}
bool QGeoPathPrivate::isEmpty() const
{
- return m_path.isEmpty();
+ return m_path.isEmpty(); // this should perhaps return geometric emptiness, less than 2 points for line, or empty polygon for polygons
}
const QList<QGeoCoordinate> &QGeoPathPrivate::path() const
@@ -416,6 +424,7 @@ void QGeoPathPrivate::setWidth(const qreal &width)
double QGeoPathPrivate::length(int indexFrom, int indexTo) const
{
+ bool wrap = indexTo == -1;
if (indexTo < 0 || indexTo >= path().size())
indexTo = path().size() - 1;
double len = 0.0;
@@ -423,6 +432,8 @@ double QGeoPathPrivate::length(int indexFrom, int indexTo) const
// instead of the shortest path from A to B.
for (int i = indexFrom; i < indexTo; i++)
len += m_path[i].distanceTo(m_path[i+1]);
+ if (wrap)
+ len += m_path.last().distanceTo(m_path.first());
return len;
}
@@ -436,6 +447,14 @@ int QGeoPathPrivate::size() const
*/
bool QGeoPathPrivate::contains(const QGeoCoordinate &coordinate) const
{
+ if (type == QGeoShape::PathType)
+ return lineContains(coordinate);
+ else
+ return polygonContains(coordinate);
+}
+
+bool QGeoPathPrivate::lineContains(const QGeoCoordinate &coordinate) const
+{
// Unoptimized approach:
// - consider each segment of the path
// - project it into mercator space (rhumb lines are straight in mercator space)
@@ -503,6 +522,20 @@ bool QGeoPathPrivate::contains(const QGeoCoordinate &coordinate) const
return (m_path[0].distanceTo(coordinate) <= lineRadius);
}
+bool QGeoPathPrivate::polygonContains(const QGeoCoordinate &coordinate) const
+{
+ if (m_clipperDirty)
+ const_cast<QGeoPathPrivate *>(this)->updateClipperPath();
+
+ QDoubleVector2D coord = QWebMercator::coordToMercator(coordinate);
+ double tlx = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
+ if (coord.x() < tlx)
+ coord.setX(coord.x() + 1.0);
+
+ IntPoint intCoord = QClipperUtils::toIntPoint(coord);
+ return c2t::clip2tri::pointInPolygon(intCoord, m_clipperPath) != 0;
+}
+
QGeoCoordinate QGeoPathPrivate::center() const
{
return boundingGeoRectangle().center();
@@ -591,6 +624,7 @@ void QGeoPathPrivate::removeCoordinate(int index)
void QGeoPathPrivate::computeBoundingBox()
{
+ m_clipperDirty = true;
if (m_path.isEmpty()) {
m_deltaXs.clear();
m_minX = qInf();
@@ -641,6 +675,7 @@ void QGeoPathPrivate::computeBoundingBox()
void QGeoPathPrivate::updateBoundingBox()
{
+ m_clipperDirty = true;
if (m_path.isEmpty()) {
m_deltaXs.clear();
m_minX = qInf();
@@ -693,4 +728,18 @@ void QGeoPathPrivate::updateBoundingBox()
QGeoCoordinate(m_minLati, currentMaxLongi));
}
+void QGeoPathPrivate::updateClipperPath()
+{
+ m_clipperDirty = false;
+ double tlx = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
+ QList<QDoubleVector2D> preservedPath;
+ for (const QGeoCoordinate &c : m_path) {
+ QDoubleVector2D crd = QWebMercator::coordToMercator(c);
+ if (crd.x() < tlx)
+ crd.setX(crd.x() + 1.0);
+ preservedPath << crd;
+ }
+ m_clipperPath = QClipperUtils::qListToPath(preservedPath);
+}
+
QT_END_NAMESPACE
diff --git a/src/positioning/qgeopath_p.h b/src/positioning/qgeopath_p.h
index 48334017..3eceff24 100644
--- a/src/positioning/qgeopath_p.h
+++ b/src/positioning/qgeopath_p.h
@@ -54,6 +54,7 @@
#include "qgeoshape_p.h"
#include "qgeocoordinate.h"
#include "qlocationutils_p.h"
+#include <QtPositioning/private/qclipperutils_p.h>
#include <QtCore/QVector>
@@ -62,14 +63,16 @@ QT_BEGIN_NAMESPACE
class QGeoPathPrivate : public QGeoShapePrivate
{
public:
- QGeoPathPrivate();
- QGeoPathPrivate(const QList<QGeoCoordinate> &path, const qreal width = 0.0);
+ QGeoPathPrivate(QGeoShape::ShapeType type);
+ QGeoPathPrivate(QGeoShape::ShapeType type, const QList<QGeoCoordinate> &path, const qreal width = 0.0);
QGeoPathPrivate(const QGeoPathPrivate &other);
~QGeoPathPrivate();
bool isValid() const Q_DECL_OVERRIDE;
bool isEmpty() const Q_DECL_OVERRIDE;
bool contains(const QGeoCoordinate &coordinate) const Q_DECL_OVERRIDE;
+ bool lineContains(const QGeoCoordinate &coordinate) const;
+ bool polygonContains(const QGeoCoordinate &coordinate) const;
QGeoCoordinate center() const Q_DECL_OVERRIDE;
QGeoRectangle boundingGeoRectangle() const Q_DECL_OVERRIDE;
@@ -95,7 +98,7 @@ public:
void removeCoordinate(int index);
void computeBoundingBox();
void updateBoundingBox();
-
+ void updateClipperPath();
QList<QGeoCoordinate> m_path;
QVector<double> m_deltaXs; // longitude deltas from m_path[0]
@@ -105,6 +108,8 @@ public:
double m_maxLati; // minimum latitude. paths do not wrap around through the poles
QGeoRectangle m_bbox;
qreal m_width;
+ bool m_clipperDirty;
+ QtClipperLib::Path m_clipperPath;
};
QT_END_NAMESPACE
diff --git a/src/positioning/qgeopolygon.cpp b/src/positioning/qgeopolygon.cpp
new file mode 100644
index 00000000..d817ea07
--- /dev/null
+++ b/src/positioning/qgeopolygon.cpp
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtPositioning module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgeopolygon.h"
+#include "qgeopath_p.h"
+
+#include "qgeocoordinate.h"
+#include "qnumeric.h"
+#include "qlocationutils_p.h"
+#include "qwebmercator_p.h"
+
+#include "qdoublevector2d_p.h"
+#include "qdoublevector3d_p.h"
+#include "qwebmercator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGeoPolygon
+ \inmodule QtPositioning
+ \ingroup QtPositioning-positioning
+ \since 5.10
+
+ \brief The QGeoPolygon class defines a geographic polygon.
+
+ The polygon is defined by an ordered list of QGeoCoordinates representing its perimeter.
+
+ Each two adjacent elements in this list are intended to be connected
+ together by the shortest line segment of constant bearing passing
+ through both elements.
+ This type of connection can cross the date line in the longitudinal direction,
+ but never crosses the poles.
+
+ This is relevant for the calculation of the bounding box returned by
+ \l QGeoShape::boundingGeoRectangle() for this shape, which will have the latitude of
+ the top left corner set to the maximum latitude in the path point set.
+ Similarly, the latitude of the bottom right corner will be the minimum latitude
+ in the path point set.
+
+ This class is a \l Q_GADGET.
+ It can be \l{Cpp_value_integration_positioning}{directly used from C++ and QML}.
+*/
+
+/*!
+ \property QGeoPolygon::path
+ \brief This property holds the list of coordinates for the geo polygon.
+
+ The polygon is both invalid and empty if it contains no coordinate.
+
+ A default constructed QGeoPolygon is therefore invalid.
+*/
+
+inline QGeoPolygonPrivate *QGeoPolygon::d_func()
+{
+ return static_cast<QGeoPolygonPrivate *>(d_ptr.data());
+}
+
+inline const QGeoPolygonPrivate *QGeoPolygon::d_func() const
+{
+ return static_cast<const QGeoPolygonPrivate *>(d_ptr.constData());
+}
+
+struct PolygonVariantConversions
+{
+ PolygonVariantConversions()
+ {
+ QMetaType::registerConverter<QGeoShape, QGeoPolygon>();
+ QMetaType::registerConverter<QGeoPolygon, QGeoShape>();
+ }
+};
+
+Q_GLOBAL_STATIC(PolygonVariantConversions, initPathConversions)
+
+/*!
+ Constructs a new, empty geo path.
+*/
+QGeoPolygon::QGeoPolygon()
+: QGeoShape(new QGeoPolygonPrivate(QGeoShape::PolygonType))
+{
+ initPathConversions();
+}
+
+/*!
+ Constructs a new geo path from a list of coordinates
+ (\a path and \a width).
+*/
+QGeoPolygon::QGeoPolygon(const QList<QGeoCoordinate> &path)
+: QGeoShape(new QGeoPolygonPrivate(QGeoShape::PolygonType, path))
+{
+ initPathConversions();
+}
+
+/*!
+ Constructs a new geo path from the contents of \a other.
+*/
+QGeoPolygon::QGeoPolygon(const QGeoPolygon &other)
+: QGeoShape(other)
+{
+ initPathConversions();
+}
+
+/*!
+ Constructs a new geo path from the contents of \a other.
+*/
+QGeoPolygon::QGeoPolygon(const QGeoShape &other)
+: QGeoShape(other)
+{
+ initPathConversions();
+ if (type() != QGeoShape::PolygonType)
+ d_ptr = new QGeoPolygonPrivate(QGeoShape::PolygonType);
+}
+
+/*!
+ Destroys this path.
+*/
+QGeoPolygon::~QGeoPolygon() {}
+
+/*!
+ Assigns \a other to this geo path and returns a reference to this geo path.
+*/
+QGeoPolygon &QGeoPolygon::operator=(const QGeoPolygon &other)
+{
+ QGeoShape::operator=(other);
+ return *this;
+}
+
+/*!
+ Returns whether this geo path is equal to \a other.
+*/
+bool QGeoPolygon::operator==(const QGeoPolygon &other) const
+{
+ Q_D(const QGeoPolygon);
+ return *d == *other.d_func();
+}
+
+/*!
+ Returns whether this geo path is not equal to \a other.
+*/
+bool QGeoPolygon::operator!=(const QGeoPolygon &other) const
+{
+ Q_D(const QGeoPolygon);
+ return !(*d == *other.d_func());
+}
+
+void QGeoPolygon::setPath(const QList<QGeoCoordinate> &path)
+{
+ Q_D(QGeoPolygon);
+ return d->setPath(path);
+}
+
+/*!
+ Returns all the elements. Equivalent to QGeoShape::center().
+ The center coordinate, in case of a QGeoPolygon, is the center of its bounding box.
+*/
+const QList<QGeoCoordinate> &QGeoPolygon::path() const
+{
+ Q_D(const QGeoPolygon);
+ return d->path();
+}
+
+/*!
+ Translates this geo path by \a degreesLatitude northwards and \a degreesLongitude eastwards.
+
+ Negative values of \a degreesLatitude and \a degreesLongitude correspond to
+ southward and westward translation respectively.
+*/
+void QGeoPolygon::translate(double degreesLatitude, double degreesLongitude)
+{
+ Q_D(QGeoPolygon);
+ d->translate(degreesLatitude, degreesLongitude);
+}
+
+/*!
+ Returns a copy of this geo polygon translated by \a degreesLatitude northwards and
+ \a degreesLongitude eastwards.
+
+ Negative values of \a degreesLatitude and \a degreesLongitude correspond to
+ southward and westward translation respectively.
+
+ \sa translate()
+*/
+QGeoPolygon QGeoPolygon::translated(double degreesLatitude, double degreesLongitude) const
+{
+ QGeoPolygon result(*this);
+ result.translate(degreesLatitude, degreesLongitude);
+ return result;
+}
+
+/*!
+ Returns the length of the polygon's perimeter, in meters, from the element \a indexFrom to the element \a indexTo.
+ The length is intended to be the sum of the shortest distances for each pair of adjacent points.
+*/
+double QGeoPolygon::length(int indexFrom, int indexTo) const
+{
+ Q_D(const QGeoPolygon);
+ return d->length(indexFrom, indexTo);
+}
+
+/*!
+ Returns the number of elements in the polygon.
+
+ \since 5.10
+*/
+int QGeoPolygon::size() const
+{
+ Q_D(const QGeoPolygon);
+ return d->size();
+}
+
+/*!
+ Appends \a coordinate to the polygon.
+*/
+void QGeoPolygon::addCoordinate(const QGeoCoordinate &coordinate)
+{
+ Q_D(QGeoPolygon);
+ d->addCoordinate(coordinate);
+}
+
+/*!
+ Inserts \a coordinate at the specified \a index.
+*/
+void QGeoPolygon::insertCoordinate(int index, const QGeoCoordinate &coordinate)
+{
+ Q_D(QGeoPolygon);
+ d->insertCoordinate(index, coordinate);
+}
+
+/*!
+ Replaces the path element at the specified \a index with \a coordinate.
+*/
+void QGeoPolygon::replaceCoordinate(int index, const QGeoCoordinate &coordinate)
+{
+ Q_D(QGeoPolygon);
+ d->replaceCoordinate(index, coordinate);
+}
+
+/*!
+ Returns the coordinate at \a index .
+*/
+QGeoCoordinate QGeoPolygon::coordinateAt(int index) const
+{
+ Q_D(const QGeoPolygon);
+ return d->coordinateAt(index);
+}
+
+/*!
+ Returns true if the polygon's perimeter contains \a coordinate as one of the elements.
+*/
+bool QGeoPolygon::containsCoordinate(const QGeoCoordinate &coordinate) const
+{
+ Q_D(const QGeoPolygon);
+ return d->containsCoordinate(coordinate);
+}
+
+/*!
+ Removes the last occurrence of \a coordinate from the polygon.
+*/
+void QGeoPolygon::removeCoordinate(const QGeoCoordinate &coordinate)
+{
+ Q_D(QGeoPolygon);
+ d->removeCoordinate(coordinate);
+}
+
+/*!
+ Removes element at position \a index from the polygon.
+*/
+void QGeoPolygon::removeCoordinate(int index)
+{
+ Q_D(QGeoPolygon);
+ d->removeCoordinate(index);
+}
+
+/*!
+ Returns the geo path properties as a string.
+*/
+QString QGeoPolygon::toString() const
+{
+ if (type() != QGeoShape::PolygonType) {
+ qWarning("Not a polygon");
+ return QStringLiteral("QGeoPolygon(not a polygon)");
+ }
+
+ QString pathString;
+ for (const auto &p : path())
+ pathString += p.toString() + QLatin1Char(',');
+
+ return QStringLiteral("QGeoPolygon([ %1 ])").arg(pathString);
+}
+
+QT_END_NAMESPACE
diff --git a/src/positioning/qgeopolygon.h b/src/positioning/qgeopolygon.h
new file mode 100644
index 00000000..90886da7
--- /dev/null
+++ b/src/positioning/qgeopolygon.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtPositioning module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGEOPOLYGON_H
+#define QGEOPOLYGON_H
+
+#include <QtPositioning/QGeoShape>
+
+QT_BEGIN_NAMESPACE
+
+class QGeoCoordinate;
+class QGeoPathPrivate;
+typedef QGeoPathPrivate QGeoPolygonPrivate;
+
+class Q_POSITIONING_EXPORT QGeoPolygon : public QGeoShape
+{
+ Q_GADGET
+
+public:
+ QGeoPolygon();
+ QGeoPolygon(const QList<QGeoCoordinate> &path);
+ QGeoPolygon(const QGeoPolygon &other);
+ QGeoPolygon(const QGeoShape &other);
+
+ ~QGeoPolygon();
+
+ QGeoPolygon &operator=(const QGeoPolygon &other);
+
+ using QGeoShape::operator==;
+ bool operator==(const QGeoPolygon &other) const;
+
+ using QGeoShape::operator!=;
+ bool operator!=(const QGeoPolygon &other) const;
+
+ void setPath(const QList<QGeoCoordinate> &path);
+ const QList<QGeoCoordinate> &path() const;
+
+ Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude);
+ Q_INVOKABLE QGeoPolygon translated(double degreesLatitude, double degreesLongitude) const;
+ Q_INVOKABLE double length(int indexFrom = 0, int indexTo = -1) const;
+ Q_INVOKABLE int size() const;
+ Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate);
+ Q_INVOKABLE void insertCoordinate(int index, const QGeoCoordinate &coordinate);
+ Q_INVOKABLE void replaceCoordinate(int index, const QGeoCoordinate &coordinate);
+ Q_INVOKABLE QGeoCoordinate coordinateAt(int index) const;
+ Q_INVOKABLE bool containsCoordinate(const QGeoCoordinate &coordinate) const;
+ Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate);
+ Q_INVOKABLE void removeCoordinate(int index);
+
+ Q_INVOKABLE QString toString() const;
+
+private:
+ inline QGeoPolygonPrivate *d_func();
+ inline const QGeoPolygonPrivate *d_func() const;
+};
+
+Q_DECLARE_TYPEINFO(QGeoPolygon, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QGeoPolygon)
+
+#endif // QGEOPOLYGON_H
diff --git a/src/positioning/qgeoshape.cpp b/src/positioning/qgeoshape.cpp
index ad7b0c58..7acbc9fa 100644
--- a/src/positioning/qgeoshape.cpp
+++ b/src/positioning/qgeoshape.cpp
@@ -42,6 +42,7 @@
#include "qgeorectangle.h"
#include "qgeocircle.h"
#include "qgeopath.h"
+#include "qgeopolygon.h"
#ifndef QT_NO_DEBUG_STREAM
@@ -345,6 +346,9 @@ QDebug operator<<(QDebug dbg, const QGeoShape &shape)
case QGeoShape::PathType:
dbg << "Path";
break;
+ case QGeoShape::PolygonType:
+ dbg << "Polygon";
+ break;
case QGeoShape::CircleType:
dbg << "Circle";
}
@@ -379,6 +383,13 @@ QDataStream &operator<<(QDataStream &stream, const QGeoShape &shape)
stream << c;
break;
}
+ case QGeoShape::PolygonType: {
+ QGeoPolygon p = shape;
+ stream << p.path().size();
+ for (const auto &c: p.path())
+ stream << c;
+ break;
+ }
}
return stream;
@@ -419,6 +430,18 @@ QDataStream &operator>>(QDataStream &stream, QGeoShape &shape)
shape = QGeoPath(l);
break;
}
+ case QGeoShape::PolygonType: {
+ QList<QGeoCoordinate> l;
+ QGeoCoordinate c;
+ int sz;
+ stream >> sz;
+ for (int i = 0; i < sz; i++) {
+ stream >> c;
+ l.append(c);
+ }
+ shape = QGeoPolygon(l);
+ break;
+ }
}
return stream;
diff --git a/src/positioning/qgeoshape.h b/src/positioning/qgeoshape.h
index defd0eec..c0bc658b 100644
--- a/src/positioning/qgeoshape.h
+++ b/src/positioning/qgeoshape.h
@@ -66,7 +66,8 @@ public:
UnknownType,
RectangleType,
CircleType,
- PathType
+ PathType,
+ PolygonType
};
ShapeType type() const;
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 5594b8b0..d97af779 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -78,6 +78,7 @@ SUBDIRS += \
qgeorectangle \
qgeocircle \
qgeopath \
+ qgeopolygon \
qgeocoordinate \
qgeolocation \
qgeopositioninfo \
diff --git a/tests/auto/qgeopolygon/qgeopolygon.pro b/tests/auto/qgeopolygon/qgeopolygon.pro
new file mode 100644
index 00000000..f6314ca4
--- /dev/null
+++ b/tests/auto/qgeopolygon/qgeopolygon.pro
@@ -0,0 +1,8 @@
+TEMPLATE = app
+CONFIG += testcase
+TARGET = tst_qgeopolygon
+
+SOURCES += \
+ tst_qgeopolygon.cpp
+
+QT += positioning testlib
diff --git a/tests/auto/qgeopolygon/tst_qgeopolygon.cpp b/tests/auto/qgeopolygon/tst_qgeopolygon.cpp
new file mode 100644
index 00000000..12e39f0f
--- /dev/null
+++ b/tests/auto/qgeopolygon/tst_qgeopolygon.cpp
@@ -0,0 +1,402 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtPositioning/QGeoCoordinate>
+#include <QtPositioning/QGeoRectangle>
+#include <QtPositioning/QGeoPolygon>
+
+QT_USE_NAMESPACE
+
+class tst_QGeoPolygon : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void defaultConstructor();
+ void listConstructor();
+ void assignment();
+
+ void comparison();
+ void type();
+
+ void path();
+ void size();
+
+ void translate_data();
+ void translate();
+
+ void valid_data();
+ void valid();
+
+ void contains_data();
+ void contains();
+
+ void boundingGeoRectangle_data();
+ void boundingGeoRectangle();
+
+ void extendShape();
+ void extendShape_data();
+};
+
+void tst_QGeoPolygon::defaultConstructor()
+{
+ QGeoPolygon p;
+ QVERIFY(!p.path().size());
+ QVERIFY(!p.size());
+ QVERIFY(!p.isValid());
+ QVERIFY(p.isEmpty());
+}
+
+void tst_QGeoPolygon::listConstructor()
+{
+ QList<QGeoCoordinate> coords;
+ coords.append(QGeoCoordinate(1,1));
+ coords.append(QGeoCoordinate(2,2));
+ QGeoPolygon p2(coords);
+ QCOMPARE(p2.path().size(), 2);
+ QCOMPARE(p2.size(), 2);
+ QVERIFY(!p2.isValid()); // a polygon can't have only 2 coords
+ QVERIFY(!p2.isEmpty());
+
+ coords.append(QGeoCoordinate(3,0));
+
+ QGeoPolygon p(coords);
+ QCOMPARE(p.path().size(), 3);
+ QCOMPARE(p.size(), 3);
+ QVERIFY(p.isValid());
+ QVERIFY(!p.isEmpty());
+
+
+ for (const QGeoCoordinate &c : coords) {
+ QCOMPARE(p.path().contains(c), true);
+ QCOMPARE(p.containsCoordinate(c), true);
+ }
+}
+
+void tst_QGeoPolygon::assignment()
+{
+ QGeoPolygon p1;
+ QList<QGeoCoordinate> coords;
+ coords.append(QGeoCoordinate(1,1));
+ coords.append(QGeoCoordinate(2,2));
+ coords.append(QGeoCoordinate(3,0));
+ QGeoPolygon p2(coords);
+
+ QVERIFY(p1 != p2);
+
+ p1 = p2;
+ QCOMPARE(p1.path(), coords);
+ QCOMPARE(p1, p2);
+
+ // Assign c1 to an area
+ QGeoShape area = p1;
+ QCOMPARE(area.type(), p1.type());
+ QVERIFY(area == p1);
+
+ // Assign the area back to a polygon
+ QGeoPolygon p3 = area;
+ QCOMPARE(p3.path(), coords);
+ QVERIFY(p3 == p1);
+
+ // Check that the copy is not modified when modifying the original.
+ p1.addCoordinate(QGeoCoordinate(4,0));
+ QVERIFY(p3 != p1);
+}
+
+void tst_QGeoPolygon::comparison()
+{
+ QList<QGeoCoordinate> coords;
+ coords.append(QGeoCoordinate(1,1));
+ coords.append(QGeoCoordinate(2,2));
+ coords.append(QGeoCoordinate(3,0));
+ QList<QGeoCoordinate> coords2;
+ coords2.append(QGeoCoordinate(3,1));
+ coords2.append(QGeoCoordinate(4,2));
+ coords2.append(QGeoCoordinate(3,0));
+ QGeoPolygon c1(coords);
+ QGeoPolygon c2(coords);
+ QGeoPolygon c3(coords2);
+
+ QVERIFY(c1 == c2);
+ QVERIFY(!(c1 != c2));
+
+ QVERIFY(!(c1 == c3));
+ QVERIFY(c1 != c3);
+
+ QVERIFY(!(c2 == c3));
+ QVERIFY(c2 != c3);
+
+ QGeoRectangle b1(QGeoCoordinate(20,20),QGeoCoordinate(10,30));
+ QVERIFY(!(c1 == b1));
+ QVERIFY(c1 != b1);
+
+ QGeoShape *c2Ptr = &c2;
+ QVERIFY(c1 == *c2Ptr);
+ QVERIFY(!(c1 != *c2Ptr));
+
+ QGeoShape *c3Ptr = &c3;
+ QVERIFY(!(c1 == *c3Ptr));
+ QVERIFY(c1 != *c3Ptr);
+}
+
+void tst_QGeoPolygon::type()
+{
+ QGeoPolygon c;
+ QCOMPARE(c.type(), QGeoShape::PolygonType);
+}
+
+void tst_QGeoPolygon::path()
+{
+ QList<QGeoCoordinate> coords;
+ coords.append(QGeoCoordinate(1,1));
+ coords.append(QGeoCoordinate(2,2));
+ coords.append(QGeoCoordinate(3,0));
+
+ QGeoPolygon p;
+ p.setPath(coords);
+ QCOMPARE(p.path().size(), 3);
+ QCOMPARE(p.size(), 3);
+
+ for (const QGeoCoordinate &c : coords) {
+ QCOMPARE(p.path().contains(c), true);
+ QCOMPARE(p.containsCoordinate(c), true);
+ }
+}
+
+void tst_QGeoPolygon::size()
+{
+ QList<QGeoCoordinate> coords;
+
+ QGeoPolygon p1(coords);
+ QCOMPARE(p1.size(), coords.size());
+
+ coords.append(QGeoCoordinate(1,1));
+ QGeoPolygon p2(coords);
+ QCOMPARE(p2.size(), coords.size());
+
+ coords.append(QGeoCoordinate(2,2));
+ QGeoPolygon p3(coords);
+ QCOMPARE(p3.size(), coords.size());
+
+ coords.append(QGeoCoordinate(3,0));
+ QGeoPolygon p4(coords);
+ QCOMPARE(p4.size(), coords.size());
+
+ p4.removeCoordinate(2);
+ QCOMPARE(p4.size(), coords.size() - 1);
+
+ p4.removeCoordinate(coords.first());
+ QCOMPARE(p4.size(), coords.size() - 2);
+}
+
+void tst_QGeoPolygon::translate_data()
+{
+ QTest::addColumn<QGeoCoordinate>("c1");
+ QTest::addColumn<QGeoCoordinate>("c2");
+ QTest::addColumn<QGeoCoordinate>("c3");
+ QTest::addColumn<double>("lat");
+ QTest::addColumn<double>("lon");
+
+ QTest::newRow("Simple") << QGeoCoordinate(1,1) << QGeoCoordinate(2,2) <<
+ QGeoCoordinate(3,0) << 5.0 << 4.0;
+ QTest::newRow("Backward") << QGeoCoordinate(1,1) << QGeoCoordinate(2,2) <<
+ QGeoCoordinate(3,0) << -5.0 << -4.0;
+}
+
+void tst_QGeoPolygon::translate()
+{
+ QFETCH(QGeoCoordinate, c1);
+ QFETCH(QGeoCoordinate, c2);
+ QFETCH(QGeoCoordinate, c3);
+ QFETCH(double, lat);
+ QFETCH(double, lon);
+
+ QList<QGeoCoordinate> coords;
+ coords.append(c1);
+ coords.append(c2);
+ coords.append(c3);
+ QGeoPolygon p(coords);
+
+ p.translate(lat, lon);
+
+ for (int i = 0; i < p.path().size(); i++) {
+ QCOMPARE(coords[i].latitude(), p.path()[i].latitude() - lat );
+ QCOMPARE(coords[i].longitude(), p.path()[i].longitude() - lon );
+ }
+}
+
+void tst_QGeoPolygon::valid_data()
+{
+ QTest::addColumn<QGeoCoordinate>("c1");
+ QTest::addColumn<QGeoCoordinate>("c2");
+ QTest::addColumn<QGeoCoordinate>("c3");
+ QTest::addColumn<bool>("valid");
+
+ QTest::newRow("empty coords") << QGeoCoordinate() << QGeoCoordinate() << QGeoCoordinate() << false;
+ QTest::newRow("invalid coord") << QGeoCoordinate(50, 50) << QGeoCoordinate(60, 60) << QGeoCoordinate(700, 700) << false;
+ QTest::newRow("good") << QGeoCoordinate(10, 10) << QGeoCoordinate(11, 11) << QGeoCoordinate(10, 12) << true;
+}
+
+void tst_QGeoPolygon::valid()
+{
+ QFETCH(QGeoCoordinate, c1);
+ QFETCH(QGeoCoordinate, c2);
+ QFETCH(QGeoCoordinate, c3);
+ QFETCH(bool, valid);
+
+ QList<QGeoCoordinate> coords;
+ coords.append(c1);
+ coords.append(c2);
+ coords.append(c3);
+ QGeoPolygon p(coords);
+
+ QCOMPARE(p.isValid(), valid);
+
+ QGeoShape area = p;
+ QCOMPARE(area.isValid(), valid);
+}
+
+void tst_QGeoPolygon::contains_data()
+{
+ QTest::addColumn<QGeoCoordinate>("c1");
+ QTest::addColumn<QGeoCoordinate>("c2");
+ QTest::addColumn<QGeoCoordinate>("c3");
+ QTest::addColumn<QGeoCoordinate>("probe");
+ QTest::addColumn<bool>("result");
+
+ QList<QGeoCoordinate> c;
+ c.append(QGeoCoordinate(1,1));
+ c.append(QGeoCoordinate(2,2));
+ c.append(QGeoCoordinate(3,0));
+
+ QTest::newRow("One of the points") << c[0] << c[1] << c[2] << QGeoCoordinate(2, 2) << true;
+ QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << QGeoCoordinate(0.8, 0.8) << false;
+ QTest::newRow("Not so far away and large line") << c[0] << c[1] << c[2] << QGeoCoordinate(0.8, 0.8) << false;
+ QTest::newRow("Inside") << c[0] << c[1] << c[2] << QGeoCoordinate(2.0, 1.0) << true;
+}
+
+void tst_QGeoPolygon::contains()
+{
+ QFETCH(QGeoCoordinate, c1);
+ QFETCH(QGeoCoordinate, c2);
+ QFETCH(QGeoCoordinate, c3);
+ QFETCH(QGeoCoordinate, probe);
+ QFETCH(bool, result);
+
+ QList<QGeoCoordinate> coords;
+ coords.append(c1);
+ coords.append(c2);
+ coords.append(c3);
+ QGeoPolygon p(coords);
+
+ QCOMPARE(p.contains(probe), result);
+
+ QGeoShape area = p;
+ QCOMPARE(area.contains(probe), result);
+}
+
+void tst_QGeoPolygon::boundingGeoRectangle_data()
+{
+ QTest::addColumn<QGeoCoordinate>("c1");
+ QTest::addColumn<QGeoCoordinate>("c2");
+ QTest::addColumn<QGeoCoordinate>("c3");
+ QTest::addColumn<QGeoCoordinate>("probe");
+ QTest::addColumn<bool>("result");
+
+ QList<QGeoCoordinate> c;
+ c.append(QGeoCoordinate(1,1));
+ c.append(QGeoCoordinate(2,2));
+ c.append(QGeoCoordinate(3,0));
+
+ QTest::newRow("One of the points") << c[0] << c[1] << c[2] << QGeoCoordinate(2, 2) << true;
+ QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << QGeoCoordinate(0, 0) << false;
+ QTest::newRow("Inside the bounds") << c[0] << c[1] << c[2] << QGeoCoordinate(1, 0) << true;
+ QTest::newRow("Inside the bounds") << c[0] << c[1] << c[2] << QGeoCoordinate(1.1, 0.1) << true;
+}
+
+void tst_QGeoPolygon::boundingGeoRectangle()
+{
+ QFETCH(QGeoCoordinate, c1);
+ QFETCH(QGeoCoordinate, c2);
+ QFETCH(QGeoCoordinate, c3);
+ QFETCH(QGeoCoordinate, probe);
+ QFETCH(bool, result);
+
+ QList<QGeoCoordinate> coords;
+ coords.append(c1);
+ coords.append(c2);
+ coords.append(c3);
+ QGeoPolygon p(coords);
+
+ QGeoRectangle box = p.boundingGeoRectangle();
+ QCOMPARE(box.contains(probe), result);
+}
+
+void tst_QGeoPolygon::extendShape()
+{
+ QFETCH(QGeoCoordinate, c1);
+ QFETCH(QGeoCoordinate, c2);
+ QFETCH(QGeoCoordinate, c3);
+ QFETCH(QGeoCoordinate, probe);
+ QFETCH(bool, before);
+ QFETCH(bool, after);
+
+ QList<QGeoCoordinate> coords;
+ coords.append(c1);
+ coords.append(c2);
+ coords.append(c3);
+ QGeoPolygon p(coords);
+
+
+ QCOMPARE(p.contains(probe), before);
+ p.extendShape(probe);
+ QCOMPARE(p.contains(probe), after);
+}
+
+void tst_QGeoPolygon::extendShape_data()
+{
+ QTest::addColumn<QGeoCoordinate>("c1");
+ QTest::addColumn<QGeoCoordinate>("c2");
+ QTest::addColumn<QGeoCoordinate>("c3");
+ QTest::addColumn<QGeoCoordinate>("probe");
+ QTest::addColumn<bool>("before");
+ QTest::addColumn<bool>("after");
+
+ QList<QGeoCoordinate> c;
+ c.append(QGeoCoordinate(1,1));
+ c.append(QGeoCoordinate(2,2));
+ c.append(QGeoCoordinate(3,0));
+
+ QTest::newRow("One of the points") << c[0] << c[1] << c[2] << QGeoCoordinate(2, 2) << true << true;
+ QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << QGeoCoordinate(0, 0) << false << true;
+ QTest::newRow("Contained point") << c[0] << c[1] << c[2] << QGeoCoordinate(2.0, 1.0) << true << true;
+}
+
+QTEST_MAIN(tst_QGeoPolygon)
+#include "tst_qgeopolygon.moc"