Skip to content

Commit

Permalink
Refine QgsFeature geometry getters/setters
Browse files Browse the repository at this point in the history
All pointer based methods have been removed.

Now we have only:

  void setGeometry( const QgsGeometry& geom )

and

  QgsGeometry geometry() const

Benefits include avoiding a whole lot of tricky pointer lifetime
issues, potential memory leaks, and finally closing #777, which
has survived for over 9 years!...

Impacts on PyQGIS code:
- no more need for the messy
  g = QgsGeometry( feature.geometry() )
  workaround, just use g = feature.geometry() instead
- IMPORTANT: you can no longer test whether a feature has geometry
 using `if f.geometry():`, since QgsFeature::geometry() will
 *always* return an object. Instead, use
 `if not f.geometry().isEmpty():`, or preferably the new method
 `if not f.hasGeometry():`

Fix #777
  • Loading branch information
nyalldawson committed Aug 1, 2016
1 parent aceddae commit bd7d913
Show file tree
Hide file tree
Showing 202 changed files with 1,377 additions and 1,313 deletions.
29 changes: 29 additions & 0 deletions doc/api_break.dox
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,34 @@ attributeIndexes(), pkAttributeIndexes(), isSaveAndLoadStyleToDBSupported()</li>
<li>geometryAndOwnership() has been removed. Use geometry() instead.</li>
<li>setGeometryAndOwnership() has been removed. Use setGeometry() instead.</li>
<li>The setGeometry( QgsGeometry* ) method has been removed, use setGeometry( const QgsGeometry& ) instead.</li>
<li>The geometry() method now returns a copy of the geometry, not a pointer. Since QgsGeometry objects are
implicitly shared this is a low-cost copy, and avoids ownership and dangling pointer issues. <b>Very important: Testing that
a feature has a geometry is now done using the new hasGeometry() method. QgsFeature::geometry() will ALWAYS return
true, as the method will return an empty geometry if the feature has no geometry.</b></li>
<li>The temporary constGeometry() method has been removed. Use geometry() instead.</li>
<li>setFields( const QgsFields*, bool ) has been removed, use setFields( const QgsFields&, bool ) instead.</li>
</ul>

\subsection qgis_api_break_3_0_QgsGeometryAnalyzer QgsGeometryAnalyzer

<ul>
<li>locateBetweenMeasures() and locateAlongMeasure() now take geometry references, not pointers</li>
</ul>

\subsection qgis_api_break_3_0_QgsGroupWMSDataDialog QgsGroupWMSDataDialog

<ul>
<li>QgsGroupWMSDataDialo has been renamed to QgsGroupWmsDataDialog</li>
</ul>

\subsection qgis_api_break_3_0_QgsHighlight QgsHighlight

<ul>
<li>The QgsHighlight constructor now takes a geometry reference, not a pointer.</li>
</ul>



\subsection qgis_api_break_3_0_QgsVectorDataProvider QgsVectorDataProvider

<ul>
Expand All @@ -236,6 +255,7 @@ only affects third party c++ providers, and does not affect PyQGIS scripts.</li>
<li>The SaveAsShapefile, SelectGeometryAtId, RandomSelectGeometryAtId and SequentialSelectGeometryAtId
capabilities have been removed, as they were unused and had no effect.</li>
<li>capabilities() now returns a typesafe QgsVectorDataProvider::Capabilities object, not an integer.</li>
<li>convertToProviderType() now takes a geometry reference, not a pointer.</li>
</ul>

\subsection qgis_api_break_3_0_QgsLabelingEngineInterface QgsLabelingEngineInterface
Expand Down Expand Up @@ -331,6 +351,7 @@ be returned instead of a null pointer if no transformation is required.</li>

<ul>
<li>init(QgsMapRenderer*) has been removed. Use init(const QgsMapSettings&) instead.</li>
<li>prepareGeometry and geometryRequiresPreparation now take geometry references, not pointers.</li>
</ul>

\subsection qgis_api_break_3_0_QgsOSMElement QgsOSMElement
Expand Down Expand Up @@ -435,6 +456,8 @@ setExcludeAttributesWms()</li>
setExcludeAttributesWfs()</li>
<li>changeGeometry() now accepts a geometry reference, not a pointer.</li>
<li>The geometryChanged() signal now uses a const QgsGeometry reference.</li>
<li>The deprecated removePolygonIntersections has been removed.</li>
<li>addTopologicalPoints() now takes a geometry reference, not a pointer.</li>
</ul>

\subsection qgis_api_break_3_0_QgsVectorLayerEditBuffer QgsVectorLayerEditBuffer
Expand All @@ -444,6 +467,12 @@ setExcludeAttributesWfs()</li>
<li>The geometryChanged() signal now uses a const QgsGeometry reference.</li>
</ul>

