21 #include <QDomDocument> 22 #include <QJsonObject> 31 #include "qgis_core.h" 40 #include "json_fwd.hpp" 125 Q_PROPERTY(
bool isNull READ isNull )
137 NothingHappened = 1000,
275 static QgsGeometry collectGeometry(
const QVector<QgsGeometry> &geometries );
292 static QgsGeometry createWedgeBuffer(
const QgsPoint ¢er,
double azimuth,
double angularWidth,
293 double outerRadius,
double innerRadius = 0 );
300 void fromWkb(
unsigned char *wkb,
int length )
SIP_SKIP;
306 void fromWkb(
const QByteArray &wkb );
326 bool isEmpty()
const;
329 bool isMultipart()
const;
368 FlagAllowSelfTouchingHoles = 1 << 0,
379 bool isGeosValid( QgsGeometry::ValidityFlags flags =
nullptr )
const;
389 bool isSimple()
const;
417 double length()
const;
601 double hausdorffDistance(
const QgsGeometry &geom )
const;
621 double hausdorffDistanceDensify(
const QgsGeometry &geom,
double densifyFraction )
const;
637 QgsPointXY closestVertex(
const QgsPointXY &point,
int &atVertex
SIP_OUT,
int &beforeVertex SIP_OUT,
int &afterVertex SIP_OUT,
double &sqrDist SIP_OUT )
const;
647 double distanceToVertex(
int vertex )
const;
656 double angleAtVertex(
int vertex )
const;
670 void adjacentVertices(
int atVertex,
int &beforeVertex
SIP_OUT,
int &afterVertex SIP_OUT )
const;
684 bool insertVertex(
double x,
double y,
int beforeVertex );
698 bool insertVertex(
const QgsPoint &point,
int beforeVertex );
707 bool moveVertex(
double x,
double y,
int atVertex );
716 bool moveVertex(
const QgsPoint &p,
int atVertex );
729 bool deleteVertex(
int atVertex );
736 QgsPoint vertexAt(
int atVertex )
const;
770 double closestVertexWithContext(
const QgsPointXY &point,
int &atVertex
SIP_OUT )
const;
836 QgsGeometry removeInteriorRings(
double minimumAllowedArea = -1 )
const;
842 OperationResult translate(
double dx,
double dy,
double dz = 0.0,
double dm = 0.0 );
868 OperationResult transform( const QTransform &t,
double zTranslate = 0.0,
double zScale = 1.0,
double mTranslate = 0.0,
double mScale = 1.0 );
910 int makeDifferenceInPlace( const
QgsGeometry &other ) SIP_SKIP;
934 QgsGeometry orientedMinimumBoundingBox(
double &area SIP_OUT,
double &
angle SIP_OUT,
double &width SIP_OUT,
double &height SIP_OUT ) const;
941 QgsGeometry orientedMinimumBoundingBox() const SIP_SKIP;
951 QgsGeometry minimalEnclosingCircle(
QgsPointXY ¢er SIP_OUT,
double &radius SIP_OUT,
unsigned int segments = 36 ) const;
958 QgsGeometry minimalEnclosingCircle(
unsigned int segments = 36 ) const SIP_SKIP;
968 QgsGeometry orthogonalize(
double tolerance = 1.0E-8,
int maxIterations = 1000,
double angleThreshold = 15.0 ) const;
982 QgsGeometry snappedToGrid(
double hSpacing,
double vSpacing,
double dSpacing = 0,
double mSpacing = 0 ) const;
1004 bool removeDuplicateNodes(
double epsilon = 4 * std::numeric_limits<
double>::epsilon(),
bool useZValues = false );
1015 bool intersects( const
QgsRectangle &rectangle ) const;
1026 bool intersects( const
QgsGeometry &geometry ) const;
1037 bool boundingBoxIntersects( const
QgsRectangle &rectangle ) const;
1048 bool boundingBoxIntersects( const
QgsGeometry &geometry ) const;
1059 bool contains( const
QgsGeometry &geometry ) const;
1065 bool disjoint( const
QgsGeometry &geometry ) const;
1071 bool touches( const
QgsGeometry &geometry ) const;
1077 bool overlaps( const
QgsGeometry &geometry ) const;
1090 bool crosses( const
QgsGeometry &geometry ) const;
1125 QgsGeometry buffer(
double distance,
int segments )
const;
1149 QgsGeometry offsetCurve(
double distance,
int segments,
JoinStyle joinStyle,
double miterLimit )
const;
1168 double miterLimit = 2.0 )
const;
1187 QgsGeometry taperedBuffer(
double startWidth,
double endWidth,
int segments )
const;
1203 QgsGeometry variableWidthBufferByM(
int segments )
const;
1211 QgsGeometry extendLine(
double startDistance,
double endDistance )
const;
1225 QgsGeometry densifyByCount(
int extraNodesPerSegment )
const;
1241 QgsGeometry densifyByDistance(
double distance )
const;
1323 QgsGeometry delaunayTriangulation(
double tolerance = 0.0,
bool edgesOnly =
false )
const;
1344 QgsGeometry subdivide(
int maxNodes = 256 )
const;
1374 double lineLocatePoint(
const QgsGeometry &point )
const;
1385 double interpolateAngle(
double distance )
const;
1470 QVector< QgsPointXY > randomPointsInPolygon(
int count,
const std::function<
bool(
const QgsPointXY & ) > &acceptPoint,
unsigned long seed = 0,
QgsFeedback *feedback =
nullptr )
const;
1485 QVector< QgsPointXY > randomPointsInPolygon(
int count,
unsigned long seed = 0,
QgsFeedback *feedback =
nullptr )
const;
1503 const
QgsWkbTypes::GeometryType type = sipCpp->type();
1504 if ( sipCpp->isNull() )
1506 PyErr_SetString( PyExc_ValueError, QStringLiteral(
"Cannot generate points inside a null geometry." ).toUtf8().constData() );
1511 PyErr_SetString( PyExc_TypeError, QStringLiteral(
"Cannot generate points inside a %1 geometry. Only Polygon types are permitted." ).arg(
QgsWkbTypes::displayString( sipCpp->wkbType() ) ).toUtf8().constData() );
1516 const sipMappedType *qvector_type = sipFindMappedType(
"QVector<QgsPointXY>" );
1517 sipRes = sipConvertFromNewType(
new QVector< QgsPointXY >( sipCpp->randomPointsInPolygon( a0, a1 ) ), qvector_type, Py_None );
1529 QByteArray asWkb()
const;
1536 QString asWkt(
int precision = 17 )
const;
1539 SIP_PYOBJECT __repr__();
1542 if ( sipCpp->isNull() )
1543 str = QStringLiteral(
"<QgsGeometry: null>" );
1546 QString wkt = sipCpp->asWkt();
1547 if ( wkt.length() > 1000 )
1548 wkt = wkt.left( 1000 ) + QStringLiteral(
"..." );
1549 str = QStringLiteral(
"<QgsGeometry: %1>" ).arg( wkt );
1551 sipRes = PyUnicode_FromString( str.toUtf8().constData() );
1558 QString asJson(
int precision = 17 )
const;
1565 virtual json asJsonObject(
int precision = 17 ) const SIP_SKIP;
1602 if ( sipCpp->isNull() )
1604 PyErr_SetString( PyExc_ValueError, QStringLiteral(
"Null geometry cannot be converted to a point." ).toUtf8().constData() );
1609 PyErr_SetString( PyExc_TypeError, QStringLiteral(
"%1 geometry cannot be converted to a point. Only Point types are permitted." ).arg(
QgsWkbTypes::displayString( type ) ).toUtf8().constData() );
1614 sipRes = sipConvertFromNewType(
new QgsPointXY( sipCpp->asPoint() ), sipType_QgsPointXY, Py_None );
1629 QgsPolylineXY asPolyline()
const;
1642 SIP_PYOBJECT asPolyline() const
SIP_TYPEHINT( QgsPolylineXY );
1645 if ( sipCpp->isNull() )
1647 PyErr_SetString( PyExc_ValueError, QStringLiteral(
"Null geometry cannot be converted to a polyline." ).toUtf8().constData() );
1652 PyErr_SetString( PyExc_TypeError, QStringLiteral(
"%1 geometry cannot be converted to a polyline. Only single line or curve types are permitted." ).arg(
QgsWkbTypes::displayString( type ) ).toUtf8().constData() );
1657 const sipMappedType *qvector_type = sipFindMappedType(
"QVector< QgsPointXY >" );
1658 sipRes = sipConvertFromNewType(
new QgsPolylineXY( sipCpp->asPolyline() ), qvector_type, Py_None );
1689 if ( sipCpp->isNull() )
1691 PyErr_SetString( PyExc_ValueError, QStringLiteral(
"Null geometry cannot be converted to a polygon." ).toUtf8().constData() );
1696 PyErr_SetString( PyExc_TypeError, QStringLiteral(
"%1 geometry cannot be converted to a polygon. Only single polygon or curve polygon types are permitted." ).arg(
QgsWkbTypes::displayString( type ) ).toUtf8().constData() );
1701 const sipMappedType *qvector_type = sipFindMappedType(
"QVector<QVector<QgsPointXY>>" );
1702 sipRes = sipConvertFromNewType(
new QgsPolygonXY( sipCpp->asPolygon() ), qvector_type, Py_None );
1731 if ( sipCpp->isNull() )
1733 PyErr_SetString( PyExc_ValueError, QStringLiteral(
"Null geometry cannot be converted to a multipoint." ).toUtf8().constData() );
1738 PyErr_SetString( PyExc_TypeError, QStringLiteral(
"%1 geometry cannot be converted to a multipoint. Only multipoint types are permitted." ).arg(
QgsWkbTypes::displayString( type ) ).toUtf8().constData() );
1743 const sipMappedType *qvector_type = sipFindMappedType(
"QVector< QgsPointXY >" );
1744 sipRes = sipConvertFromNewType(
new QgsPolylineXY( sipCpp->asMultiPoint() ), qvector_type, Py_None );
1775 if ( sipCpp->isNull() )
1777 PyErr_SetString( PyExc_ValueError, QStringLiteral(
"Null geometry cannot be converted to a multilinestring." ).toUtf8().constData() );
1782 PyErr_SetString( PyExc_TypeError, QStringLiteral(
"%1 geometry cannot be converted to a multilinestring. Only multi linestring or curves are permitted." ).arg(
QgsWkbTypes::displayString( type ) ).toUtf8().constData() );
1787 const sipMappedType *qvector_type = sipFindMappedType(
"QVector<QVector<QgsPointXY>>" );
1788 sipRes = sipConvertFromNewType(
new QgsMultiPolylineXY( sipCpp->asMultiPolyline() ), qvector_type, Py_None );
1819 if ( sipCpp->isNull() )
1821 PyErr_SetString( PyExc_ValueError, QStringLiteral(
"Null geometry cannot be converted to a multipolygon." ).toUtf8().constData() );
1826 PyErr_SetString( PyExc_TypeError, QStringLiteral(
"%1 geometry cannot be converted to a multipolygon. Only multi polygon or curves are permitted." ).arg(
QgsWkbTypes::displayString( type ) ).toUtf8().constData() );
1831 const sipMappedType *qvector_type = sipFindMappedType(
"QVector<QVector<QVector<QgsPointXY>>>" );
1832 sipRes = sipConvertFromNewType(
new QgsMultiPolygonXY( sipCpp->asMultiPolygon() ), qvector_type, Py_None );
1841 QVector<QgsGeometry> asGeometryCollection()
const;
1848 QPointF asQPointF()
const;
1856 QPolygonF asQPolygonF()
const;
1864 bool deleteRing(
int ringNum,
int partNum = 0 );
1871 bool deletePart(
int partNum );
1881 bool convertToMultiType();
1892 bool convertToSingleType();
1915 int avoidIntersections(
const QList<QgsVectorLayer *> &avoidIntersectionsLayers,
1954 : mMessage( QStringLiteral(
"none" ) )
1961 Error(
const QString &m,
const QgsPointXY &p )
1964 , mHasLocation( true ) {}
1969 QString what()
const;
1974 QgsPointXY where()
const;
1979 bool hasWhere()
const;
1982 SIP_PYOBJECT __repr__();
1984 QString str = QStringLiteral(
"<QgsGeometry.Error: %1>" ).arg( sipCpp->what() );
1985 sipRes = PyUnicode_FromString( str.toUtf8().data() );
1991 return other.mMessage == mMessage && other.mHasLocation == mHasLocation && other.mLocation == mLocation;
1996 QgsPointXY mLocation;
1997 bool mHasLocation =
false;
2018 void validateGeometry( QVector<QgsGeometry::Error> &errors
SIP_OUT,
ValidationMethod method = ValidatorQgisInternal, QgsGeometry::ValidityFlags flags =
nullptr )
const;
2025 static QgsGeometry unaryUnion(
const QVector<QgsGeometry> &geometries );
2035 static QgsGeometry polygonize(
const QVector<QgsGeometry> &geometries );
2052 bool requiresConversionToStraightSegments()
const;
2066 void draw( QPainter &p )
const;
2100 QString lastError()
const;
2111 void filterVertices(
const std::function<
bool(
const QgsPoint & ) > &filter )
SIP_SKIP;
2143 static QgsGeometry fromQPolygonF(
const QPolygonF &polygon );
2152 Q_DECL_DEPRECATED
static QgsPolylineXY createPolylineFromQPolygonF(
const QPolygonF &polygon )
SIP_DEPRECATED;
2161 Q_DECL_DEPRECATED
static QgsPolygonXY createPolygonFromQPolygonF(
const QPolygonF &polygon )
SIP_DEPRECATED;
2174 static bool compare(
const QgsPolylineXY &p1,
const QgsPolylineXY &p2,
2175 double epsilon = 4 * std::numeric_limits<double>::epsilon() );
2186 static bool compare(
const QgsPolygonXY &p1,
const QgsPolygonXY &p2,
2187 double epsilon = 4 * std::numeric_limits<double>::epsilon() );
2199 static bool compare(
const QgsMultiPolygonXY &p1,
const QgsMultiPolygonXY &p2,
2200 double epsilon = 4 * std::numeric_limits<double>::epsilon() );
2221 static bool compare( PyObject *obj1, PyObject *obj2,
double epsilon = 4 * std::numeric_limits<double>::epsilon() );
2229 if ( PyList_Check( a0 ) && PyList_Check( a1 ) &&
2230 PyList_GET_SIZE( a0 ) && PyList_GET_SIZE( a1 ) )
2232 PyObject *o0 = PyList_GetItem( a0, 0 );
2233 PyObject *o1 = PyList_GetItem( a1, 0 );
2237 if ( sipCanConvertToType( o0, sipType_QgsPointXY, SIP_NOT_NONE ) &&
2238 sipCanConvertToType( o1, sipType_QgsPointXY, SIP_NOT_NONE ) &&
2239 sipCanConvertToType( a0, sipType_QVector_0100QgsPointXY, SIP_NOT_NONE ) &&
2240 sipCanConvertToType( a1, sipType_QVector_0100QgsPointXY, SIP_NOT_NONE ) )
2244 p0 =
reinterpret_cast<QgsPolylineXY *
>( sipConvertToType( a0, sipType_QVector_0100QgsPointXY, 0, SIP_NOT_NONE, &state0, &sipIsErr ) );
2245 p1 =
reinterpret_cast<QgsPolylineXY *
>( sipConvertToType( a1, sipType_QVector_0100QgsPointXY, 0, SIP_NOT_NONE, &state1, &sipIsErr ) );
2248 sipReleaseType( p0, sipType_QVector_0100QgsPointXY, state0 );
2249 sipReleaseType( p1, sipType_QVector_0100QgsPointXY, state1 );
2256 else if ( PyList_Check( o0 ) && PyList_Check( o1 ) &&
2257 PyList_GET_SIZE( o0 ) && PyList_GET_SIZE( o1 ) )
2259 PyObject *oo0 = PyList_GetItem( o0, 0 );
2260 PyObject *oo1 = PyList_GetItem( o1, 0 );
2264 if ( sipCanConvertToType( oo0, sipType_QgsPointXY, SIP_NOT_NONE ) &&
2265 sipCanConvertToType( oo1, sipType_QgsPointXY, SIP_NOT_NONE ) &&
2266 sipCanConvertToType( a0, sipType_QVector_0600QVector_0100QgsPointXY, SIP_NOT_NONE ) &&
2267 sipCanConvertToType( a1, sipType_QVector_0600QVector_0100QgsPointXY, SIP_NOT_NONE ) )
2271 p0 =
reinterpret_cast<QgsPolygonXY *
>( sipConvertToType( a0, sipType_QVector_0600QVector_0100QgsPointXY, 0, SIP_NOT_NONE, &state0, &sipIsErr ) );
2272 p1 =
reinterpret_cast<QgsPolygonXY *
>( sipConvertToType( a1, sipType_QVector_0600QVector_0100QgsPointXY, 0, SIP_NOT_NONE, &state1, &sipIsErr ) );
2275 sipReleaseType( p0, sipType_QVector_0600QVector_0100QgsPointXY, state0 );
2276 sipReleaseType( p1, sipType_QVector_0600QVector_0100QgsPointXY, state1 );
2283 else if ( PyList_Check( oo0 ) && PyList_Check( oo1 ) &&
2284 PyList_GET_SIZE( oo0 ) && PyList_GET_SIZE( oo1 ) )
2286 PyObject *ooo0 = PyList_GetItem( oo0, 0 );
2287 PyObject *ooo1 = PyList_GetItem( oo1, 0 );
2291 if ( sipCanConvertToType( ooo0, sipType_QgsPointXY, SIP_NOT_NONE ) &&
2292 sipCanConvertToType( ooo1, sipType_QgsPointXY, SIP_NOT_NONE ) &&
2293 sipCanConvertToType( a0, sipType_QVector_0600QVector_0600QVector_0100QgsPointXY, SIP_NOT_NONE ) &&
2294 sipCanConvertToType( a1, sipType_QVector_0600QVector_0600QVector_0100QgsPointXY, SIP_NOT_NONE ) )
2296 QgsMultiPolygonXY *p0;
2297 QgsMultiPolygonXY *p1;
2298 p0 =
reinterpret_cast<QgsMultiPolygonXY *
>( sipConvertToType( a0, sipType_QVector_0600QVector_0600QVector_0100QgsPointXY, 0, SIP_NOT_NONE, &state0, &sipIsErr ) );
2299 p1 =
reinterpret_cast<QgsMultiPolygonXY *
>( sipConvertToType( a1, sipType_QVector_0600QVector_0600QVector_0100QgsPointXY, 0, SIP_NOT_NONE, &state1, &sipIsErr ) );
2302 sipReleaseType( p0, sipType_QVector_0600QVector_0600QVector_0100QgsPointXY, state0 );
2303 sipReleaseType( p1, sipType_QVector_0600QVector_0600QVector_0100QgsPointXY, state1 );
2336 QgsGeometry smooth(
unsigned int iterations = 1,
double offset = 0.25,
2337 double minimumDistance = -1.0,
double maxAngle = 180.0 )
const;
2349 static void convertPointList(
const QVector<QgsPointXY> &input,
QgsPointSequence &output );
2356 static void convertPointList(
const QgsPointSequence &input, QVector<QgsPointXY> &output );
2359 operator QVariant()
const 2361 return QVariant::fromValue( *
this );
2369 mutable QString mLastError;
2381 void reset( std::unique_ptr< QgsAbstractGeometry > newGeometry );
2383 static void convertToPolyline(
const QgsPointSequence &input, QgsPolylineXY &output );
2384 static void convertPolygon(
const QgsPolygon &input, QgsPolygonXY &output );
2387 QgsGeometry convertToPoint(
bool destMultipart )
const;
2389 QgsGeometry convertToLine(
bool destMultipart )
const;
2391 QgsGeometry convertToPolygon(
bool destMultipart )
const;
2404 std::unique_ptr< QgsLineString > smoothLine(
const QgsLineString &line,
unsigned int iterations = 1,
double offset = 0.25,
2405 double minimumDistance = -1,
double maxAngle = 180.0 )
const;
2418 std::unique_ptr< QgsPolygon > smoothPolygon(
const QgsPolygon &polygon,
unsigned int iterations = 1,
double offset = 0.25,
2419 double minimumDistance = -1,
double maxAngle = 180.0 )
const;
2427 Q_DECLARE_OPERATORS_FOR_FLAGS( QgsGeometry::ValidityFlags )
Geometry engine misses a method implemented or an error occurred in the geometry engine.
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
A rectangle specified with double values.
Java-style iterator for traversal of parts of a geometry.
The input ring doesn't have any existing ring to fit into.
Java-style iterator for traversal of vertices of a geometry.
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
The source geometry is not multi.
Maximum angle between generating radii (lines from arc center to output vertices) ...
Use GEOS validation methods.
bool operator==(const QgsGeometry::Error &other) const
Java-style iterator for const traversal of parts of a geometry.
Handles storage of information regarding WKB types and their properties.
A class to represent a 2D point.
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
A geometry is the spatial representation of a feature.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
The part_iterator class provides STL-style iterator for const references to geometry parts...
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
EndCapStyle
End cap styles for buffers.
#define SIP_TYPEHINT(type)
OperationResult
Success or failure of a geometry operation.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Error(const QString &m, const QgsPointXY &p)
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
CORE_EXPORT QDataStream & operator<<(QDataStream &out, const QgsGeometry &geometry)
Writes the geometry to stream out. QGIS version compatibility is not guaranteed.
Perform transforms between map coordinates and device coordinates.
The selected geometry cannot be found.
No features were selected.
More than one features were selected.
Type
The WKB type describes the number of dimensions a geometry has.
Utility class for identifying a unique vertex within a geometry.
const double DEFAULT_SEGMENT_EPSILON
Default snapping tolerance for segments.
The input ring crosses existing rings (it is not disjoint)
The part_iterator class provides STL-style iterator for geometry parts.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Use internal QgsGeometryValidator method.
Q_DECLARE_METATYPE(QgsMeshTimeSettings)
The input ring is not closed.
Square cap (extends past start/end of line by buffer distance)
Abstract base class for curved geometry type.
Abstract base class for all geometries.
The vertex_iterator class provides STL-style iterator for vertices.
Point geometry type, with support for z-dimension and m-values.
This class offers geometry processing methods.
CORE_EXPORT QDataStream & operator>>(QDataStream &in, QgsGeometry &geometry)
Reads a geometry from stream in into geometry. QGIS version compatibility is not guaranteed.
BufferSide
Side of line to buffer.
QVector< QgsPoint > QgsPointSequence
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
The base geometry on which the operation is done is invalid or empty.
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
Line string geometry type, with support for z-dimension and m-values.
ValidationMethod
Available methods for validating geometries.
ValidityFlag
Validity check flags.
static QString displayString(Type type)
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
The input ring is not valid.
Contains geometry relation and modification algorithms.
Custom exception class for Coordinate Reference System related exceptions.
JoinStyle
Join styles for buffers.
Flat cap (in line with start/end of line)
Represents a vector layer which manages a vector based data sets.
static Type flatType(Type type)
Returns the flat type for a WKB type.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negative values mean left ...
static bool compare(const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compares two polylines for equality within a specified tolerance.