Skip to content

Commit 47d09fe

Browse files
committedJan 13, 2014
Fixed point displacement renderer (and made it faster)
1 parent 78841d9 commit 47d09fe

File tree

2 files changed

+78
-169
lines changed

2 files changed

+78
-169
lines changed
 

‎src/core/symbology-ng/qgspointdisplacementrenderer.cpp

Lines changed: 65 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ QgsFeatureRendererV2* QgsPointDisplacementRenderer::clone()
5353
{
5454
QgsPointDisplacementRenderer* r = new QgsPointDisplacementRenderer( mLabelAttributeName );
5555
r->setEmbeddedRenderer( mRenderer->clone() );
56-
r->setDisplacementGroups( mDisplacementGroups );
5756
r->setCircleWidth( mCircleWidth );
5857
r->setCircleColor( mCircleColor );
5958
r->setLabelFont( mLabelFont );
@@ -77,6 +76,13 @@ void QgsPointDisplacementRenderer::toSld( QDomDocument& doc, QDomElement &elemen
7776
bool QgsPointDisplacementRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
7877
{
7978
Q_UNUSED( drawVertexMarker );
79+
Q_UNUSED( context );
80+
Q_UNUSED( layer );
81+
82+
//check, if there is already a point at that position
83+
if ( !feature.geometry() )
84+
return false;
85+
8086
//point position in screen coords
8187
QgsGeometry* geom = feature.geometry();
8288
QGis::WkbType geomType = geom->wkbType();
@@ -85,58 +91,55 @@ bool QgsPointDisplacementRenderer::renderFeature( QgsFeature& feature, QgsRender
8591
//can only render point type
8692
return false;
8793
}
88-
QPointF pt;
89-
_getPoint( pt, context, geom->asWkb() );
9094

95+
if ( selected )
96+
mSelectedFeatures.insert( feature.id() );
97+
98+
QList<QgsFeatureId> intersectList = mSpatialIndex->intersects( searchRect( feature.geometry()->asPoint() ) );
99+
if ( intersectList.empty() )
100+
{
101+
mSpatialIndex->insertFeature( feature );
102+
// create new group
103+
DisplacementGroup newGroup;
104+
newGroup.insert( feature.id(), feature );
105+
mDisplacementGroups.push_back( newGroup );
106+
// add to group index
107+
mGroupIndex.insert( feature.id(), mDisplacementGroups.count() - 1 );
108+
return true;
109+
}
110+
111+
//go through all the displacement group maps and search an entry where the id equals the result of the spatial search
112+
QgsFeatureId existingEntry = intersectList.at( 0 );
113+
114+
int groupIdx = mGroupIndex[ existingEntry ];
115+
DisplacementGroup& group = mDisplacementGroups[groupIdx];
116+
117+
// add to a group
118+
group.insert( feature.id(), feature );
119+
// add to group index
120+
mGroupIndex.insert( feature.id(), groupIdx );
121+
return true;
122+
}
123+
124+
void QgsPointDisplacementRenderer::drawGroup( const DisplacementGroup& group, QgsRenderContext& context )
125+
{
126+
const QgsFeature& feature = group.begin().value();
127+
bool selected = mSelectedFeatures.contains( feature.id() ); // maybe we should highlight individual features instead of the whole group?
128+
129+
QPointF pt;
130+
_getPoint( pt, context, feature.geometry()->asWkb() );
91131

92132
//get list of labels and symbols
93133
QStringList labelAttributeList;
94134
QList<QgsMarkerSymbolV2*> symbolList;
95135

96-
if ( mDisplacementIds.contains( feature.id() ) )
136+
for ( DisplacementGroup::const_iterator attIt = group.constBegin(); attIt != group.constEnd(); ++attIt )
97137
{
98-
//create the symbol for the whole display group if the id is the first entry in a display group
99-
QList<QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin();
100-
for ( ; it != mDisplacementGroups.end(); ++it )
101-
{
102-
//create the symbol for the whole display group if the id is the first entry in a display group
103-
if ( feature.id() == it->begin().key() )
104-
{
105-
QMap<QgsFeatureId, QgsFeature>::iterator attIt = it->begin();
106-
for ( ; attIt != it->end(); ++attIt )
107-
{
108-
if ( mDrawLabels )
109-
{
110-
labelAttributeList << getLabel( attIt.value() );
111-
}
112-
else
113-
{
114-
labelAttributeList << QString();
115-
}
116-
symbolList << dynamic_cast<QgsMarkerSymbolV2*>( firstSymbolForFeature( mRenderer, attIt.value() ) );
117-
}
118-
}
119-
}
120-
}
121-
else //only one feature
122-
{
123-
symbolList << dynamic_cast<QgsMarkerSymbolV2*>( firstSymbolForFeature( mRenderer, feature ) );
124-
if ( mDrawLabels )
125-
{
126-
labelAttributeList << getLabel( feature );
127-
}
128-
else
129-
{
130-
labelAttributeList << QString();
131-
}
132-
}
133-
134-
if ( symbolList.isEmpty() && labelAttributeList.isEmpty() )
135-
{
136-
return true; //display all point symbols for one posi
138+
labelAttributeList << ( mDrawLabels ? getLabel( attIt.value() ) : QString() );
139+
QgsFeature& f = const_cast<QgsFeature&>( attIt.value() ); // other parts of API use non-const ref to QgsFeature :-/
140+
symbolList << dynamic_cast<QgsMarkerSymbolV2*>( firstSymbolForFeature( mRenderer, f ) );
137141
}
138142

139-
140143
//draw symbol
141144
double diagonal = 0;
142145
double currentWidthFactor; //scale symbol size to map unit and output resolution
@@ -172,7 +175,7 @@ bool QgsPointDisplacementRenderer::renderFeature( QgsFeature& feature, QgsRender
172175
{
173176
if ( mCenterSymbol )
174177
{
175-
mCenterSymbol->renderPoint( pt, &feature, context, layer, selected );
178+
mCenterSymbol->renderPoint( pt, &feature, context, -1, selected );
176179
}
177180
else
178181
{
@@ -184,7 +187,6 @@ bool QgsPointDisplacementRenderer::renderFeature( QgsFeature& feature, QgsRender
184187
drawSymbols( feature, context, symbolList, symbolPositions, selected );
185188
//and also the labels
186189
drawLabels( pt, symbolContext, labelPositions, labelAttributeList );
187-
return true;
188190
}
189191

190192
void QgsPointDisplacementRenderer::setEmbeddedRenderer( QgsFeatureRendererV2* r )
@@ -203,9 +205,10 @@ void QgsPointDisplacementRenderer::startRender( QgsRenderContext& context, const
203205
{
204206
mRenderer->startRender( context, fields );
205207

206-
//create groups with features that have the same position
207-
createDisplacementGroups( 0, context.extent() );
208-
printInfoDisplacementGroups(); //just for debugging
208+
mDisplacementGroups.clear();
209+
mGroupIndex.clear();
210+
mSpatialIndex = new QgsSpatialIndex;
211+
mSelectedFeatures.clear();
209212

210213
if ( mLabelAttributeName.isEmpty() )
211214
{
@@ -234,6 +237,18 @@ void QgsPointDisplacementRenderer::startRender( QgsRenderContext& context, const
234237
void QgsPointDisplacementRenderer::stopRender( QgsRenderContext& context )
235238
{
236239
QgsDebugMsg( "QgsPointDisplacementRenderer::stopRender" );
240+
241+
//printInfoDisplacementGroups(); //just for debugging
242+
243+
for ( QList<DisplacementGroup>::const_iterator it = mDisplacementGroups.begin(); it != mDisplacementGroups.end(); ++it )
244+
drawGroup( *it, context );
245+
246+
mDisplacementGroups.clear();
247+
mGroupIndex.clear();
248+
delete mSpatialIndex;
249+
mSpatialIndex = 0;
250+
mSelectedFeatures.clear();
251+
237252
mRenderer->stopRender( context );
238253
if ( mCenterSymbol )
239254
{
@@ -339,95 +354,6 @@ QgsLegendSymbolList QgsPointDisplacementRenderer::legendSymbolItems( double scal
339354
return QgsLegendSymbolList();
340355
}
341356

342-
void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent )
343-
{
344-
Q_ASSERT( 0 && "Point Displacement Renderer is currently not working - it is now illegal to interact with QgsVectorLayer"
345-
" in any way. The renderer should build groups in the renderFeature() calls and then do all rendering in stopRender()");
346-
347-
if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) )
348-
{
349-
return;
350-
}
351-
352-
mDisplacementGroups.clear();
353-
mDisplacementIds.clear();
354-
355-
//use a spatial index to check if there is already a point at a position
356-
QgsSpatialIndex spatialIndex;
357-
358-
//attributes
359-
QgsAttributeList attList;
360-
QList<QString> attributeStrings = usedAttributes();
361-
QList<QString>::const_iterator attStringIt = attributeStrings.constBegin();
362-
for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt )
363-
{
364-
attList.push_back( vlayer->fieldNameIndex( *attStringIt ) );
365-
}
366-
367-
QgsFeature f;
368-
QList<QgsFeatureId> intersectList;
369-
370-
//Because the new vector api does not allow querying features by id within a nextFeature loop, default constructed QgsFeature() is
371-
//inserted first and the real features are created in a second loop
372-
373-
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( viewExtent ).setSubsetOfAttributes( attList ) );
374-
while ( fit.nextFeature( f ) )
375-
{
376-
intersectList.clear();
377-
378-
//check, if there is already a point at that position
379-
if ( f.geometry() )
380-
{
381-
intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) );
382-
if ( intersectList.empty() )
383-
{
384-
spatialIndex.insertFeature( f );
385-
}
386-
else
387-
{
388-
//go through all the displacement group maps and search an entry where the id equals the result of the spatial search
389-
QgsFeatureId existingEntry = intersectList.at( 0 );
390-
bool found = false;
391-
QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin();
392-
for ( ; it != mDisplacementGroups.end(); ++it )
393-
{
394-
if ( it->size() > 0 && it->contains( existingEntry ) )
395-
{
396-
found = true;
397-
QgsFeature feature;
398-
it->insert( f.id(), QgsFeature() );
399-
mDisplacementIds.insert( f.id() );
400-
break;
401-
}
402-
}
403-
404-
if ( !found )//insert the already existing feature and the new one into a map
405-
{
406-
QMap<QgsFeatureId, QgsFeature> newMap;
407-
newMap.insert( existingEntry, QgsFeature() );
408-
mDisplacementIds.insert( existingEntry );
409-
newMap.insert( f.id(), QgsFeature() );
410-
mDisplacementIds.insert( f.id() );
411-
mDisplacementGroups.push_back( newMap );
412-
}
413-
}
414-
}
415-
}
416-
417-
//insert the real features into mDisplacementGroups
418-
QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin();
419-
for ( ; it != mDisplacementGroups.end(); ++it )
420-
{
421-
QMap<QgsFeatureId, QgsFeature>::iterator mapIt = it->begin();
422-
for ( ; mapIt != it->end(); ++mapIt )
423-
{
424-
QgsFeature fet;
425-
vlayer->getFeatures( QgsFeatureRequest().setFilterFid( mapIt.key() ) ).nextFeature( fet );
426-
mapIt.value() = fet;
427-
}
428-
}
429-
430-
}
431357

432358
QgsRectangle QgsPointDisplacementRenderer::searchRect( const QgsPoint& p ) const
433359
{
@@ -447,28 +373,6 @@ void QgsPointDisplacementRenderer::printInfoDisplacementGroups()
447373
QgsDebugMsg( FID_TO_STRING( it.key() ) );
448374
}
449375
}
450-
QgsDebugMsg( "********all displacement ids*********" );
451-
QSet<QgsFeatureId>::const_iterator iIt = mDisplacementIds.constBegin();
452-
for ( ; iIt != mDisplacementIds.constEnd(); ++iIt )
453-
{
454-
QgsDebugMsg( FID_TO_STRING( *iIt ) );
455-
}
456-
}
457-
458-
void QgsPointDisplacementRenderer::setDisplacementGroups( const QList< QMap<QgsFeatureId, QgsFeature> >& list )
459-
{
460-
mDisplacementGroups = list;
461-
mDisplacementIds.clear();
462-
463-
QList<QMap<QgsFeatureId, QgsFeature> >::const_iterator list_it = mDisplacementGroups.constBegin();
464-
for ( ; list_it != mDisplacementGroups.constEnd(); ++list_it )
465-
{
466-
QMap<QgsFeatureId, QgsFeature>::const_iterator map_it = list_it->constBegin();
467-
for ( ; map_it != list_it->constEnd(); ++map_it )
468-
{
469-
mDisplacementIds.insert( map_it.key() );
470-
}
471-
}
472376
}
473377

474378
QString QgsPointDisplacementRenderer::getLabel( const QgsFeature& f )
@@ -537,7 +441,7 @@ void QgsPointDisplacementRenderer::drawCircle( double radiusPainterUnits, QgsSym
537441
p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
538442
}
539443

540-
void QgsPointDisplacementRenderer::drawSymbols( QgsFeature& f, QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions, bool selected )
444+
void QgsPointDisplacementRenderer::drawSymbols( const QgsFeature& f, QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions, bool selected )
541445
{
542446
QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
543447
QList<QgsMarkerSymbolV2*>::const_iterator symbolIt = symbolList.constBegin();

‎src/core/symbology-ng/qgspointdisplacementrenderer.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
#include <QFont>
2626
#include <QSet>
2727

28-
class QgsVectorLayer;
28+
class QgsSpatialIndex;
2929

3030
/**A renderer that automatically displaces points with the same position*/
3131
class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
@@ -67,7 +67,8 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
6767
QgsFeatureRendererV2* embeddedRenderer() { return mRenderer;}
6868

6969
//! not available in python bindings
70-
void setDisplacementGroups( const QList<QMap<QgsFeatureId, QgsFeature> >& list );
70+
//! @deprecated since 2.1
71+
Q_DECL_DEPRECATED void setDisplacementGroups( const QList<QMap<QgsFeatureId, QgsFeature> >& list ) { Q_UNUSED( list ); }
7172

7273
void setLabelFont( const QFont& f ) { mLabelFont = f; }
7374
QFont labelFont() const { return mLabelFont;}
@@ -125,13 +126,16 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
125126
/**Maximum scale denominator for label display. Negative number means no scale limitation*/
126127
double mMaxLabelScaleDenominator;
127128

129+
typedef QMap<QgsFeatureId, QgsFeature> DisplacementGroup;
128130
/**Groups of features that have the same position*/
129-
QList<QMap<QgsFeatureId, QgsFeature> > mDisplacementGroups;
130-
/**Set that contains all the ids the display groups (for quicker lookup)*/
131-
QSet<QgsFeatureId> mDisplacementIds;
131+
QList<DisplacementGroup> mDisplacementGroups;
132+
/**Mapping from feature ID to its group index*/
133+
QMap<QgsFeatureId, int> mGroupIndex;
134+
/**Spatial index for fast lookup of close points*/
135+
QgsSpatialIndex* mSpatialIndex;
136+
/** keeps trask which features are selected */
137+
QSet<QgsFeatureId> mSelectedFeatures;
132138

133-
/**Create the displacement groups efficiently using a spatial index*/
134-
void createDisplacementGroups( QgsVectorLayer *vlayer, const QgsRectangle& viewExtent );
135139
/**Creates a search rectangle with mTolerance*/
136140
QgsRectangle searchRect( const QgsPoint& p ) const;
137141
/**This is a debugging function to check the entries in the displacement groups*/
@@ -146,8 +150,9 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
146150

147151
//helper functions
148152
void calculateSymbolAndLabelPositions( const QPointF& centerPoint, int nPosition, double radius, double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts ) const;
153+
void drawGroup( const DisplacementGroup& group, QgsRenderContext& context );
149154
void drawCircle( double radiusPainterUnits, QgsSymbolV2RenderContext& context, const QPointF& centerPoint, int nSymbols );
150-
void drawSymbols( QgsFeature& f, QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions, bool selected = false );
155+
void drawSymbols( const QgsFeature& f, QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions, bool selected = false );
151156
void drawLabels( const QPointF& centerPoint, QgsSymbolV2RenderContext& context, const QList<QPointF>& labelShifts, const QStringList& labelList );
152157
/**Returns first symbol for feature or 0 if none*/
153158
QgsSymbolV2* firstSymbolForFeature( QgsFeatureRendererV2* r, QgsFeature& f );

0 commit comments

Comments
 (0)
Please sign in to comment.