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