\subsection qgis_api_break_3_0_QgsVectorLayerEditUtils QgsVectorLayerEditUtils

<ul>
<li>addTopologicalPoints() now accepts a geometry reference, not a pointer.</li>
</ul>

\subsection qgis_api_break_3_0_QgsVectorLayerImport QgsVectorLayerImport

<ul>
Expand Down
4 changes: 2 additions & 2 deletions python/analysis/vector/qgsgeometryanalyzer.sip
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ class QgsGeometryAnalyzer
bool forceSingleGeometry = false, QgsVectorDataProvider* memoryProvider = 0, QProgressDialog* p = 0 );

/** Returns linear reference geometry as a multiline (or 0 if no match). Currently, the z-coordinates are considered to be the measures (no support for m-values in QGIS)*/
QgsGeometry* locateBetweenMeasures( double fromMeasure, double toMeasure, const QgsGeometry* lineGeom );
QgsGeometry* locateBetweenMeasures( double fromMeasure, double toMeasure, const QgsGeometry& lineGeom );
/** Returns linear reference geometry. Unlike the PostGIS function, this method always returns multipoint or 0 if no match (not geometry collection).
* Currently, the z-coordinates are considered to be the measures (no support for m-values in QGIS)
*/
QgsGeometry* locateAlongMeasure( double measure, const QgsGeometry* lineGeom );
QgsGeometry* locateAlongMeasure( double measure, const QgsGeometry& lineGeom );

};
8 changes: 8 additions & 0 deletions python/core/geometry/qgsgeometry.sip
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,14 @@ class QgsGeometry
*/
int makeDifference( const QgsGeometry* other );

/** Returns the geometry formed by modifying this geometry such that it does not
* intersect the other geometry.
* @param other geometry that should not be intersect
* @return difference geometry, or empty geometry if difference could not be calculated
* @note added in QGIS 3.0
*/
QgsGeometry makeDifference( const QgsGeometry& other ) const;

/** Returns the bounding box of this feature*/
QgsRectangle boundingBox() const;

Expand Down
53 changes: 22 additions & 31 deletions python/core/qgsfeature.sip
Original file line number Diff line number Diff line change
Expand Up @@ -322,41 +322,32 @@ class QgsFeature
*/
void setValid( bool validity );

/** Get the geometry object associated with this feature. If the geometry
* is not going to be modified than calling the const @link constGeometry @endlink
* method is preferable as it avoids a potentially expensive detach operation.
*
* It is possible to modify the geometry in place but this will
* be removed in 3.0 and therefore @link setGeometry @endlink should be called explicitly.
*
* @note will be modified to return by value in QGIS 3.0: `QgsGeometry geometry() const;`
*
* @returns pointer to feature's geometry
* @see constGeometry
* @see setGeometry
/** Returns true if the feature has an associated geometry.
* @see geometry()
* @note added in QGIS 3.0.
*/
QgsGeometry* geometry();

/** Gets a const pointer to the geometry object associated with this feature. If the geometry
* is not going to be modified than this method is preferable to the non-const
* @link geometry @endlink method.
* @note this is a temporary method for 2.x release cycle. Will be removed in QGIS 3.0.
* @returns const pointer to feature's geometry
* @see geometry
* @see geometryAndOwnership
* @see setGeometry
* @note added in QGIS 2.9
* @note will be removed in QGIS 3.0
bool hasGeometry() const;

/** Returns the geometry associated with this feature. If the feature has no geometry,
* an empty QgsGeometry object will be returned.
* @see hasGeometry()
* @see setGeometry()
*/
QgsGeometry geometry() const;

/** Set the feature's geometry.
* @param geometry new feature geometry
* @see geometry()
* @see clearGeometry()
*/
const QgsGeometry* constGeometry() const;
void setGeometry( const QgsGeometry& geometry );

/** Set this feature's geometry from another QgsGeometry object. This method performs a deep copy
* of the geometry.
* @param geom new feature geometry
* @see geometry
* @see constGeometry
/** Removes any geometry associated with the feature.
* @see setGeometry()
* @see hasGeometry()
* @note added in QGIS 3.0
*/
void setGeometry( const QgsGeometry& geom );
void clearGeometry();

