|
Quantum GIS API Documentation
master-ce49b66
|
00001 /*************************************************************************** 00002 qgsellipsesymbollayerv2.cpp 00003 --------------------- 00004 begin : June 2011 00005 copyright : (C) 2011 by Marco Hugentobler 00006 email : marco dot hugentobler at sourcepole dot ch 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 #include "qgsellipsesymbollayerv2.h" 00016 #include "qgsexpression.h" 00017 #include "qgsfeature.h" 00018 #include "qgsrendercontext.h" 00019 #include "qgsvectorlayer.h" 00020 #include "qgslogger.h" 00021 00022 #include <QPainter> 00023 #include <QSet> 00024 #include <QDomDocument> 00025 #include <QDomElement> 00026 00027 QgsEllipseSymbolLayerV2::QgsEllipseSymbolLayerV2(): mSymbolName( "circle" ), mSymbolWidth( 4 ), mSymbolWidthUnit( QgsSymbolV2::MM ), mSymbolHeight( 3 ), 00028 mSymbolHeightUnit( QgsSymbolV2::MM ), mFillColor( Qt::white ), mOutlineColor( Qt::black ), mOutlineWidth( 0 ), mOutlineWidthUnit( QgsSymbolV2::MM ) 00029 { 00030 mPen.setColor( mOutlineColor ); 00031 mPen.setWidth( 1.0 ); 00032 mPen.setJoinStyle( Qt::MiterJoin ); 00033 mBrush.setColor( mFillColor ); 00034 mBrush.setStyle( Qt::SolidPattern ); 00035 mOffset = QPointF( 0, 0 ); 00036 00037 mAngle = 0; 00038 } 00039 00040 QgsEllipseSymbolLayerV2::~QgsEllipseSymbolLayerV2() 00041 { 00042 } 00043 00044 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::create( const QgsStringMap& properties ) 00045 { 00046 QgsEllipseSymbolLayerV2* layer = new QgsEllipseSymbolLayerV2(); 00047 if ( properties.contains( "symbol_name" ) ) 00048 { 00049 layer->setSymbolName( properties[ "symbol_name" ] ); 00050 } 00051 if ( properties.contains( "symbol_width" ) ) 00052 { 00053 layer->setSymbolWidth( properties["symbol_width"].toDouble() ); 00054 } 00055 if ( properties.contains( "symbol_width_unit" ) ) 00056 { 00057 layer->setSymbolWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["symbol_width_unit"] ) ); 00058 } 00059 if ( properties.contains( "symbol_height" ) ) 00060 { 00061 layer->setSymbolHeight( properties["symbol_height"].toDouble() ); 00062 } 00063 if ( properties.contains( "symbol_height_unit" ) ) 00064 { 00065 layer->setSymbolHeightUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["symbol_height_unit"] ) ); 00066 } 00067 if ( properties.contains( "angle" ) ) 00068 { 00069 layer->setAngle( properties["angle"].toDouble() ); 00070 } 00071 if ( properties.contains( "outline_width" ) ) 00072 { 00073 layer->setOutlineWidth( properties["outline_width"].toDouble() ); 00074 } 00075 if ( properties.contains( "outline_width_unit" ) ) 00076 { 00077 layer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) ); 00078 } 00079 if ( properties.contains( "fill_color" ) ) 00080 { 00081 layer->setFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["fill_color"] ) ); 00082 } 00083 if ( properties.contains( "outline_color" ) ) 00084 { 00085 layer->setOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] ) ); 00086 } 00087 if ( properties.contains( "offset" ) ) 00088 { 00089 layer->setOffset( QgsSymbolLayerV2Utils::decodePoint( properties["offset"] ) ); 00090 } 00091 if ( properties.contains( "offset_unit" ) ) 00092 { 00093 layer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) ); 00094 } 00095 00096 //data defined properties 00097 if ( properties.contains( "width_expression" ) ) 00098 { 00099 layer->setDataDefinedProperty( "width", properties["width_expression"] ); 00100 } 00101 if ( properties.contains( "height_expression" ) ) 00102 { 00103 layer->setDataDefinedProperty( "height", properties["height_expression"] ); 00104 } 00105 if ( properties.contains( "rotation_expression" ) ) 00106 { 00107 layer->setDataDefinedProperty( "rotation", properties["rotation_expression"] ); 00108 } 00109 if ( properties.contains( "outline_width_expression" ) ) 00110 { 00111 layer->setDataDefinedProperty( "outline_width", properties[ "outline_width_expression" ] ); 00112 } 00113 if ( properties.contains( "fill_color_expression" ) ) 00114 { 00115 layer->setDataDefinedProperty( "fill_color", properties["fill_color_expression"] ); 00116 } 00117 if ( properties.contains( "outline_color_expression" ) ) 00118 { 00119 layer->setDataDefinedProperty( "outline_color", properties["outline_color_expression"] ); 00120 } 00121 if ( properties.contains( "symbol_name_expression" ) ) 00122 { 00123 layer->setDataDefinedProperty( "symbol_name", properties["symbol_name_expression"] ); 00124 } 00125 if ( properties.contains( "offset_expression" ) ) 00126 { 00127 layer->setDataDefinedProperty( "offset", properties["offset_expression"] ); 00128 } 00129 00130 //compatibility with old project file format 00131 if ( !properties["width_field"].isEmpty() ) 00132 { 00133 layer->setDataDefinedProperty( "width", properties["width_field"] ); 00134 } 00135 if ( !properties["height_field"].isEmpty() ) 00136 { 00137 layer->setDataDefinedProperty( "height", properties["height_field"] ); 00138 } 00139 if ( !properties["rotation_field"].isEmpty() ) 00140 { 00141 layer->setDataDefinedProperty( "rotation", properties["rotation_field"] ); 00142 } 00143 if ( !properties["outline_width_field"].isEmpty() ) 00144 { 00145 layer->setDataDefinedProperty( "outline_width", properties[ "outline_width_field" ] ); 00146 } 00147 if ( !properties["fill_color_field"].isEmpty() ) 00148 { 00149 layer->setDataDefinedProperty( "fill_color", properties["fill_color_field"] ); 00150 } 00151 if ( !properties["outline_color_field"].isEmpty() ) 00152 { 00153 layer->setDataDefinedProperty( "outline_color", properties["outline_color_field"] ); 00154 } 00155 if ( !properties["symbol_name_field"].isEmpty() ) 00156 { 00157 layer->setDataDefinedProperty( "symbol_name", properties["symbol_name_field"] ); 00158 } 00159 00160 return layer; 00161 } 00162 00163 void QgsEllipseSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00164 { 00165 QgsExpression* outlineWidthExpression = expression( "outline_width" ); 00166 QgsExpression* fillColorExpression = expression( "fill_color" ); 00167 QgsExpression* outlineColorExpression = expression( "outline_color" ); 00168 QgsExpression* widthExpression = expression( "width" ); 00169 QgsExpression* heightExpression = expression( "height" ); 00170 QgsExpression* symbolNameExpression = expression( "symbol_name" ); 00171 QgsExpression* rotationExpression = expression( "rotation" ); 00172 00173 if ( outlineWidthExpression ) 00174 { 00175 double width = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00176 width *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit ); 00177 mPen.setWidthF( width ); 00178 } 00179 if ( fillColorExpression ) 00180 { 00181 QString colorString = fillColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString(); 00182 mBrush.setColor( QColor( colorString ) ); 00183 } 00184 if ( outlineColorExpression ) 00185 { 00186 QString colorString = outlineColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString(); 00187 mPen.setColor( QColor( colorString ) ); 00188 } 00189 if ( widthExpression || heightExpression || symbolNameExpression ) 00190 { 00191 QString symbolName = mSymbolName; 00192 if ( symbolNameExpression ) 00193 { 00194 symbolName = symbolNameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString(); 00195 } 00196 preparePath( symbolName, context, context.feature() ); 00197 } 00198 00199 //offset 00200 double offsetX = 0; 00201 double offsetY = 0; 00202 markerOffset( context, offsetX, offsetY ); 00203 QPointF off( offsetX, offsetY ); 00204 00205 QPainter* p = context.renderContext().painter(); 00206 if ( !p ) 00207 { 00208 return; 00209 } 00210 00211 //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle) 00212 double rotation = 0.0; 00213 if ( rotationExpression ) 00214 { 00215 rotation = rotationExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00216 } 00217 else if ( !qgsDoubleNear( mAngle, 0.0 ) ) 00218 { 00219 rotation = mAngle; 00220 } 00221 if ( rotation ) 00222 off = _rotatedOffset( off, rotation ); 00223 00224 QMatrix transform; 00225 transform.translate( point.x() + off.x(), point.y() + off.y() ); 00226 if ( !qgsDoubleNear( rotation, 0.0 ) ) 00227 { 00228 transform.rotate( rotation ); 00229 } 00230 00231 p->setPen( mPen ); 00232 p->setBrush( mBrush ); 00233 p->drawPath( transform.map( mPainterPath ) ); 00234 } 00235 00236 QString QgsEllipseSymbolLayerV2::layerType() const 00237 { 00238 return "EllipseMarker"; 00239 } 00240 00241 void QgsEllipseSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00242 { 00243 if ( !context.feature() || !hasDataDefinedProperty() ) 00244 { 00245 preparePath( mSymbolName, context ); 00246 } 00247 mPen.setColor( mOutlineColor ); 00248 mPen.setWidthF( mOutlineWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit ) ); 00249 mBrush.setColor( mFillColor ); 00250 prepareExpressions( context.layer() ); 00251 } 00252 00253 void QgsEllipseSymbolLayerV2::stopRender( QgsSymbolV2RenderContext & ) 00254 { 00255 } 00256 00257 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::clone() const 00258 { 00259 return QgsEllipseSymbolLayerV2::create( properties() ); 00260 } 00261 00262 void QgsEllipseSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00263 { 00264 QDomElement symbolizerElem = doc.createElement( "se:PointSymbolizer" ); 00265 if ( !props.value( "uom", "" ).isEmpty() ) 00266 symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) ); 00267 element.appendChild( symbolizerElem ); 00268 00269 // <Geometry> 00270 QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) ); 00271 00272 writeSldMarker( doc, symbolizerElem, props ); 00273 } 00274 00275 void QgsEllipseSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00276 { 00277 // <Graphic> 00278 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 00279 element.appendChild( graphicElem ); 00280 00281 QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mFillColor, mOutlineColor, mOutlineWidth, mSymbolWidth ); 00282 00283 // store w/h factor in a <VendorOption> 00284 double widthHeightFactor = mSymbolWidth / mSymbolHeight; 00285 QDomElement factorElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) ); 00286 graphicElem.appendChild( factorElem ); 00287 00288 // <Rotation> 00289 const QgsExpression* rotationExpression = dataDefinedProperty( "rotation" ); 00290 QString angleFunc = props.value( "angle", "" ); 00291 if ( angleFunc.isEmpty() ) // symbol has no angle set 00292 { 00293 00294 if ( rotationExpression ) 00295 angleFunc = rotationExpression->dump(); 00296 else if ( !qgsDoubleNear( mAngle, 0.0 ) ) 00297 angleFunc = QString::number( mAngle ); 00298 } 00299 else if ( rotationExpression ) 00300 { 00301 // the symbol has an angle and the symbol layer have a rotation 00302 // property set 00303 angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( rotationExpression->dump() ); 00304 } 00305 else if ( !qgsDoubleNear( mAngle, 0.0 ) ) 00306 { 00307 // both the symbol and the symbol layer have angle value set 00308 bool ok; 00309 double angle = angleFunc.toDouble( &ok ); 00310 if ( !ok ) 00311 { 00312 // its a string (probably a property name or a function) 00313 angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( mAngle ); 00314 } 00315 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) ) 00316 { 00317 // it's a double value 00318 angleFunc = QString::number( angle + mAngle ); 00319 } 00320 } 00321 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 00322 } 00323 00324 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::createFromSld( QDomElement &element ) 00325 { 00326 QgsDebugMsg( "Entered." ); 00327 00328 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00329 if ( graphicElem.isNull() ) 00330 return NULL; 00331 00332 QString name = "circle"; 00333 QColor fillColor, borderColor; 00334 double borderWidth, size; 00335 double widthHeightFactor = 1.0; 00336 00337 QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem ); 00338 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it ) 00339 { 00340 if ( it.key() == "widthHeightFactor" ) 00341 { 00342 bool ok; 00343 double v = it.value().toDouble( &ok ); 00344 if ( ok && !qgsDoubleNear( v, 0.0 ) && v > 0 ) 00345 widthHeightFactor = v; 00346 } 00347 } 00348 00349 if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, borderColor, borderWidth, size ) ) 00350 return NULL; 00351 00352 double angle = 0.0; 00353 QString angleFunc; 00354 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 00355 { 00356 bool ok; 00357 double d = angleFunc.toDouble( &ok ); 00358 if ( ok ) 00359 angle = d; 00360 } 00361 00362 QgsEllipseSymbolLayerV2 *m = new QgsEllipseSymbolLayerV2(); 00363 m->setSymbolName( name ); 00364 m->setFillColor( fillColor ); 00365 m->setOutlineColor( borderColor ); 00366 m->setOutlineWidth( borderWidth ); 00367 m->setSymbolWidth( size ); 00368 m->setSymbolHeight( size / widthHeightFactor ); 00369 m->setAngle( angle ); 00370 return m; 00371 } 00372 00373 QgsStringMap QgsEllipseSymbolLayerV2::properties() const 00374 { 00375 QgsStringMap map; 00376 map["symbol_name"] = mSymbolName; 00377 map["symbol_width"] = QString::number( mSymbolWidth ); 00378 map["symbol_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSymbolWidthUnit ); 00379 map["symbol_height"] = QString::number( mSymbolHeight ); 00380 map["symbol_height_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSymbolHeightUnit ); 00381 map["angle"] = QString::number( mAngle ); 00382 map["outline_width"] = QString::number( mOutlineWidth ); 00383 map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ); 00384 map["fill_color"] = QgsSymbolLayerV2Utils::encodeColor( mFillColor ); 00385 map["outline_color"] = QgsSymbolLayerV2Utils::encodeColor( mOutlineColor ); 00386 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00387 map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ); 00388 saveDataDefinedProperties( map ); 00389 return map; 00390 } 00391 00392 bool QgsEllipseSymbolLayerV2::hasDataDefinedProperty() const 00393 { 00394 return ( dataDefinedProperty( "width" ) || dataDefinedProperty( "height" ) || dataDefinedProperty( "rotation" ) 00395 || dataDefinedProperty( "outline_width" ) || dataDefinedProperty( "fill_color" ) || dataDefinedProperty( "outline_color" ) 00396 || dataDefinedProperty( "symbol_name" ) || dataDefinedProperty( "offset" ) ); 00397 } 00398 00399 void QgsEllipseSymbolLayerV2::preparePath( const QString& symbolName, QgsSymbolV2RenderContext& context, const QgsFeature* f ) 00400 { 00401 mPainterPath = QPainterPath(); 00402 const QgsRenderContext& ct = context.renderContext(); 00403 00404 double width = 0; 00405 00406 QgsExpression* widthExpression = expression( "width" ); 00407 if ( widthExpression ) //1. priority: data defined setting on symbol layer level 00408 { 00409 width = widthExpression->evaluate( const_cast<QgsFeature*>( f ) ).toDouble(); 00410 } 00411 else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level 00412 { 00413 width = mSize; 00414 } 00415 else //3. priority: global width setting 00416 { 00417 width = mSymbolWidth; 00418 } 00419 width *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( ct, mSymbolWidthUnit ); 00420 00421 double height = 0; 00422 QgsExpression* heightExpression = expression( "height" ); 00423 if ( heightExpression ) //1. priority: data defined setting on symbol layer level 00424 { 00425 height = heightExpression->evaluate( const_cast<QgsFeature*>( f ) ).toDouble(); 00426 } 00427 else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level 00428 { 00429 height = mSize; 00430 } 00431 else //3. priority: global height setting 00432 { 00433 height = mSymbolHeight; 00434 } 00435 height *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( ct, mSymbolHeightUnit ); 00436 00437 if ( symbolName == "circle" ) 00438 { 00439 mPainterPath.addEllipse( QRectF( -width / 2.0, -height / 2.0, width, height ) ); 00440 } 00441 else if ( symbolName == "rectangle" ) 00442 { 00443 mPainterPath.addRect( QRectF( -width / 2.0, -height / 2.0, width, height ) ); 00444 } 00445 else if ( symbolName == "cross" ) 00446 { 00447 mPainterPath.moveTo( 0, -height / 2.0 ); 00448 mPainterPath.lineTo( 0, height / 2.0 ); 00449 mPainterPath.moveTo( -width / 2.0, 0 ); 00450 mPainterPath.lineTo( width / 2.0, 0 ); 00451 } 00452 else if ( symbolName == "triangle" ) 00453 { 00454 mPainterPath.moveTo( 0, -height / 2.0 ); 00455 mPainterPath.lineTo( -width / 2.0, height / 2.0 ); 00456 mPainterPath.lineTo( width / 2.0, height / 2.0 ); 00457 mPainterPath.lineTo( 0, -height / 2.0 ); 00458 } 00459 } 00460 00461 void QgsEllipseSymbolLayerV2::setOutputUnit( QgsSymbolV2::OutputUnit unit ) 00462 { 00463 mSymbolWidthUnit = unit; 00464 mSymbolHeightUnit = unit; 00465 mOutlineWidthUnit = unit; 00466 } 00467 00468 QgsSymbolV2::OutputUnit QgsEllipseSymbolLayerV2::outputUnit() const 00469 { 00470 QgsSymbolV2::OutputUnit unit = mSymbolWidthUnit; 00471 if ( mSymbolHeightUnit != unit || mOutlineWidthUnit != unit ) 00472 { 00473 return QgsSymbolV2::Mixed; 00474 } 00475 return unit; 00476 }