|
QGIS API Documentation
master-6227475
|
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 }