/** Assign a field map with the feature to allow attribute access by attribute name.
* @param fields The attribute fields which this feature holds
Expand Down
8 changes: 4 additions & 4 deletions python/core/qgspallabeling.sip
Original file line number Diff line number Diff line change
Expand Up @@ -991,10 +991,10 @@ class QgsPalLabeling : QgsLabelingEngineInterface
* @param context render context
* @param ct coordinate transform, or invalid transform if no transformation required
* @param clipGeometry geometry to clip features to, if applicable
* @returns prepared geometry, the caller takes ownership
* @returns prepared geometry
* @note added in QGIS 2.9
*/
static QgsGeometry* prepareGeometry( const QgsGeometry *geometry, QgsRenderContext &context, const QgsCoordinateTransform& ct, QgsGeometry *clipGeometry = 0 ) /Factory/;
static QgsGeometry prepareGeometry( const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform& ct, QgsGeometry *clipGeometry = 0 ) /Factory/;

/** Checks whether a geometry requires preparation before registration with PAL
* @param geometry geometry to prepare
Expand All @@ -1004,7 +1004,7 @@ class QgsPalLabeling : QgsLabelingEngineInterface
* @returns true if geometry requires preparation
* @note added in QGIS 2.9
*/
static bool geometryRequiresPreparation( const QgsGeometry *geometry, QgsRenderContext &context, const QgsCoordinateTransform& ct, QgsGeometry *clipGeometry = 0 );
static bool geometryRequiresPreparation( const QgsGeometry& geometry, QgsRenderContext &context, const QgsCoordinateTransform& ct, QgsGeometry *clipGeometry = 0 );

/** Splits a text string to a list of separate lines, using a specified wrap character.
* The text string will be split on either newline characters or the wrap character.
Expand Down Expand Up @@ -1054,5 +1054,5 @@ class QgsPalLabeling : QgsLabelingEngineInterface
* @returns true if geometry exceeds minimum size
* @note added in QGIS 2.9
*/
static bool checkMinimumSizeMM( const QgsRenderContext &context, const QgsGeometry *geom, double minSize );
static bool checkMinimumSizeMM( const QgsRenderContext &context, const QgsGeometry* geom, double minSize );
};
2 changes: 1 addition & 1 deletion python/core/qgsvectordataprovider.sip
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ class QgsVectorDataProvider : QgsDataProvider

/** Converts the geometry to the provider type if possible / necessary
@return the converted geometry or nullptr if no conversion was necessary or possible*/
QgsGeometry* convertToProviderType( const QgsGeometry* geom ) const /Factory/;
QgsGeometry* convertToProviderType( const QgsGeometry& geom ) const /Factory/;
};

QFlags<QgsVectorDataProvider::Capability> operator|(QgsVectorDataProvider::Capability f1, QFlags<QgsVectorDataProvider::Capability> f2);
12 changes: 1 addition & 11 deletions python/core/qgsvectorlayer.sip
Original file line number Diff line number Diff line change
Expand Up @@ -763,22 +763,12 @@ class QgsVectorLayer : QgsMapLayer
*/
int splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing = false );

/** Changes the specified geometry such that it has no intersections with other
* polygon (or multipolygon) geometries in this vector layer
* @param geom geometry to modify
* @param ignoreFeatures list of feature ids where intersections should be ignored
* @return 0 in case of success
*
* @deprecated since 2.2 - not being used for "avoid intersections" functionality anymore
*/
int removePolygonIntersections( QgsGeometry* geom, const QgsFeatureIds& ignoreFeatures = QgsFeatureIds() ) /Deprecated/;

/** Adds topological points for every vertex of the geometry.
* @param geom the geometry where each vertex is added to segments of other features
* @note geom is not going to be modified by the function
* @return 0 in case of success
*/
int addTopologicalPoints( const QgsGeometry* geom );
int addTopologicalPoints( const QgsGeometry& geom );

/** Adds a vertex to segments which intersect point p but don't
* already have a vertex there. If a feature already has a vertex at position p,
Expand Down
2 changes: 1 addition & 1 deletion python/core/qgsvectorlayereditutils.sip
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class QgsVectorLayerEditUtils
* @note geom is not going to be modified by the function
* @return 0 in case of success
*/
int addTopologicalPoints( const QgsGeometry *geom );
int addTopologicalPoints( const QgsGeometry& geom );

/** Adds a vertex to segments which intersect point p but don't
* already have a vertex there. If a feature already has a vertex at position p,
Expand Down
2 changes: 1 addition & 1 deletion python/gui/qgshighlight.sip
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class QgsHighlight : QgsMapCanvasItem
#include <qgshighlight.h>
%End
public:
QgsHighlight( QgsMapCanvas *mapCanvas, const QgsGeometry *geom, QgsVectorLayer *layer );
QgsHighlight( QgsMapCanvas *mapCanvas, const QgsGeometry& geom, QgsVectorLayer *layer );
~QgsHighlight();

/** Set line/outline to color, polygon fill to color with alpha = 63.