QGIS API Documentation  master-6227475
src/core/symbology-ng/qgsrendererv2.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsrendererv2.cpp
00003     ---------------------
00004     begin                : November 2009
00005     copyright            : (C) 2009 by Martin Dobias
00006     email                : wonder dot sk at gmail dot com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include "qgsrendererv2.h"
00017 #include "qgssymbolv2.h"
00018 #include "qgssymbollayerv2utils.h"
00019 
00020 #include "qgssinglesymbolrendererv2.h" // for default renderer
00021 
00022 #include "qgsrendererv2registry.h"
00023 
00024 #include "qgsrendercontext.h"
00025 #include "qgsclipper.h"
00026 #include "qgsgeometry.h"
00027 #include "qgsfeature.h"
00028 #include "qgslogger.h"
00029 #include "qgsvectorlayer.h"
00030 
00031 #include <QDomElement>
00032 #include <QDomDocument>
00033 #include <QPolygonF>
00034 
00035 
00036 
00037 unsigned char* QgsFeatureRendererV2::_getPoint( QPointF& pt, QgsRenderContext& context, unsigned char* wkb )
00038 {
00039   wkb++; // jump over endian info
00040   unsigned int wkbType = *(( int* ) wkb );
00041   wkb += sizeof( unsigned int );
00042 
00043   double x = *(( double * ) wkb ); wkb += sizeof( double );
00044   double y = *(( double * ) wkb ); wkb += sizeof( double );
00045 
00046   if ( wkbType == QGis::WKBPolygon25D )
00047     wkb += sizeof( double );
00048 
00049   if ( context.coordinateTransform() )
00050   {
00051     double z = 0; // dummy variable for coordiante transform
00052     context.coordinateTransform()->transformInPlace( x, y, z );
00053   }
00054 
00055   context.mapToPixel().transformInPlace( x, y );
00056 
00057   pt = QPointF( x, y );
00058   return wkb;
00059 }
00060 
00061 unsigned char* QgsFeatureRendererV2::_getLineString( QPolygonF& pts, QgsRenderContext& context, unsigned char* wkb )
00062 {
00063   wkb++; // jump over endian info
00064   unsigned int wkbType = *(( int* ) wkb );
00065   wkb += sizeof( unsigned int );
00066   unsigned int nPoints = *(( int* ) wkb );
00067   wkb += sizeof( unsigned int );
00068 
00069   bool hasZValue = ( wkbType == QGis::WKBLineString25D );
00070   double x, y;
00071   const QgsCoordinateTransform* ct = context.coordinateTransform();
00072   const QgsMapToPixel& mtp = context.mapToPixel();
00073 
00074   //apply clipping for large lines to achieve a better rendering performance
00075   if ( nPoints > 1 )
00076   {
00077     const QgsRectangle& e = context.extent();
00078     double cw = e.width() / 10; double ch = e.height() / 10;
00079     QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
00080     wkb = QgsClipper::clippedLineWKB( wkb - ( 2 * sizeof( unsigned int ) + 1 ), clipRect, pts );
00081   }
00082   else
00083   {
00084     pts.resize( nPoints );
00085 
00086     QPointF* ptr = pts.data();
00087     for ( unsigned int i = 0; i < nPoints; ++i, ++ptr )
00088     {
00089       x = *(( double * ) wkb );
00090       wkb += sizeof( double );
00091       y = *(( double * ) wkb );
00092       wkb += sizeof( double );
00093 
00094       if ( hasZValue ) // ignore Z value
00095         wkb += sizeof( double );
00096 
00097       *ptr = QPointF( x, y );
00098     }
00099   }
00100 
00101   //transform the QPolygonF to screen coordinates
00102   if ( ct )
00103   {
00104     ct->transformPolygon( pts );
00105   }
00106 
00107   QPointF* ptr = pts.data();
00108   for ( int i = 0; i < pts.size(); ++i, ++ptr )
00109   {
00110     mtp.transformInPlace( ptr->rx(), ptr->ry() );
00111   }
00112 
00113 
00114   return wkb;
00115 }
00116 
00117 unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, unsigned char* wkb )
00118 {
00119   wkb++; // jump over endian info
00120   unsigned int wkbType = *(( int* ) wkb );
00121   wkb += sizeof( unsigned int ); // jump over wkb type
00122   unsigned int numRings = *(( int* ) wkb );
00123   wkb += sizeof( unsigned int );
00124 
00125   if ( numRings == 0 )  // sanity check for zero rings in polygon
00126     return wkb;
00127 
00128   bool hasZValue = ( wkbType == QGis::WKBPolygon25D );
00129   double x, y;
00130   holes.clear();
00131 
00132   const QgsCoordinateTransform* ct = context.coordinateTransform();
00133   const QgsMapToPixel& mtp = context.mapToPixel();
00134   const QgsRectangle& e = context.extent();
00135   double cw = e.width() / 10; double ch = e.height() / 10;
00136   QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
00137 
00138   for ( unsigned int idx = 0; idx < numRings; idx++ )
00139   {
00140     unsigned int nPoints = *(( int* )wkb );
00141     wkb += sizeof( unsigned int );
00142 
00143     QPolygonF poly( nPoints );
00144 
00145     // Extract the points from the WKB and store in a pair of vectors.
00146     QPointF* ptr = poly.data();
00147     for ( unsigned int jdx = 0; jdx < nPoints; ++jdx, ++ptr )
00148     {
00149       x = *(( double * ) wkb ); wkb += sizeof( double );
00150       y = *(( double * ) wkb ); wkb += sizeof( double );
00151 
00152       *ptr = QPointF( x, y );
00153 
00154       if ( hasZValue )
00155         wkb += sizeof( double );
00156     }
00157 
00158     if ( nPoints < 1 )
00159       continue;
00160 
00161     //clip close to view extent
00162     QgsClipper::trimPolygon( poly, clipRect );
00163 
00164     //transform the QPolygonF to screen coordinates
00165     if ( ct )
00166     {
00167       ct->transformPolygon( poly );
00168     }
00169 
00170 
00171     ptr = poly.data();
00172     for ( int i = 0; i < poly.size(); ++i, ++ptr )
00173     {
00174       mtp.transformInPlace( ptr->rx(), ptr->ry() );
00175     }
00176 
00177     if ( idx == 0 )
00178       pts = poly;
00179     else
00180       holes.append( poly );
00181   }
00182 
00183   return wkb;
00184 }
00185 
00186 void QgsFeatureRendererV2::setScaleMethodToSymbol( QgsSymbolV2* symbol, int scaleMethod )
00187 {
00188   if ( symbol )
00189   {
00190     if ( symbol->type() == QgsSymbolV2::Marker )
00191     {
00192       QgsMarkerSymbolV2* ms = static_cast<QgsMarkerSymbolV2*>( symbol );
00193       if ( ms )
00194       {
00195         ms->setScaleMethod(( QgsSymbolV2::ScaleMethod )scaleMethod );
00196       }
00197     }
00198   }
00199 }
00200 
00201 
00202 QgsFeatureRendererV2::QgsFeatureRendererV2( QString type )
00203     : mType( type ), mUsingSymbolLevels( false ),
00204     mCurrentVertexMarkerType( QgsVectorLayer::Cross ),
00205     mCurrentVertexMarkerSize( 3 )
00206 {
00207 }
00208 
00209 QgsFeatureRendererV2* QgsFeatureRendererV2::defaultRenderer( QGis::GeometryType geomType )
00210 {
00211   return new QgsSingleSymbolRendererV2( QgsSymbolV2::defaultSymbol( geomType ) );
00212 }
00213 
00214 
00215 bool QgsFeatureRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
00216 {
00217   QgsSymbolV2* symbol = symbolForFeature( feature );
00218   if ( symbol == NULL )
00219     return false;
00220 
00221   renderFeatureWithSymbol( feature, symbol, context, layer, selected, drawVertexMarker );
00222   return true;
00223 }
00224 
00225 void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymbolV2* symbol, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
00226 {
00227   QgsSymbolV2::SymbolType symbolType = symbol->type();
00228 
00229   QgsGeometry* geom = feature.geometry();
00230   switch ( geom->wkbType() )
00231   {
00232     case QGis::WKBPoint:
00233     case QGis::WKBPoint25D:
00234     {
00235       if ( symbolType != QgsSymbolV2::Marker )
00236       {
00237         QgsDebugMsg( "point can be drawn only with marker symbol!" );
00238         break;
00239       }
00240       QPointF pt;
00241       _getPoint( pt, context, geom->asWkb() );
00242       (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected );
00243 
00244       //if ( drawVertexMarker )
00245       //  renderVertexMarker( pt, context );
00246     }
00247     break;
00248 
00249     case QGis::WKBLineString:
00250     case QGis::WKBLineString25D:
00251     {
00252       if ( symbolType != QgsSymbolV2::Line )
00253       {
00254         QgsDebugMsg( "linestring can be drawn only with line symbol!" );
00255         break;
00256       }
00257       QPolygonF pts;
00258       _getLineString( pts, context, geom->asWkb() );
00259       (( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected );
00260 
00261       if ( drawVertexMarker )
00262         renderVertexMarkerPolyline( pts, context );
00263     }
00264     break;
00265 
00266     case QGis::WKBPolygon:
00267     case QGis::WKBPolygon25D:
00268     {
00269       if ( symbolType != QgsSymbolV2::Fill )
00270       {
00271         QgsDebugMsg( "polygon can be drawn only with fill symbol!" );
00272         break;
00273       }
00274       QPolygonF pts;
00275       QList<QPolygonF> holes;
00276       _getPolygon( pts, holes, context, geom->asWkb() );
00277       (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );
00278 
00279       if ( drawVertexMarker )
00280         renderVertexMarkerPolygon( pts, ( holes.count() ? &holes : NULL ), context );
00281     }
00282     break;
00283 
00284     case QGis::WKBMultiPoint:
00285     case QGis::WKBMultiPoint25D:
00286     {
00287       if ( symbolType != QgsSymbolV2::Marker )
00288       {
00289         QgsDebugMsg( "multi-point can be drawn only with marker symbol!" );
00290         break;
00291       }
00292 
00293       unsigned char* wkb = geom->asWkb();
00294       unsigned int num = *(( int* )( wkb + 5 ) );
00295       unsigned char* ptr = wkb + 9;
00296       QPointF pt;
00297 
00298       for ( unsigned int i = 0; i < num; ++i )
00299       {
00300         ptr = _getPoint( pt, context, ptr );
00301         (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected );
00302 
00303         //if ( drawVertexMarker )
00304         //  renderVertexMarker( pt, context );
00305       }
00306     }
00307     break;
00308 
00309     case QGis::WKBMultiLineString:
00310     case QGis::WKBMultiLineString25D:
00311     {
00312       if ( symbolType != QgsSymbolV2::Line )
00313       {
00314         QgsDebugMsg( "multi-linestring can be drawn only with line symbol!" );
00315         break;
00316       }
00317 
00318       unsigned char* wkb = geom->asWkb();
00319       unsigned int num = *(( int* )( wkb + 5 ) );
00320       unsigned char* ptr = wkb + 9;
00321       QPolygonF pts;
00322 
00323       for ( unsigned int i = 0; i < num; ++i )
00324       {
00325         ptr = _getLineString( pts, context, ptr );
00326         (( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected );
00327 
00328         if ( drawVertexMarker )
00329           renderVertexMarkerPolyline( pts, context );
00330       }
00331     }
00332     break;
00333 
00334     case QGis::WKBMultiPolygon:
00335     case QGis::WKBMultiPolygon25D:
00336     {
00337       if ( symbolType != QgsSymbolV2::Fill )
00338       {
00339         QgsDebugMsg( "multi-polygon can be drawn only with fill symbol!" );
00340         break;
00341       }
00342 
00343       unsigned char* wkb = geom->asWkb();
00344       unsigned int num = *(( int* )( wkb + 5 ) );
00345       unsigned char* ptr = wkb + 9;
00346       QPolygonF pts;
00347       QList<QPolygonF> holes;
00348 
00349       for ( unsigned int i = 0; i < num; ++i )
00350       {
00351         ptr = _getPolygon( pts, holes, context, ptr );
00352         (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );
00353 
00354         if ( drawVertexMarker )
00355           renderVertexMarkerPolygon( pts, ( holes.count() ? &holes : NULL ), context );
00356       }
00357     }
00358     break;
00359 
00360     default:
00361       QgsDebugMsg( QString( "feature %1: unsupported wkb type 0x%2 for rendering" ).arg( feature.id() ).arg( geom->wkbType(), 0, 16 ) );
00362   }
00363 }
00364 
00365 QString QgsFeatureRendererV2::dump()
00366 {
00367   return "UNKNOWN RENDERER\n";
00368 }
00369 
00370 
00371 QgsFeatureRendererV2* QgsFeatureRendererV2::load( QDomElement& element )
00372 {
00373   // <renderer-v2 type=""> ... </renderer-v2>
00374 
00375   if ( element.isNull() )
00376     return NULL;
00377 
00378   // load renderer
00379   QString rendererType = element.attribute( "type" );
00380 
00381   QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( rendererType );
00382   if ( m == NULL )
00383     return NULL;
00384 
00385   QgsFeatureRendererV2* r = m->createRenderer( element );
00386   if ( r )
00387   {
00388     r->setUsingSymbolLevels( element.attribute( "symbollevels", "0" ).toInt() );
00389   }
00390   return r;
00391 }
00392 
00393 QDomElement QgsFeatureRendererV2::save( QDomDocument& doc )
00394 {
00395   // create empty renderer element
00396   return doc.createElement( RENDERER_TAG_NAME );
00397 }
00398 
00399 QgsFeatureRendererV2* QgsFeatureRendererV2::loadSld( const QDomNode &node, QGis::GeometryType geomType, QString &errorMessage )
00400 {
00401   QDomElement element = node.toElement();
00402   if ( element.isNull() )
00403     return NULL;
00404 
00405   // get the UserStyle element
00406   QDomElement userStyleElem = element.firstChildElement( "UserStyle" );
00407   if ( userStyleElem.isNull() )
00408   {
00409     // UserStyle element not found, nothing will be rendered
00410     errorMessage = "Info: UserStyle element not found.";
00411     return NULL;
00412   }
00413 
00414   // get the FeatureTypeStyle element
00415   QDomElement featTypeStyleElem = userStyleElem.firstChildElement( "FeatureTypeStyle" );
00416   if ( featTypeStyleElem.isNull() )
00417   {
00418     errorMessage = "Info: FeatureTypeStyle element not found.";
00419     return NULL;
00420   }
00421 
00422   // use the RuleRenderer when more rules are present or the rule
00423   // has filters or min/max scale denominators set,
00424   // otherwise use the SingleSymbol renderer
00425   bool needRuleRenderer = false;
00426   int ruleCount = 0;
00427 
00428   QDomElement ruleElem = featTypeStyleElem.firstChildElement( "Rule" );
00429   while ( !ruleElem.isNull() )
00430   {
00431     ruleCount++;
00432 
00433     // more rules present, use the RuleRenderer
00434     if ( ruleCount > 1 )
00435     {
00436       QgsDebugMsg( "more Rule elements found: need a RuleRenderer" );
00437       needRuleRenderer = true;
00438       break;
00439     }
00440 
00441     QDomElement ruleChildElem = ruleElem.firstChildElement();
00442     while ( !ruleChildElem.isNull() )
00443     {
00444       // rule has filter or min/max scale denominator, use the RuleRenderer
00445       if ( ruleChildElem.localName() == "Filter" ||
00446            ruleChildElem.localName() == "MinScaleDenominator" ||
00447            ruleChildElem.localName() == "MaxScaleDenominator" )
00448       {
00449         QgsDebugMsg( "Filter or Min/MaxScaleDenominator element found: need a RuleRenderer" );
00450         needRuleRenderer = true;
00451         break;
00452       }
00453 
00454       ruleChildElem = ruleChildElem.nextSiblingElement();
00455     }
00456 
00457     if ( needRuleRenderer )
00458     {
00459       break;
00460     }
00461 
00462     ruleElem = ruleElem.nextSiblingElement( "Rule" );
00463   }
00464 
00465   QString rendererType;
00466   if ( needRuleRenderer )
00467   {
00468     rendererType = "RuleRenderer";
00469   }
00470   else
00471   {
00472     rendererType = "singleSymbol";
00473   }
00474   QgsDebugMsg( QString( "Instantiating a '%1' renderer..." ).arg( rendererType ) );
00475 
00476   // create the renderer and return it
00477   QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( rendererType );
00478   if ( m == NULL )
00479   {
00480     errorMessage = QString( "Error: Unable to get metadata for '%1' renderer." ).arg( rendererType );
00481     return NULL;
00482   }
00483 
00484   QgsFeatureRendererV2* r = m->createRendererFromSld( featTypeStyleElem, geomType );
00485   return r;
00486 }
00487 
00488 QDomElement QgsFeatureRendererV2::writeSld( QDomDocument& doc, const QgsVectorLayer &layer ) const
00489 {
00490   QDomElement userStyleElem = doc.createElement( "UserStyle" );
00491 
00492   QDomElement nameElem = doc.createElement( "se:Name" );
00493   nameElem.appendChild( doc.createTextNode( layer.name() ) );
00494   userStyleElem.appendChild( nameElem );
00495 
00496   QDomElement featureTypeStyleElem = doc.createElement( "se:FeatureTypeStyle" );
00497   toSld( doc, featureTypeStyleElem );
00498   userStyleElem.appendChild( featureTypeStyleElem );
00499 
00500   return userStyleElem;
00501 }
00502 
00503 QgsLegendSymbologyList QgsFeatureRendererV2::legendSymbologyItems( QSize iconSize )
00504 {
00505   Q_UNUSED( iconSize );
00506   // empty list by default
00507   return QgsLegendSymbologyList();
00508 }
00509 
00510 QgsLegendSymbolList QgsFeatureRendererV2::legendSymbolItems()
00511 {
00512   return QgsLegendSymbolList();
00513 }
00514 
00515 void QgsFeatureRendererV2::setVertexMarkerAppearance( int type, int size )
00516 {
00517   mCurrentVertexMarkerType = type;
00518   mCurrentVertexMarkerSize = size;
00519 }
00520 
00521 void QgsFeatureRendererV2::renderVertexMarker( QPointF& pt, QgsRenderContext& context )
00522 {
00523   QgsVectorLayer::drawVertexMarker( pt.x(), pt.y(), *context.painter(),
00524                                     ( QgsVectorLayer::VertexMarkerType ) mCurrentVertexMarkerType,
00525                                     mCurrentVertexMarkerSize );
00526 }
00527 
00528 void QgsFeatureRendererV2::renderVertexMarkerPolyline( QPolygonF& pts, QgsRenderContext& context )
00529 {
00530   foreach ( QPointF pt, pts )
00531     renderVertexMarker( pt, context );
00532 }
00533 
00534 void QgsFeatureRendererV2::renderVertexMarkerPolygon( QPolygonF& pts, QList<QPolygonF>* rings, QgsRenderContext& context )
00535 {
00536   foreach ( QPointF pt, pts )
00537     renderVertexMarker( pt, context );
00538 
00539   if ( rings )
00540   {
00541     foreach ( QPolygonF ring, *rings )
00542     {
00543       foreach ( QPointF pt, ring )
00544         renderVertexMarker( pt, context );
00545     }
00546   }
00547 }
00548 
00549 QgsSymbolV2List QgsFeatureRendererV2::symbolsForFeature( QgsFeature& feat )
00550 {
00551   QgsSymbolV2List lst;
00552   QgsSymbolV2* s = symbolForFeature( feat );
00553   if ( s ) lst.append( s );
00554   return lst;
00555 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines