Skip to content
Permalink
Browse files
Refine QgsFeature geometry getters/setters
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 bd7d913379b68a8104608b1afab4d380e4edc26b
Show file tree
Hide file tree
Showing 202 changed files with 1,377 additions and 1,313 deletions.
@@ -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>
@@ -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
@@ -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
@@ -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
@@ -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>
@@ -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 );

};
@@ -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;

@@ -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
@@ -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
@@ -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.
@@ -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 );
};
@@ -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);
@@ -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,
@@ -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,
@@ -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.
@@ -48,7 +48,7 @@ def __init__(self, table, parent=None):
for f in self.layer.getFeatures():
a = f.attributes()
# add the geometry type
if f.geometry():
if f.hasGeometry():
a.append(QgsWKBTypes.displayString(Qgis.fromOldWkbType(f.geometry().wkbType())))
else:
a.append('None')
@@ -97,7 +97,7 @@ def __init__(self, db, sql, parent=None):
for f in p.getFeatures():
a = f.attributes()
if has_geometry:
if f.geometry():
if f.hasGeometry():
a += [f.geometry().exportToWkt()]
else:
a += [None]
@@ -56,8 +56,8 @@ def buffering(progress, writer, distance, field, useField, layer, dissolve,
else:
value = distance

inGeom = QgsGeometry(inFeat.geometry())
if inGeom.isGeosEmpty():
inGeom = inFeat.geometry()
if inGeom.isEmpty() or inGeom.isGeosEmpty():
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty geometry. Skipping...'.format(inFeat.id()))
continue
if not inGeom.isGeosValid():
@@ -84,8 +84,8 @@ def buffering(progress, writer, distance, field, useField, layer, dissolve,
value = attrs[field]
else:
value = distance
inGeom = QgsGeometry(inFeat.geometry())
if inGeom.isGeosEmpty():
inGeom = inFeat.geometry()
if inGeom.isEmpty() or inGeom.isGeosEmpty():
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty geometry. Skipping...'.format(inFeat.id()))
continue
if not inGeom.isGeosValid():