00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "qgspointdisplacementrenderer.h"
00019 #include "qgsgeometry.h"
00020 #include "qgslogger.h"
00021 #include "qgsspatialindex.h"
00022 #include "qgssymbolv2.h"
00023 #include "qgssymbollayerv2utils.h"
00024 #include "qgsvectorlayer.h"
00025
00026 #include <QDomElement>
00027 #include <QPainter>
00028
00029 #include <cmath>
00030
00031 QgsPointDisplacementRenderer::QgsPointDisplacementRenderer( const QString& labelAttributeName )
00032 : QgsFeatureRendererV2( "pointDisplacement" )
00033 , mLabelAttributeName( labelAttributeName )
00034 , mLabelIndex( -1 )
00035 , mTolerance( 0.00001 )
00036 , mCircleWidth( 0.4 )
00037 , mCircleColor( QColor( 125, 125, 125 ) )
00038 , mCircleRadiusAddition( 0 )
00039 , mMaxLabelScaleDenominator( -1 )
00040 {
00041 mRenderer = QgsFeatureRendererV2::defaultRenderer( QGis::Point );
00042 mCenterSymbol = new QgsMarkerSymbolV2();
00043 mDrawLabels = true;
00044 }
00045
00046 QgsPointDisplacementRenderer::~QgsPointDisplacementRenderer()
00047 {
00048 delete mCenterSymbol;
00049 delete mRenderer;
00050 }
00051
00052 QgsFeatureRendererV2* QgsPointDisplacementRenderer::clone()
00053 {
00054 QgsPointDisplacementRenderer* r = new QgsPointDisplacementRenderer( mLabelAttributeName );
00055 r->setEmbeddedRenderer( mRenderer->clone() );
00056 r->setDisplacementGroups( mDisplacementGroups );
00057 r->setCircleWidth( mCircleWidth );
00058 r->setCircleColor( mCircleColor );
00059 r->setLabelFont( mLabelFont );
00060 r->setLabelColor( mLabelColor );
00061 r->setCircleRadiusAddition( mCircleRadiusAddition );
00062 r->setMaxLabelScaleDenominator( mMaxLabelScaleDenominator );
00063 r->setTolerance( mTolerance );
00064 if ( mCenterSymbol )
00065 {
00066 r->setCenterSymbol( dynamic_cast<QgsMarkerSymbolV2*>( mCenterSymbol->clone() ) );
00067 }
00068 return r;
00069 }
00070
00071 void QgsPointDisplacementRenderer::toSld( QDomDocument& doc, QDomElement &element ) const
00072 {
00073 mRenderer->toSld( doc, element );
00074 }
00075
00076
00077 bool QgsPointDisplacementRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
00078 {
00079 Q_UNUSED( drawVertexMarker );
00080
00081 QgsGeometry* geom = feature.geometry();
00082 QGis::WkbType geomType = geom->wkbType();
00083 if ( geomType != QGis::WKBPoint && geomType != QGis::WKBPoint25D )
00084 {
00085
00086 return false;
00087 }
00088 QPointF pt;
00089 _getPoint( pt, context, geom->asWkb() );
00090
00091
00092
00093 QStringList labelAttributeList;
00094 QList<QgsMarkerSymbolV2*> symbolList;
00095
00096 if ( mDisplacementIds.contains( feature.id() ) )
00097 {
00098
00099 QList<QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin();
00100 for ( ; it != mDisplacementGroups.end(); ++it )
00101 {
00102
00103 if ( feature.id() == it->begin().key() )
00104 {
00105 QMap<QgsFeatureId, QgsFeature>::iterator attIt = it->begin();
00106 for ( ; attIt != it->end(); ++attIt )
00107 {
00108 if ( mDrawLabels )
00109 {
00110 labelAttributeList << getLabel( attIt.value() );
00111 }
00112 else
00113 {
00114 labelAttributeList << QString();
00115 }
00116 symbolList << dynamic_cast<QgsMarkerSymbolV2*>( firstSymbolForFeature( mRenderer, attIt.value() ) );
00117 }
00118 }
00119 }
00120 }
00121 else
00122 {
00123 symbolList << dynamic_cast<QgsMarkerSymbolV2*>( firstSymbolForFeature( mRenderer, feature ) );
00124 if ( mDrawLabels )
00125 {
00126 labelAttributeList << getLabel( feature );
00127 }
00128 else
00129 {
00130 labelAttributeList << QString();
00131 }
00132 }
00133
00134 if ( symbolList.isEmpty() && labelAttributeList.isEmpty() )
00135 {
00136 return true;
00137 }
00138
00139
00140
00141 double diagonal = 0;
00142 double currentWidthFactor;
00143
00144 QList<QgsMarkerSymbolV2*>::const_iterator it = symbolList.constBegin();
00145 for ( ; it != symbolList.constEnd(); ++it )
00146 {
00147 if ( *it )
00148 {
00149 currentWidthFactor = QgsSymbolLayerV2Utils::lineWidthScaleFactor( context, ( *it )->outputUnit() );
00150 double currentDiagonal = sqrt( 2 * (( *it )->size() * ( *it )->size() ) ) * currentWidthFactor;
00151 if ( currentDiagonal > diagonal )
00152 {
00153 diagonal = currentDiagonal;
00154 }
00155 }
00156 }
00157
00158
00159 QgsSymbolV2RenderContext symbolContext( context, QgsSymbolV2::MM, 1.0, selected );
00160 double circleAdditionPainterUnits = symbolContext.outputLineWidth( mCircleRadiusAddition );
00161 double radius = qMax(( diagonal / 2 ), labelAttributeList.size() * diagonal / 2 / M_PI ) + circleAdditionPainterUnits;
00162
00163
00164 drawCircle( radius, symbolContext, pt, symbolList.size() );
00165
00166 QList<QPointF> symbolPositions;
00167 QList<QPointF> labelPositions;
00168 calculateSymbolAndLabelPositions( pt, labelAttributeList.size(), radius, diagonal, symbolPositions, labelPositions );
00169
00170
00171 if ( labelAttributeList.size() > 1 )
00172 {
00173 if ( mCenterSymbol )
00174 {
00175 mCenterSymbol->renderPoint( pt, &feature, context, layer, selected );
00176 }
00177 else
00178 {
00179 context.painter()->drawRect( QRectF( pt.x() - symbolContext.outputLineWidth( 1 ), pt.y() - symbolContext.outputLineWidth( 1 ), symbolContext.outputLineWidth( 2 ), symbolContext.outputLineWidth( 2 ) ) );
00180 }
00181 }
00182
00183
00184 drawSymbols( feature, context, symbolList, symbolPositions, selected );
00185
00186 drawLabels( pt, symbolContext, labelPositions, labelAttributeList );
00187 return true;
00188 }
00189
00190 void QgsPointDisplacementRenderer::setEmbeddedRenderer( QgsFeatureRendererV2* r )
00191 {
00192 delete mRenderer;
00193 mRenderer = r;
00194 }
00195
00196 QgsSymbolV2* QgsPointDisplacementRenderer::symbolForFeature( QgsFeature& feature )
00197 {
00198 Q_UNUSED( feature );
00199 return 0;
00200 }
00201
00202 void QgsPointDisplacementRenderer::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer )
00203 {
00204 mRenderer->startRender( context, vlayer );
00205
00206
00207 createDisplacementGroups( const_cast<QgsVectorLayer*>( vlayer ), context.extent() );
00208 printInfoDisplacementGroups();
00209
00210 if ( mLabelAttributeName.isEmpty() )
00211 {
00212 mLabelIndex = -1;
00213 }
00214 else
00215 {
00216 mLabelIndex = vlayer->fieldNameIndex( mLabelAttributeName );
00217 }
00218
00219 if ( mMaxLabelScaleDenominator > 0 && context.rendererScale() > mMaxLabelScaleDenominator )
00220 {
00221 mDrawLabels = false;
00222 }
00223 else
00224 {
00225 mDrawLabels = true;
00226 }
00227
00228 if ( mCenterSymbol )
00229 {
00230 mCenterSymbol->startRender( context, vlayer );
00231 }
00232 }
00233
00234 void QgsPointDisplacementRenderer::stopRender( QgsRenderContext& context )
00235 {
00236 QgsDebugMsg( "QgsPointDisplacementRenderer::stopRender" );
00237 mRenderer->stopRender( context );
00238 if ( mCenterSymbol )
00239 {
00240 mCenterSymbol->stopRender( context );
00241 }
00242 }
00243
00244 QList<QString> QgsPointDisplacementRenderer::usedAttributes()
00245 {
00246 QList<QString> attributeList;
00247 if ( !mLabelAttributeName.isEmpty() )
00248 {
00249 attributeList.push_back( mLabelAttributeName );
00250 }
00251 if ( mRenderer )
00252 {
00253 attributeList += mRenderer->usedAttributes();
00254 }
00255 return attributeList;
00256 }
00257
00258 QgsSymbolV2List QgsPointDisplacementRenderer::symbols()
00259 {
00260 if ( mRenderer )
00261 {
00262 return mRenderer->symbols();
00263 }
00264 else
00265 {
00266 return QgsSymbolV2List();
00267 }
00268 }
00269
00270 QgsFeatureRendererV2* QgsPointDisplacementRenderer::create( QDomElement& symbologyElem )
00271 {
00272 QgsPointDisplacementRenderer* r = new QgsPointDisplacementRenderer();
00273 r->setLabelAttributeName( symbologyElem.attribute( "labelAttributeName" ) );
00274 QFont labelFont;
00275 labelFont.fromString( symbologyElem.attribute( "labelFont", "" ) );
00276 r->setLabelFont( labelFont );
00277 r->setCircleWidth( symbologyElem.attribute( "circleWidth", "0.4" ).toDouble() );
00278 r->setCircleColor( QgsSymbolLayerV2Utils::decodeColor( symbologyElem.attribute( "circleColor", "" ) ) );
00279 r->setLabelColor( QgsSymbolLayerV2Utils::decodeColor( symbologyElem.attribute( "labelColor", "" ) ) );
00280 r->setCircleRadiusAddition( symbologyElem.attribute( "circleRadiusAddition", "0.0" ).toDouble() );
00281 r->setMaxLabelScaleDenominator( symbologyElem.attribute( "maxLabelScaleDenominator", "-1" ).toDouble() );
00282
00283
00284 QDomElement embeddedRendererElem = symbologyElem.firstChildElement( "renderer-v2" );
00285 if ( !embeddedRendererElem.isNull() )
00286 {
00287 r->setEmbeddedRenderer( QgsFeatureRendererV2::load( embeddedRendererElem ) );
00288 }
00289
00290
00291 QDomElement centerSymbolElem = symbologyElem.firstChildElement( "symbol" );
00292 if ( !centerSymbolElem.isNull() )
00293 {
00294 r->setCenterSymbol( dynamic_cast<QgsMarkerSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( centerSymbolElem ) ) );
00295 }
00296 return r;
00297 }
00298
00299 QDomElement QgsPointDisplacementRenderer::save( QDomDocument& doc )
00300 {
00301 QDomElement rendererElement = doc.createElement( RENDERER_TAG_NAME );
00302 rendererElement.setAttribute( "type", "pointDisplacement" );
00303 rendererElement.setAttribute( "labelAttributeName", mLabelAttributeName );
00304 rendererElement.setAttribute( "labelFont", mLabelFont.toString() );
00305 rendererElement.setAttribute( "circleWidth", mCircleWidth );
00306 rendererElement.setAttribute( "circleColor", QgsSymbolLayerV2Utils::encodeColor( mCircleColor ) );
00307 rendererElement.setAttribute( "labelColor", QgsSymbolLayerV2Utils::encodeColor( mLabelColor ) );
00308 rendererElement.setAttribute( "circleRadiusAddition", mCircleRadiusAddition );
00309 rendererElement.setAttribute( "maxLabelScaleDenominator", mMaxLabelScaleDenominator );
00310
00311 if ( mRenderer )
00312 {
00313 QDomElement embeddedRendererElem = mRenderer->save( doc );
00314 rendererElement.appendChild( embeddedRendererElem );
00315 }
00316 if ( mCenterSymbol )
00317 {
00318 QDomElement centerSymbolElem = QgsSymbolLayerV2Utils::saveSymbol( "centerSymbol", mCenterSymbol, doc );
00319 rendererElement.appendChild( centerSymbolElem );
00320 }
00321 return rendererElement;
00322 }
00323
00324 QgsLegendSymbologyList QgsPointDisplacementRenderer::legendSymbologyItems( QSize iconSize )
00325 {
00326 if ( mRenderer )
00327 {
00328 return mRenderer->legendSymbologyItems( iconSize );
00329 }
00330 return QgsLegendSymbologyList();
00331 }
00332
00333 QgsLegendSymbolList QgsPointDisplacementRenderer::legendSymbolItems()
00334 {
00335 if ( mRenderer )
00336 {
00337 return mRenderer->legendSymbolItems();
00338 }
00339 return QgsLegendSymbolList();
00340 }
00341
00342 void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent )
00343 {
00344 if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) )
00345 {
00346 return;
00347 }
00348
00349 mDisplacementGroups.clear();
00350 mDisplacementIds.clear();
00351
00352
00353 QgsSpatialIndex spatialIndex;
00354
00355
00356 QgsAttributeList attList;
00357 QList<QString> attributeStrings = usedAttributes();
00358 QList<QString>::const_iterator attStringIt = attributeStrings.constBegin();
00359 for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt )
00360 {
00361 attList.push_back( vlayer->fieldNameIndex( *attStringIt ) );
00362 }
00363
00364 QgsFeature f;
00365 QList<QgsFeatureId> intersectList;
00366
00367 vlayer->select( attList, viewExtent, true, false );
00368 while ( vlayer->nextFeature( f ) )
00369 {
00370 intersectList.clear();
00371
00372
00373 if ( f.geometry() )
00374 {
00375 intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) );
00376 if ( intersectList.empty() )
00377 {
00378 spatialIndex.insertFeature( f );
00379 }
00380 else
00381 {
00382
00383 QgsFeatureId existingEntry = intersectList.at( 0 );
00384 bool found = false;
00385 QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin();
00386 for ( ; it != mDisplacementGroups.end(); ++it )
00387 {
00388 if ( it->size() > 0 && it->contains( existingEntry ) )
00389 {
00390 found = true;
00391 QgsFeature feature;
00392 it->insert( f.id(), f );
00393 mDisplacementIds.insert( f.id() );
00394 break;
00395 }
00396 }
00397
00398 if ( !found )
00399 {
00400 QMap<QgsFeatureId, QgsFeature> newMap;
00401 QgsFeature existingFeature;
00402 vlayer->featureAtId( existingEntry, existingFeature );
00403 newMap.insert( existingEntry, existingFeature );
00404 mDisplacementIds.insert( existingEntry );
00405 newMap.insert( f.id(), f );
00406 mDisplacementIds.insert( f.id() );
00407 mDisplacementGroups.push_back( newMap );
00408 }
00409 }
00410 }
00411 }
00412
00413 vlayer->select( attList, viewExtent, true, false );
00414 }
00415
00416 QgsRectangle QgsPointDisplacementRenderer::searchRect( const QgsPoint& p ) const
00417 {
00418 return QgsRectangle( p.x() - mTolerance, p.y() - mTolerance, p.x() + mTolerance, p.y() + mTolerance );
00419 }
00420
00421 void QgsPointDisplacementRenderer::printInfoDisplacementGroups()
00422 {
00423 int nGroups = mDisplacementGroups.size();
00424 QgsDebugMsg( "number of displacement groups:" + QString::number( nGroups ) );
00425 for ( int i = 0; i < nGroups; ++i )
00426 {
00427 QgsDebugMsg( "***************displacement group " + QString::number( i ) );
00428 QMap<QgsFeatureId, QgsFeature>::const_iterator it = mDisplacementGroups.at( i ).constBegin();
00429 for ( ; it != mDisplacementGroups.at( i ).constEnd(); ++it )
00430 {
00431 QgsDebugMsg( FID_TO_STRING( it.key() ) );
00432 }
00433 }
00434 QgsDebugMsg( "********all displacement ids*********" );
00435 QSet<QgsFeatureId>::const_iterator iIt = mDisplacementIds.constBegin();
00436 for ( ; iIt != mDisplacementIds.constEnd(); ++iIt )
00437 {
00438 QgsDebugMsg( FID_TO_STRING( *iIt ) );
00439 }
00440 }
00441
00442 void QgsPointDisplacementRenderer::setDisplacementGroups( const QList< QMap<QgsFeatureId, QgsFeature> >& list )
00443 {
00444 mDisplacementGroups = list;
00445 mDisplacementIds.clear();
00446
00447 QList<QMap<QgsFeatureId, QgsFeature> >::const_iterator list_it = mDisplacementGroups.constBegin();
00448 for ( ; list_it != mDisplacementGroups.constEnd(); ++list_it )
00449 {
00450 QMap<QgsFeatureId, QgsFeature>::const_iterator map_it = list_it->constBegin();
00451 for ( ; map_it != list_it->constEnd(); ++map_it )
00452 {
00453 mDisplacementIds.insert( map_it.key() );
00454 }
00455 }
00456 }
00457
00458 QString QgsPointDisplacementRenderer::getLabel( const QgsFeature& f )
00459 {
00460 QString attribute;
00461 QgsAttributeMap attMap = f.attributeMap();
00462 if ( attMap.size() > 0 )
00463 {
00464 QgsAttributeMap::const_iterator valIt = attMap.find( mLabelIndex );
00465 if ( valIt != attMap.constEnd() )
00466 {
00467 attribute = valIt->toString();
00468 }
00469 }
00470 return attribute;
00471 }
00472
00473 void QgsPointDisplacementRenderer::setCenterSymbol( QgsMarkerSymbolV2* symbol )
00474 {
00475 delete mCenterSymbol;
00476 mCenterSymbol = symbol;
00477 }
00478
00479
00480
00481 void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( const QPointF& centerPoint, int nPosition, double radius,
00482 double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts ) const
00483 {
00484 symbolPositions.clear();
00485 labelShifts.clear();
00486
00487 if ( nPosition < 1 )
00488 {
00489 return;
00490 }
00491 else if ( nPosition == 1 )
00492 {
00493 symbolPositions.append( centerPoint );
00494 labelShifts.append( QPointF( symbolDiagonal / 2.0, -symbolDiagonal / 2.0 ) );
00495 return;
00496 }
00497
00498 double fullPerimeter = 2 * M_PI;
00499 double angleStep = fullPerimeter / nPosition;
00500 double currentAngle;
00501
00502 for ( currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
00503 {
00504 double sinusCurrentAngle = sin( currentAngle );
00505 double cosinusCurrentAngle = cos( currentAngle );
00506 QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
00507 QPointF labelShift(( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
00508 symbolPositions.append( centerPoint + positionShift );
00509 labelShifts.append( labelShift );
00510 }
00511 }
00512
00513 void QgsPointDisplacementRenderer::drawCircle( double radiusPainterUnits, QgsSymbolV2RenderContext& context, const QPointF& centerPoint, int nSymbols )
00514 {
00515 QPainter* p = context.renderContext().painter();
00516 if ( nSymbols < 2 || !p )
00517 {
00518 return;
00519 }
00520
00521
00522 QPen circlePen( mCircleColor );
00523 circlePen.setWidthF( context.outputLineWidth( mCircleWidth ) );
00524 p->setPen( circlePen );
00525 p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
00526 }
00527
00528 void QgsPointDisplacementRenderer::drawSymbols( QgsFeature& f, QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions, bool selected )
00529 {
00530 QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
00531 QList<QgsMarkerSymbolV2*>::const_iterator symbolIt = symbolList.constBegin();
00532 for ( ; symbolPosIt != symbolPositions.constEnd() && symbolIt != symbolList.constEnd(); ++symbolPosIt, ++symbolIt )
00533 {
00534 if ( *symbolIt )
00535 {
00536 ( *symbolIt )->renderPoint( *symbolPosIt, &f, context, -1, selected );
00537 }
00538 }
00539 }
00540
00541 void QgsPointDisplacementRenderer::drawLabels( const QPointF& centerPoint, QgsSymbolV2RenderContext& context, const QList<QPointF>& labelShifts, const QStringList& labelList )
00542 {
00543 QPainter* p = context.renderContext().painter();
00544 if ( !p )
00545 {
00546 return;
00547 }
00548
00549 QPen labelPen( mLabelColor );
00550 p->setPen( labelPen );
00551
00552
00553 QFont pixelSizeFont = mLabelFont;
00554 pixelSizeFont.setPixelSize( context.outputLineWidth( mLabelFont.pointSizeF() * 0.3527 ) );
00555 QFont scaledFont = pixelSizeFont;
00556 scaledFont.setPixelSize( pixelSizeFont.pixelSize() * context.renderContext().rasterScaleFactor() );
00557 p->setFont( scaledFont );
00558
00559 QFontMetricsF fontMetrics( pixelSizeFont );
00560 QPointF currentLabelShift;
00561
00562 QList<QPointF>::const_iterator labelPosIt = labelShifts.constBegin();
00563 QStringList::const_iterator text_it = labelList.constBegin();
00564
00565 for ( ; labelPosIt != labelShifts.constEnd() && text_it != labelList.constEnd(); ++labelPosIt, ++text_it )
00566 {
00567 currentLabelShift = *labelPosIt;
00568 if ( currentLabelShift.x() < 0 )
00569 {
00570 currentLabelShift.setX( currentLabelShift.x() - fontMetrics.width( *text_it ) );
00571 }
00572 if ( currentLabelShift.y() > 0 )
00573 {
00574 currentLabelShift.setY( currentLabelShift.y() + fontMetrics.ascent() );
00575 }
00576
00577 QPointF drawingPoint( centerPoint + currentLabelShift );
00578 p->save();
00579 p->translate( drawingPoint.x(), drawingPoint.y() );
00580 p->scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
00581 p->drawText( QPointF( 0, 0 ), *text_it );
00582 p->restore();
00583 }
00584 }
00585
00586 QgsSymbolV2* QgsPointDisplacementRenderer::firstSymbolForFeature( QgsFeatureRendererV2* r, QgsFeature& f )
00587 {
00588 if ( !r )
00589 {
00590 return 0;
00591 }
00592
00593 QgsSymbolV2List symbolList = r->symbolsForFeature( f );
00594 if ( symbolList.size() < 1 )
00595 {
00596 return 0;
00597 }
00598
00599 return symbolList.at( 0 );
00600 }