Quantum GIS API Documentation
1.8
|
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 "qgsfeature.h" 00017 #include "qgsrendercontext.h" 00018 #include "qgsvectorlayer.h" 00019 #include "qgslogger.h" 00020 00021 #include <QPainter> 00022 #include <QSet> 00023 #include <QDomDocument> 00024 #include <QDomElement> 00025 00026 QgsEllipseSymbolLayerV2::QgsEllipseSymbolLayerV2(): mSymbolName( "circle" ), mSymbolWidth( 4 ), mSymbolHeight( 3 ), 00027 mFillColor( Qt::black ), mOutlineColor( Qt::white ), mOutlineWidth( 0 ) 00028 { 00029 mPen.setColor( mOutlineColor ); 00030 mPen.setWidth( 1.0 ); 00031 mPen.setJoinStyle( Qt::MiterJoin ); 00032 mBrush.setColor( mFillColor ); 00033 mBrush.setStyle( Qt::SolidPattern ); 00034 00035 mAngle = 0; 00036 mWidthIndex = -1; 00037 mHeightIndex = -1; 00038 mRotationIndex = -1; 00039 mOutlineWidthIndex = -1; 00040 mFillColorIndex = -1; 00041 mOutlineColorIndex = -1; 00042 mSymbolNameIndex = -1; 00043 } 00044 00045 QgsEllipseSymbolLayerV2::~QgsEllipseSymbolLayerV2() 00046 { 00047 } 00048 00049 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::create( const QgsStringMap& properties ) 00050 { 00051 QgsEllipseSymbolLayerV2* layer = new QgsEllipseSymbolLayerV2(); 00052 if ( properties.contains( "symbol_name" ) ) 00053 { 00054 layer->setSymbolName( properties[ "symbol_name" ] ); 00055 } 00056 if ( properties.contains( "symbol_width" ) ) 00057 { 00058 layer->setSymbolWidth( properties["symbol_width"].toDouble() ); 00059 } 00060 if ( properties.contains( "symbol_height" ) ) 00061 { 00062 layer->setSymbolHeight( properties["symbol_height"].toDouble() ); 00063 } 00064 if ( properties.contains( "angle" ) ) 00065 { 00066 layer->setAngle( properties["angle"].toDouble() ); 00067 } 00068 if ( properties.contains( "outline_width" ) ) 00069 { 00070 layer->setOutlineWidth( properties["outline_width"].toDouble() ); 00071 } 00072 if ( properties.contains( "fill_color" ) ) 00073 { 00074 layer->setFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["fill_color"] ) ); 00075 } 00076 if ( properties.contains( "outline_color" ) ) 00077 { 00078 layer->setOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] ) ); 00079 } 00080 00081 //data defined properties 00082 if ( properties.contains( "height_field" ) ) 00083 { 00084 layer->setHeightField( properties["height_field"] ); 00085 } 00086 if ( properties.contains( "width_field" ) ) 00087 { 00088 layer->setWidthField( properties["width_field"] ); 00089 } 00090 if ( properties.contains( "rotation_field" ) ) 00091 { 00092 layer->setRotationField( properties["rotation_field"] ); 00093 } 00094 if ( properties.contains( "outline_width_field" ) ) 00095 { 00096 layer->setOutlineWidthField( properties["outline_width_field"] ); 00097 } 00098 if ( properties.contains( "fill_color_field" ) ) 00099 { 00100 layer->setFillColorField( properties["fill_color_field"] ); 00101 } 00102 if ( properties.contains( "outline_color_field" ) ) 00103 { 00104 layer->setOutlineColorField( properties["outline_color_field"] ); 00105 } 00106 if ( properties.contains( "symbol_name_field" ) ) 00107 { 00108 layer->setSymbolNameField( properties["symbol_name_field"] ); 00109 } 00110 00111 return layer; 00112 } 00113 00114 void QgsEllipseSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00115 { 00116 const QgsFeature* f = context.feature(); 00117 00118 if ( f ) 00119 { 00120 if ( mOutlineWidthIndex != -1 ) 00121 { 00122 double width = context.outputLineWidth( f->attributeMap()[mOutlineWidthIndex].toDouble() ); 00123 mPen.setWidthF( width ); 00124 } 00125 if ( mFillColorIndex != -1 ) 00126 { 00127 mBrush.setColor( QColor( f->attributeMap()[mFillColorIndex].toString() ) ); 00128 } 00129 if ( mOutlineColorIndex != -1 ) 00130 { 00131 mPen.setColor( QColor( f->attributeMap()[mOutlineColorIndex].toString() ) ); 00132 } 00133 00134 if ( mWidthIndex != -1 || mHeightIndex != -1 || mSymbolNameIndex != -1 ) 00135 { 00136 QString symbolName = ( mSymbolNameIndex == -1 ) ? mSymbolName : f->attributeMap()[mSymbolNameIndex].toString(); 00137 preparePath( symbolName, context, f ); 00138 } 00139 } 00140 00141 QPainter* p = context.renderContext().painter(); 00142 if ( !p ) 00143 { 00144 return; 00145 } 00146 00147 //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle) 00148 double rotation = 0.0; 00149 if ( f && mRotationIndex != -1 ) 00150 { 00151 rotation = f->attributeMap()[mRotationIndex].toDouble(); 00152 } 00153 else if ( !doubleNear( mAngle, 0.0 ) ) 00154 { 00155 rotation = mAngle; 00156 } 00157 00158 QMatrix transform; 00159 transform.translate( point.x(), point.y() ); 00160 if ( !doubleNear( rotation, 0.0 ) ) 00161 { 00162 transform.rotate( rotation ); 00163 } 00164 00165 p->setPen( mPen ); 00166 p->setBrush( mBrush ); 00167 p->drawPath( transform.map( mPainterPath ) ); 00168 } 00169 00170 QString QgsEllipseSymbolLayerV2::layerType() const 00171 { 00172 return "EllipseMarker"; 00173 } 00174 00175 void QgsEllipseSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00176 { 00177 if ( !context.feature() || !hasDataDefinedProperty() ) 00178 { 00179 preparePath( mSymbolName, context ); 00180 } 00181 mPen.setColor( mOutlineColor ); 00182 mPen.setWidthF( context.outputLineWidth( mOutlineWidth ) ); 00183 mBrush.setColor( mFillColor ); 00184 00185 //resolve data defined attribute indices 00186 const QgsVectorLayer* vlayer = context.layer(); 00187 if ( vlayer ) 00188 { 00189 mWidthIndex = vlayer->fieldNameIndex( mWidthField ); 00190 mHeightIndex = vlayer->fieldNameIndex( mHeightField ); 00191 mRotationIndex = vlayer->fieldNameIndex( mRotationField ); 00192 mOutlineWidthIndex = vlayer->fieldNameIndex( mOutlineWidthField ); 00193 mFillColorIndex = vlayer->fieldNameIndex( mFillColorField ); 00194 mOutlineColorIndex = vlayer->fieldNameIndex( mOutlineColorField ); 00195 mSymbolNameIndex = vlayer->fieldNameIndex( mSymbolNameField ); 00196 } 00197 } 00198 00199 void QgsEllipseSymbolLayerV2::stopRender( QgsSymbolV2RenderContext & ) 00200 { 00201 } 00202 00203 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::clone() const 00204 { 00205 return QgsEllipseSymbolLayerV2::create( properties() ); 00206 } 00207 00208 void QgsEllipseSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00209 { 00210 QDomElement symbolizerElem = doc.createElement( "se:PointSymbolizer" ); 00211 if ( !props.value( "uom", "" ).isEmpty() ) 00212 symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) ); 00213 element.appendChild( symbolizerElem ); 00214 00215 // <Geometry> 00216 QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) ); 00217 00218 writeSldMarker( doc, symbolizerElem, props ); 00219 } 00220 00221 void QgsEllipseSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00222 { 00223 // <Graphic> 00224 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 00225 element.appendChild( graphicElem ); 00226 00227 QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mFillColor, mOutlineColor, mOutlineWidth, mSymbolWidth ); 00228 00229 // store w/h factor in a <VendorOption> 00230 double widthHeightFactor = mSymbolWidth / mSymbolHeight; 00231 QDomElement factorElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) ); 00232 graphicElem.appendChild( factorElem ); 00233 00234 // <Rotation> 00235 QString angleFunc = props.value( "angle", "" ); 00236 if ( angleFunc.isEmpty() ) // symbol has no angle set 00237 { 00238 if ( !mRotationField.isEmpty() ) 00239 angleFunc = mRotationField; 00240 else if ( !doubleNear( mAngle, 0.0 ) ) 00241 angleFunc = QString::number( mAngle ); 00242 } 00243 else if ( !mRotationField.isEmpty() ) 00244 { 00245 // the symbol has an angle and the symbol layer have a rotation 00246 // property set 00247 angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( mRotationField ); 00248 } 00249 else if ( !doubleNear( mAngle, 0.0 ) ) 00250 { 00251 // both the symbol and the symbol layer have angle value set 00252 bool ok; 00253 double angle = angleFunc.toDouble( &ok ); 00254 if ( !ok ) 00255 { 00256 // its a string (probably a property name or a function) 00257 angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( mAngle ); 00258 } 00259 else if ( !doubleNear( angle + mAngle, 0.0 ) ) 00260 { 00261 // it's a double value 00262 angleFunc = QString::number( angle + mAngle ); 00263 } 00264 } 00265 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 00266 } 00267 00268 QgsSymbolLayerV2* QgsEllipseSymbolLayerV2::createFromSld( QDomElement &element ) 00269 { 00270 QgsDebugMsg( "Entered." ); 00271 00272 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00273 if ( graphicElem.isNull() ) 00274 return NULL; 00275 00276 QString name = "circle"; 00277 QColor color, borderColor; 00278 double borderWidth, size; 00279 double widthHeightFactor = 1.0; 00280 00281 QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem ); 00282 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it ) 00283 { 00284 if ( it.key() == "widthHeightFactor" ) 00285 { 00286 bool ok; 00287 double v = it.value().toDouble( &ok ); 00288 if ( ok && !doubleNear( v, 0.0 ) && v > 0 ) 00289 widthHeightFactor = v; 00290 } 00291 } 00292 00293 if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, color, borderColor, borderWidth, size ) ) 00294 return NULL; 00295 00296 double angle = 0.0; 00297 QString angleFunc; 00298 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 00299 { 00300 bool ok; 00301 double d = angleFunc.toDouble( &ok ); 00302 if ( ok ) 00303 angle = d; 00304 } 00305 00306 QgsEllipseSymbolLayerV2 *m = new QgsEllipseSymbolLayerV2(); 00307 m->setSymbolName( name ); 00308 m->setColor( color ); 00309 m->setOutlineColor( borderColor ); 00310 m->setOutlineWidth( borderWidth ); 00311 m->setSymbolWidth( size ); 00312 m->setSymbolHeight( size / widthHeightFactor ); 00313 m->setAngle( angle ); 00314 return m; 00315 } 00316 00317 QgsStringMap QgsEllipseSymbolLayerV2::properties() const 00318 { 00319 QgsStringMap map; 00320 map["symbol_name"] = mSymbolName; 00321 map["symbol_width"] = QString::number( mSymbolWidth ); 00322 map["width_field"] = mWidthField; 00323 map["symbol_height"] = QString::number( mSymbolHeight ); 00324 map["height_field"] = mHeightField; 00325 map["angle"] = QString::number( mAngle ); 00326 map["rotation_field"] = mRotationField; 00327 map["outline_width"] = QString::number( mOutlineWidth ); 00328 map["outline_width_field"] = mOutlineWidthField; 00329 map["fill_color"] = QgsSymbolLayerV2Utils::encodeColor( mFillColor ); 00330 map["fill_color_field"] = mFillColorField; 00331 map["outline_color"] = QgsSymbolLayerV2Utils::encodeColor( mOutlineColor ); 00332 map["outline_color_field"] = mOutlineColorField; 00333 map["symbol_name_field"] = mSymbolNameField; 00334 return map; 00335 } 00336 00337 bool QgsEllipseSymbolLayerV2::hasDataDefinedProperty() const 00338 { 00339 return ( mWidthIndex != -1 || mHeightIndex != -1 || mOutlineWidthIndex != -1 00340 || mFillColorIndex != -1 || mOutlineColorIndex != -1 ); 00341 } 00342 00343 void QgsEllipseSymbolLayerV2::preparePath( const QString& symbolName, QgsSymbolV2RenderContext& context, const QgsFeature* f ) 00344 { 00345 mPainterPath = QPainterPath(); 00346 00347 double width = 0; 00348 00349 if ( f && mWidthIndex != -1 ) //1. priority: data defined setting on symbol layer level 00350 { 00351 width = context.outputLineWidth( f->attributeMap()[mWidthIndex].toDouble() ); 00352 } 00353 else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level 00354 { 00355 width = context.outputLineWidth( mSize ); 00356 } 00357 else //3. priority: global width setting 00358 { 00359 width = context.outputLineWidth( mSymbolWidth ); 00360 } 00361 00362 double height = 0; 00363 if ( f && mHeightIndex != -1 ) //1. priority: data defined setting on symbol layer level 00364 { 00365 height = context.outputLineWidth( f->attributeMap()[mHeightIndex].toDouble() ); 00366 } 00367 else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level 00368 { 00369 height = context.outputLineWidth( mSize ); 00370 } 00371 else //3. priority: global height setting 00372 { 00373 height = context.outputLineWidth( mSymbolHeight ); 00374 } 00375 00376 if ( symbolName == "circle" ) 00377 { 00378 mPainterPath.addEllipse( QRectF( -width / 2.0, -height / 2.0, width, height ) ); 00379 } 00380 else if ( symbolName == "rectangle" ) 00381 { 00382 mPainterPath.addRect( QRectF( -width / 2.0, -height / 2.0, width, height ) ); 00383 } 00384 else if ( symbolName == "cross" ) 00385 { 00386 mPainterPath.moveTo( 0, -height / 2.0 ); 00387 mPainterPath.lineTo( 0, height / 2.0 ); 00388 mPainterPath.moveTo( -width / 2.0, 0 ); 00389 mPainterPath.lineTo( width / 2.0, 0 ); 00390 } 00391 else if ( symbolName == "triangle" ) 00392 { 00393 mPainterPath.moveTo( 0, -height / 2.0 ); 00394 mPainterPath.lineTo( -width / 2.0, height / 2.0 ); 00395 mPainterPath.lineTo( width / 2.0, height / 2.0 ); 00396 mPainterPath.lineTo( 0, -height / 2.0 ); 00397 } 00398 } 00399 00400 QSet<QString> QgsEllipseSymbolLayerV2::usedAttributes() const 00401 { 00402 QSet<QString> dataDefinedAttributes; 00403 if ( !mWidthField.isEmpty() ) 00404 { 00405 dataDefinedAttributes.insert( mWidthField ); 00406 } 00407 if ( !mHeightField.isEmpty() ) 00408 { 00409 dataDefinedAttributes.insert( mHeightField ); 00410 } 00411 if ( !mRotationField.isEmpty() ) 00412 { 00413 dataDefinedAttributes.insert( mRotationField ); 00414 } 00415 if ( !mOutlineWidthField.isEmpty() ) 00416 { 00417 dataDefinedAttributes.insert( mOutlineWidthField ); 00418 } 00419 if ( !mFillColorField.isEmpty() ) 00420 { 00421 dataDefinedAttributes.insert( mFillColorField ); 00422 } 00423 if ( !mOutlineColorField.isEmpty() ) 00424 { 00425 dataDefinedAttributes.insert( mOutlineColorField ); 00426 } 00427 if ( !mSymbolNameField.isEmpty() ) 00428 { 00429 dataDefinedAttributes.insert( mSymbolNameField ); 00430 } 00431 return dataDefinedAttributes; 00432 }