Quantum GIS API Documentation
1.8
|
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 }