QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgsinvertedpolygonrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsinvertedpolygonrenderer.cpp
3  ---------------------
4  begin : April 2014
5  copyright : (C) 2014 Hugo Mercier / Oslandia
6  email : hugo dot mercier at oslandia dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgssymbol.h"
19 #include "qgssymbollayerutils.h"
20 
21 #include "qgslogger.h"
22 #include "qgsfeature.h"
23 #include "qgsvectorlayer.h"
24 #include "qgssymbollayer.h"
25 #include "qgsogcutils.h"
26 #include "qgspainteffect.h"
27 #include "qgspainteffectregistry.h"
28 
29 #include <QDomDocument>
30 #include <QDomElement>
31 
33  : QgsFeatureRenderer( QStringLiteral( "invertedPolygonRenderer" ) )
34 {
35  if ( subRenderer )
36  {
37  setEmbeddedRenderer( subRenderer );
38  }
39  else
40  {
42  }
43 }
44 
46 {
47  if ( subRenderer )
48  {
49  mSubRenderer.reset( subRenderer );
50  }
51  else
52  {
53  mSubRenderer.reset( nullptr );
54  }
55 }
56 
58 {
59  return mSubRenderer.get();
60 }
61 
63 {
64  if ( !mSubRenderer )
65  return;
66 
67  mSubRenderer->setLegendSymbolItem( key, symbol );
68 }
69 
71 {
72  if ( !mSubRenderer )
73  return false;
74 
75  return mSubRenderer->legendSymbolItemsCheckable();
76 }
77 
79 {
80  if ( !mSubRenderer )
81  return false;
82 
83  return mSubRenderer->legendSymbolItemChecked( key );
84 }
85 
86 void QgsInvertedPolygonRenderer::checkLegendSymbolItem( const QString &key, bool state )
87 {
88  if ( !mSubRenderer )
89  return;
90 
91  mSubRenderer->checkLegendSymbolItem( key, state );
92 }
93 
95 {
96  QgsFeatureRenderer::startRender( context, fields );
97 
98  if ( !mSubRenderer )
99  {
100  return;
101  }
102 
103  // first call start render on the sub renderer
104  mSubRenderer->startRender( context, fields );
105 
106  mFeaturesCategories.clear();
107  mSymbolCategories.clear();
108  mFeatureDecorations.clear();
109  mFields = fields;
110 
111  // We compute coordinates of the extent which will serve as exterior ring
112  // for the final polygon
113  // It must be computed in the destination CRS if reprojection is enabled.
114 
115  if ( !context.painter() )
116  {
117  return;
118  }
119 
120  // convert viewport to dest CRS
121  // add some space to hide borders and tend to infinity
122  const double buffer = std::max( context.mapExtent().width(), context.mapExtent().height() ) * 0.1;
123  const QRectF outer = context.mapExtent().buffered( buffer ).toRectF();
124  QgsPolylineXY exteriorRing;
125  exteriorRing.reserve( 5 );
126  exteriorRing << outer.topLeft();
127  exteriorRing << outer.topRight();
128  exteriorRing << outer.bottomRight();
129  exteriorRing << outer.bottomLeft();
130  exteriorRing << outer.topLeft();
131 
132  // copy the rendering context
133  mContext = context;
134 
135  // If reprojection is enabled, we must reproject during renderFeature
136  // and act as if there is no reprojection
137  // If we don't do that, there is no need to have a simple rectangular extent
138  // that covers the whole screen
139  // (a rectangle in the destCRS cannot be expressed as valid coordinates in the sourceCRS in general)
140  if ( context.coordinateTransform().isValid() )
141  {
142  // disable projection
144  // recompute extent so that polygon clipping is correct
145  mContext.setExtent( context.mapExtent() );
146  // do we have to recompute the MapToPixel ?
147  }
148 
149  mExtentPolygon.clear();
150  mExtentPolygon.append( exteriorRing );
151 }
152 
153 bool QgsInvertedPolygonRenderer::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker )
154 {
155  if ( !context.painter() )
156  {
157  return false;
158  }
159 
160  // store this feature as a feature to render with decoration if needed
161  if ( selected || drawVertexMarker )
162  {
163  mFeatureDecorations.append( FeatureDecoration( feature, selected, drawVertexMarker, layer ) );
164  }
165 
166  // Features are grouped by category of symbols (returned by symbol(s)ForFeature)
167  // This way, users can have multiple inverted polygon fills for a layer,
168  // for instance, with rule based renderer and different symbols
169  // that have transparency.
170  //
171  // In order to assign a unique category to a set of symbols
172  // during each rendering session (between startRender() and stopRender()),
173  // we build an unique id as a QByteArray that is the concatenation
174  // of each symbol's memory address.
175  // The only assumption made here is that symbol(s)ForFeature will
176  // always return the same address for the same symbol(s) shared amongst
177  // different features.
178  // This QByteArray can then be used as a key for a QMap where the list of
179  // features for this category is stored
180  QByteArray catId;
182  {
183  QgsSymbolList syms( mSubRenderer->symbolsForFeature( feature, context ) );
184  const auto constSyms = syms;
185  for ( QgsSymbol *sym : constSyms )
186  {
187  // append the memory address
188  catId.append( reinterpret_cast<const char *>( &sym ), sizeof( sym ) );
189  }
190  }
191  else
192  {
193  QgsSymbol *sym = mSubRenderer->symbolForFeature( feature, context );
194  if ( sym )
195  {
196  catId.append( reinterpret_cast<const char *>( &sym ), sizeof( sym ) );
197  }
198  }
199 
200  if ( catId.isEmpty() )
201  {
202  return false;
203  }
204 
205  if ( ! mSymbolCategories.contains( catId ) )
206  {
207  CombinedFeature cFeat;
208  // store the first feature
209  cFeat.feature = feature;
210  mSymbolCategories.insert( catId, mSymbolCategories.count() );
211  mFeaturesCategories.append( cFeat );
212  }
213 
214  // update the geometry
215  CombinedFeature &cFeat = mFeaturesCategories[ mSymbolCategories[catId] ];
216  if ( !feature.hasGeometry() )
217  {
218  return false;
219  }
220  QgsGeometry geom = feature.geometry();
221 
223  if ( xform.isValid() )
224  {
225  geom.transform( xform );
226  }
227 
228  if ( mPreprocessingEnabled )
229  {
230  // fix the polygon if it is not valid
231  if ( ! geom.isGeosValid() )
232  {
233  geom = geom.buffer( 0, 0 );
234  }
235  }
236 
237  if ( geom.isNull() )
238  return false; // do not let invalid geometries sneak in!
239 
240  // add the geometry to the list of geometries for this feature
241  cFeat.geometries.append( geom );
242 
243  return true;
244 }
245 
247 {
249  if ( context.renderingStopped() )
250  {
251  mSubRenderer->stopRender( mContext );
252  return;
253  }
254 
255  if ( !mSubRenderer )
256  {
257  return;
258  }
259  if ( !context.painter() )
260  {
261  return;
262  }
263 
264  QgsMultiPolygonXY finalMulti; //avoid expensive allocation for list for every feature
265  QgsPolygonXY newPoly;
266 
267  const auto constMFeaturesCategories = mFeaturesCategories;
268  for ( const CombinedFeature &cit : constMFeaturesCategories )
269  {
270  finalMulti.resize( 0 ); //preserve capacity - don't use clear!
271  QgsFeature feat = cit.feature; // just a copy, so that we do not accumulate geometries again
272  if ( mPreprocessingEnabled )
273  {
274  // compute the unary union on the polygons
275  QgsGeometry unioned( QgsGeometry::unaryUnion( cit.geometries ) );
276  // compute the difference with the extent
277  QgsGeometry rect = QgsGeometry::fromPolygonXY( mExtentPolygon );
278  QgsGeometry final = rect.difference( unioned );
279  feat.setGeometry( final );
280  }
281  else
282  {
283  // No preprocessing involved.
284  // We build here a "reversed" geometry of all the polygons
285  //
286  // The final geometry is a multipolygon F, with :
287  // * the first polygon of F having the current extent as its exterior ring
288  // * each polygon's exterior ring is added as interior ring of the first polygon of F
289  // * each polygon's interior ring is added as new polygons in F
290  //
291  // No validity check is done, on purpose, it will be very slow and painting
292  // operations do not need geometries to be valid
293 
294  finalMulti.append( mExtentPolygon );
295  for ( const QgsGeometry &geom : qgis::as_const( cit.geometries ) )
296  {
297  QgsMultiPolygonXY multi;
298  QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom.constGet()->wkbType() );
299 
300  if ( ( type == QgsWkbTypes::Polygon ) || ( type == QgsWkbTypes::CurvePolygon ) )
301  {
302  multi.append( geom.asPolygon() );
303  }
304  else if ( ( type == QgsWkbTypes::MultiPolygon ) || ( type == QgsWkbTypes::MultiSurface ) )
305  {
306  multi = geom.asMultiPolygon();
307  }
308 
309  for ( int i = 0; i < multi.size(); i++ )
310  {
311  const QgsPolylineXY &exterior = multi[i][0];
312  // add the exterior ring as interior ring to the first polygon
313  // make sure it satisfies at least very basic requirements of GEOS
314  // (otherwise the creation of GEOS geometry will fail)
315  if ( exterior.count() < 4 || exterior[0] != exterior[exterior.count() - 1] )
316  continue;
317  finalMulti[0].append( exterior );
318 
319  // add interior rings as new polygons
320  for ( int j = 1; j < multi[i].size(); j++ )
321  {
322  newPoly.resize( 0 ); //preserve capacity - don't use clear!
323  newPoly.append( multi[i][j] );
324  finalMulti.append( newPoly );
325  }
326  }
327  }
328  feat.setGeometry( QgsGeometry::fromMultiPolygonXY( finalMulti ) );
329  }
330  if ( feat.hasGeometry() )
331  {
332  mContext.expressionContext().setFeature( feat );
333  mSubRenderer->renderFeature( feat, mContext );
334  }
335  }
336 
337  // when no features are visible, we still have to draw the exterior rectangle
338  // warning: when sub renderers have more than one possible symbols,
339  // there is no way to choose a correct one, because there is no attribute here
340  // in that case, nothing will be rendered
341  if ( mFeaturesCategories.isEmpty() )
342  {
343  // empty feature with default attributes
344  QgsFeature feat( mFields );
345  feat.setGeometry( QgsGeometry::fromPolygonXY( mExtentPolygon ) );
346  mSubRenderer->renderFeature( feat, mContext );
347  }
348 
349  // draw feature decorations
350  const auto constMFeatureDecorations = mFeatureDecorations;
351  for ( FeatureDecoration deco : constMFeatureDecorations )
352  {
353  mSubRenderer->renderFeature( deco.feature, mContext, deco.layer, deco.selected, deco.drawMarkers );
354  }
355 
356  mSubRenderer->stopRender( mContext );
357 }
358 
360 {
361  if ( !mSubRenderer )
362  {
363  return QStringLiteral( "INVERTED: NULL" );
364  }
365  return "INVERTED [" + mSubRenderer->dump() + ']';
366 }
367 
369 {
370  QgsInvertedPolygonRenderer *newRenderer = nullptr;
371  if ( !mSubRenderer )
372  {
373  newRenderer = new QgsInvertedPolygonRenderer( nullptr );
374  }
375  else
376  {
377  newRenderer = new QgsInvertedPolygonRenderer( mSubRenderer->clone() );
378  }
380  copyRendererData( newRenderer );
381  return newRenderer;
382 }
383 
385 {
387  //look for an embedded renderer <renderer-v2>
388  QDomElement embeddedRendererElem = element.firstChildElement( QStringLiteral( "renderer-v2" ) );
389  if ( !embeddedRendererElem.isNull() )
390  {
391  QgsFeatureRenderer *renderer = QgsFeatureRenderer::load( embeddedRendererElem, context );
392  r->setEmbeddedRenderer( renderer );
393  }
394  r->setPreprocessingEnabled( element.attribute( QStringLiteral( "preprocessing" ), QStringLiteral( "0" ) ).toInt() == 1 );
395  return r;
396 }
397 
398 QDomElement QgsInvertedPolygonRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
399 {
400  // clazy:skip
401 
402  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
403  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "invertedPolygonRenderer" ) );
404  rendererElem.setAttribute( QStringLiteral( "preprocessing" ), preprocessingEnabled() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
405  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
406 
407  if ( mSubRenderer )
408  {
409  QDomElement embeddedRendererElem = mSubRenderer->save( doc, context );
410  rendererElem.appendChild( embeddedRendererElem );
411  }
412 
414  mPaintEffect->saveProperties( doc, rendererElem );
415 
416  if ( !mOrderBy.isEmpty() )
417  {
418  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
419  mOrderBy.save( orderBy );
420  rendererElem.appendChild( orderBy );
421  }
422  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
423 
424  return rendererElem;
425 }
426 
428 {
429  if ( !mSubRenderer )
430  {
431  return nullptr;
432  }
433  return mSubRenderer->symbolForFeature( feature, context );
434 }
435 
437 {
438  if ( !mSubRenderer )
439  return nullptr;
440  return mSubRenderer->originalSymbolForFeature( feature, context );
441 }
442 
444 {
445  if ( !mSubRenderer )
446  {
447  return QgsSymbolList();
448  }
449  return mSubRenderer->symbolsForFeature( feature, context );
450 }
451 
453 {
454  if ( !mSubRenderer )
455  return QgsSymbolList();
456  return mSubRenderer->originalSymbolsForFeature( feature, context );
457 }
458 
460 {
461  if ( !mSubRenderer )
462  {
463  return QgsSymbolList();
464  }
465  return mSubRenderer->symbols( context );
466 }
467 
468 QgsFeatureRenderer::Capabilities QgsInvertedPolygonRenderer::capabilities()
469 {
470  if ( !mSubRenderer )
471  {
472  return nullptr;
473  }
474  return mSubRenderer->capabilities();
475 }
476 
478 {
479  if ( !mSubRenderer )
480  {
481  return QSet<QString>();
482  }
483  return mSubRenderer->usedAttributes( context );
484 }
485 
487 {
488  return mSubRenderer ? mSubRenderer->filterNeedsGeometry() : false;
489 }
490 
492 {
493  if ( !mSubRenderer )
494  {
495  return QgsLegendSymbolList();
496  }
497  return mSubRenderer->legendSymbolItems();
498 }
499 
501 {
502  if ( !mSubRenderer )
503  {
504  return false;
505  }
506  return mSubRenderer->willRenderFeature( feature, context );
507 }
508 
510 {
511  if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
512  {
513  return dynamic_cast<QgsInvertedPolygonRenderer *>( renderer->clone() );
514  }
515 
516  if ( renderer->type() == QLatin1String( "singleSymbol" ) ||
517  renderer->type() == QLatin1String( "categorizedSymbol" ) ||
518  renderer->type() == QLatin1String( "graduatedSymbol" ) ||
519  renderer->type() == QLatin1String( "RuleRenderer" ) )
520  {
521  return new QgsInvertedPolygonRenderer( renderer->clone() );
522  }
523  return nullptr;
524 }
525 
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygon.
The class is used as a container of context for various read/write operations on other objects...
QgsFeatureRenderer::Capabilities capabilities() override
Proxy that will call this method on the embedded renderer.
May use more than one symbol to render a feature: symbolsForFeature() will return them...
Definition: qgsrenderer.h:243
bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
QList< QgsLegendSymbolItem > QgsLegendSymbolList
QgsFeatureRequest::OrderBy mOrderBy
Definition: qgsrenderer.h:521
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
void setCoordinateTransform(const QgsCoordinateTransform &t)
Sets the current coordinate transform for the context.
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a renderer out of an XML, for loading.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Proxy that will call this method on the embedded renderer.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsFeatureRequest::OrderBy orderBy() const
Gets the order in which features shall be processed by this renderer.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:73
QgsInvertedPolygonRenderer * clone() const override
Create a deep copy of this renderer.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:49
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Proxy that will call this method on the embedded renderer.
QgsPaintEffect * mPaintEffect
Definition: qgsrenderer.h:505
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QString dump() const override
Returns debug information about this renderer.
static QgsInvertedPolygonRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
Creates a QgsInvertedPolygonRenderer by a conversion from an existing renderer.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
void setExtent(const QgsRectangle &extent)
When rendering a map layer, calling this method sets the "clipping" extent for the layer (in the laye...
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition: qgsgeometry.h:90
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
Definition: qgsrectangle.h:304
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:43
QString type() const
Definition: qgsrenderer.h:129
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Proxy that will call this method on the embedded renderer.
bool isGeosValid(QgsGeometry::ValidityFlags flags=nullptr) const
Checks validity of the geometry using GEOS.
void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
store renderer info to XML element
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
static QgsFeatureRenderer * load(QDomElement &symbologyElem, const QgsReadWriteContext &context)
create a renderer from XML element
QgsSymbolList originalSymbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Proxy that will call this method on the embedded renderer.
QgsLegendSymbolList legendSymbolItems() const override
Proxy that will call this method on the embedded renderer.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
QgsSymbol * originalSymbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Proxy that will call this method on the embedded renderer.
QgsSymbolList symbols(QgsRenderContext &context) const override
Proxy that will call this method on the embedded renderer.
QgsExpressionContext & expressionContext()
Gets the expression context.
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygon.
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:49
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QRectF toRectF() const
Returns a QRectF with same coordinates as the rectangle.
Definition: qgsrectangle.h:457
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:93
bool renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false) override SIP_THROW(QgsCsException)
Renders a given feature.
QgsInvertedPolygonRenderer(QgsFeatureRenderer *embeddedRenderer=nullptr)
Constructor.
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
Class for doing transforms between two map coordinate systems.
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:49
bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
QgsGeometry geometry
Definition: qgsfeature.h:67
void stopRender(QgsRenderContext &context) override
The actual rendering will take place here.
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
static QgsFeatureRenderer * defaultRenderer(QgsWkbTypes::GeometryType geomType)
Returns a new renderer - used by default in vector layers.
Definition: qgsrenderer.cpp:76
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:430
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Proxy that will call this method on the embedded renderer.
void setEmbeddedRenderer(QgsFeatureRenderer *subRenderer) override
Sets an embedded renderer (subrenderer) for this feature renderer.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
QgsGeometry difference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other...