QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsellipsesymbollayerv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsellipsesymbollayerv2.cpp
3  ---------------------
4  begin : June 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
16 #include "qgsexpression.h"
17 #include "qgsfeature.h"
18 #include "qgsrendercontext.h"
19 #include "qgsvectorlayer.h"
20 #include "qgslogger.h"
21 
22 #include <QPainter>
23 #include <QSet>
24 #include <QDomDocument>
25 #include <QDomElement>
26 
27 QgsEllipseSymbolLayerV2::QgsEllipseSymbolLayerV2(): mSymbolName( "circle" ), mSymbolWidth( 4 ), mSymbolWidthUnit( QgsSymbolV2::MM ), mSymbolHeight( 3 ),
28  mSymbolHeightUnit( QgsSymbolV2::MM ), mFillColor( Qt::white ), mOutlineColor( Qt::black ), mOutlineWidth( 0 ), mOutlineWidthUnit( QgsSymbolV2::MM )
29 {
30  mPen.setColor( mOutlineColor );
31  mPen.setWidth( 1.0 );
32  mPen.setJoinStyle( Qt::MiterJoin );
33  mBrush.setColor( mFillColor );
34  mBrush.setStyle( Qt::SolidPattern );
35  mOffset = QPointF( 0, 0 );
36 
37  mAngle = 0;
38 }
39 
41 {
42 }
43 
45 {
47  if ( properties.contains( "symbol_name" ) )
48  {
49  layer->setSymbolName( properties[ "symbol_name" ] );
50  }
51  if ( properties.contains( "symbol_width" ) )
52  {
53  layer->setSymbolWidth( properties["symbol_width"].toDouble() );
54  }
55  if ( properties.contains( "symbol_width_unit" ) )
56  {
57  layer->setSymbolWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["symbol_width_unit"] ) );
58  }
59  if ( properties.contains( "symbol_height" ) )
60  {
61  layer->setSymbolHeight( properties["symbol_height"].toDouble() );
62  }
63  if ( properties.contains( "symbol_height_unit" ) )
64  {
65  layer->setSymbolHeightUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["symbol_height_unit"] ) );
66  }
67  if ( properties.contains( "angle" ) )
68  {
69  layer->setAngle( properties["angle"].toDouble() );
70  }
71  if ( properties.contains( "outline_width" ) )
72  {
73  layer->setOutlineWidth( properties["outline_width"].toDouble() );
74  }
75  if ( properties.contains( "outline_width_unit" ) )
76  {
77  layer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
78  }
79  if ( properties.contains( "fill_color" ) )
80  {
81  layer->setFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["fill_color"] ) );
82  }
83  if ( properties.contains( "outline_color" ) )
84  {
85  layer->setOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] ) );
86  }
87  if ( properties.contains( "offset" ) )
88  {
89  layer->setOffset( QgsSymbolLayerV2Utils::decodePoint( properties["offset"] ) );
90  }
91  if ( properties.contains( "offset_unit" ) )
92  {
93  layer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
94  }
95 
96  //data defined properties
97  if ( properties.contains( "width_expression" ) )
98  {
99  layer->setDataDefinedProperty( "width", properties["width_expression"] );
100  }
101  if ( properties.contains( "height_expression" ) )
102  {
103  layer->setDataDefinedProperty( "height", properties["height_expression"] );
104  }
105  if ( properties.contains( "rotation_expression" ) )
106  {
107  layer->setDataDefinedProperty( "rotation", properties["rotation_expression"] );
108  }
109  if ( properties.contains( "outline_width_expression" ) )
110  {
111  layer->setDataDefinedProperty( "outline_width", properties[ "outline_width_expression" ] );
112  }
113  if ( properties.contains( "fill_color_expression" ) )
114  {
115  layer->setDataDefinedProperty( "fill_color", properties["fill_color_expression"] );
116  }
117  if ( properties.contains( "outline_color_expression" ) )
118  {
119  layer->setDataDefinedProperty( "outline_color", properties["outline_color_expression"] );
120  }
121  if ( properties.contains( "symbol_name_expression" ) )
122  {
123  layer->setDataDefinedProperty( "symbol_name", properties["symbol_name_expression"] );
124  }
125  if ( properties.contains( "offset_expression" ) )
126  {
127  layer->setDataDefinedProperty( "offset", properties["offset_expression"] );
128  }
129 
130  //compatibility with old project file format
131  if ( !properties["width_field"].isEmpty() )
132  {
133  layer->setDataDefinedProperty( "width", properties["width_field"] );
134  }
135  if ( !properties["height_field"].isEmpty() )
136  {
137  layer->setDataDefinedProperty( "height", properties["height_field"] );
138  }
139  if ( !properties["rotation_field"].isEmpty() )
140  {
141  layer->setDataDefinedProperty( "rotation", properties["rotation_field"] );
142  }
143  if ( !properties["outline_width_field"].isEmpty() )
144  {
145  layer->setDataDefinedProperty( "outline_width", properties[ "outline_width_field" ] );
146  }
147  if ( !properties["fill_color_field"].isEmpty() )
148  {
149  layer->setDataDefinedProperty( "fill_color", properties["fill_color_field"] );
150  }
151  if ( !properties["outline_color_field"].isEmpty() )
152  {
153  layer->setDataDefinedProperty( "outline_color", properties["outline_color_field"] );
154  }
155  if ( !properties["symbol_name_field"].isEmpty() )
156  {
157  layer->setDataDefinedProperty( "symbol_name", properties["symbol_name_field"] );
158  }
159 
160  return layer;
161 }
162 
164 {
165  QgsExpression* outlineWidthExpression = expression( "outline_width" );
166  QgsExpression* fillColorExpression = expression( "fill_color" );
167  QgsExpression* outlineColorExpression = expression( "outline_color" );
168  QgsExpression* widthExpression = expression( "width" );
169  QgsExpression* heightExpression = expression( "height" );
170  QgsExpression* symbolNameExpression = expression( "symbol_name" );
171  QgsExpression* rotationExpression = expression( "rotation" );
172 
173  if ( outlineWidthExpression )
174  {
175  double width = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
177  mPen.setWidthF( width );
178  }
179  if ( fillColorExpression )
180  {
181  QString colorString = fillColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
182  mBrush.setColor( QColor( colorString ) );
183  }
184  if ( outlineColorExpression )
185  {
186  QString colorString = outlineColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
187  mPen.setColor( QColor( colorString ) );
188  }
189  if ( widthExpression || heightExpression || symbolNameExpression )
190  {
191  QString symbolName = mSymbolName;
192  if ( symbolNameExpression )
193  {
194  symbolName = symbolNameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
195  }
196  preparePath( symbolName, context, context.feature() );
197  }
198 
199  //offset
200  double offsetX = 0;
201  double offsetY = 0;
202  markerOffset( context, offsetX, offsetY );
203  QPointF off( offsetX, offsetY );
204 
205  QPainter* p = context.renderContext().painter();
206  if ( !p )
207  {
208  return;
209  }
210 
211  //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle)
212  double rotation = 0.0;
213  if ( rotationExpression )
214  {
215  rotation = rotationExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
216  }
217  else if ( !qgsDoubleNear( mAngle, 0.0 ) )
218  {
219  rotation = mAngle;
220  }
221  if ( rotation )
222  off = _rotatedOffset( off, rotation );
223 
224  QMatrix transform;
225  transform.translate( point.x() + off.x(), point.y() + off.y() );
226  if ( !qgsDoubleNear( rotation, 0.0 ) )
227  {
228  transform.rotate( rotation );
229  }
230 
231  p->setPen( mPen );
232  p->setBrush( mBrush );
233  p->drawPath( transform.map( mPainterPath ) );
234 }
235 
237 {
238  return "EllipseMarker";
239 }
240 
242 {
243  if ( !context.feature() || !hasDataDefinedProperty() )
244  {
245  preparePath( mSymbolName, context );
246  }
247  mPen.setColor( mOutlineColor );
249  mBrush.setColor( mFillColor );
250  prepareExpressions( context.layer() );
251 }
252 
254 {
255 }
256 
258 {
260 }
261 
262 void QgsEllipseSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
263 {
264  QDomElement symbolizerElem = doc.createElement( "se:PointSymbolizer" );
265  if ( !props.value( "uom", "" ).isEmpty() )
266  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
267  element.appendChild( symbolizerElem );
268 
269  // <Geometry>
270  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
271 
272  writeSldMarker( doc, symbolizerElem, props );
273 }
274 
275 void QgsEllipseSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
276 {
277  // <Graphic>
278  QDomElement graphicElem = doc.createElement( "se:Graphic" );
279  element.appendChild( graphicElem );
280 
282 
283  // store w/h factor in a <VendorOption>
284  double widthHeightFactor = mSymbolWidth / mSymbolHeight;
285  QDomElement factorElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "widthHeightFactor", QString::number( widthHeightFactor ) );
286  graphicElem.appendChild( factorElem );
287 
288  // <Rotation>
289  const QgsExpression* rotationExpression = dataDefinedProperty( "rotation" );
290  QString angleFunc = props.value( "angle", "" );
291  if ( angleFunc.isEmpty() ) // symbol has no angle set
292  {
293 
294  if ( rotationExpression )
295  angleFunc = rotationExpression->dump();
296  else if ( !qgsDoubleNear( mAngle, 0.0 ) )
297  angleFunc = QString::number( mAngle );
298  }
299  else if ( rotationExpression )
300  {
301  // the symbol has an angle and the symbol layer have a rotation
302  // property set
303  angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( rotationExpression->dump() );
304  }
305  else if ( !qgsDoubleNear( mAngle, 0.0 ) )
306  {
307  // both the symbol and the symbol layer have angle value set
308  bool ok;
309  double angle = angleFunc.toDouble( &ok );
310  if ( !ok )
311  {
312  // its a string (probably a property name or a function)
313  angleFunc = QString( "%1 + %2" ).arg( angleFunc ).arg( mAngle );
314  }
315  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
316  {
317  // it's a double value
318  angleFunc = QString::number( angle + mAngle );
319  }
320  }
321  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
322 }
323 
325 {
326  QgsDebugMsg( "Entered." );
327 
328  QDomElement graphicElem = element.firstChildElement( "Graphic" );
329  if ( graphicElem.isNull() )
330  return NULL;
331 
332  QString name = "circle";
333  QColor fillColor, borderColor;
334  double borderWidth, size;
335  double widthHeightFactor = 1.0;
336 
337  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( graphicElem );
338  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
339  {
340  if ( it.key() == "widthHeightFactor" )
341  {
342  bool ok;
343  double v = it.value().toDouble( &ok );
344  if ( ok && !qgsDoubleNear( v, 0.0 ) && v > 0 )
345  widthHeightFactor = v;
346  }
347  }
348 
349  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, borderColor, borderWidth, size ) )
350  return NULL;
351 
352  double angle = 0.0;
353  QString angleFunc;
354  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
355  {
356  bool ok;
357  double d = angleFunc.toDouble( &ok );
358  if ( ok )
359  angle = d;
360  }
361 
363  m->setSymbolName( name );
364  m->setFillColor( fillColor );
365  m->setOutlineColor( borderColor );
366  m->setOutlineWidth( borderWidth );
367  m->setSymbolWidth( size );
368  m->setSymbolHeight( size / widthHeightFactor );
369  m->setAngle( angle );
370  return m;
371 }
372 
374 {
375  QgsStringMap map;
376  map["symbol_name"] = mSymbolName;
377  map["symbol_width"] = QString::number( mSymbolWidth );
378  map["symbol_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSymbolWidthUnit );
379  map["symbol_height"] = QString::number( mSymbolHeight );
380  map["symbol_height_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSymbolHeightUnit );
381  map["angle"] = QString::number( mAngle );
382  map["outline_width"] = QString::number( mOutlineWidth );
383  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
384  map["fill_color"] = QgsSymbolLayerV2Utils::encodeColor( mFillColor );
385  map["outline_color"] = QgsSymbolLayerV2Utils::encodeColor( mOutlineColor );
386  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
389  return map;
390 }
391 
393 {
394  return ( dataDefinedProperty( "width" ) || dataDefinedProperty( "height" ) || dataDefinedProperty( "rotation" )
395  || dataDefinedProperty( "outline_width" ) || dataDefinedProperty( "fill_color" ) || dataDefinedProperty( "outline_color" )
396  || dataDefinedProperty( "symbol_name" ) || dataDefinedProperty( "offset" ) );
397 }
398 
399 void QgsEllipseSymbolLayerV2::preparePath( const QString& symbolName, QgsSymbolV2RenderContext& context, const QgsFeature* f )
400 {
401  mPainterPath = QPainterPath();
402  const QgsRenderContext& ct = context.renderContext();
403 
404  double width = 0;
405 
406  QgsExpression* widthExpression = expression( "width" );
407  if ( widthExpression ) //1. priority: data defined setting on symbol layer level
408  {
409  width = widthExpression->evaluate( const_cast<QgsFeature*>( f ) ).toDouble();
410  }
411  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level
412  {
413  width = mSize;
414  }
415  else //3. priority: global width setting
416  {
417  width = mSymbolWidth;
418  }
420 
421  double height = 0;
422  QgsExpression* heightExpression = expression( "height" );
423  if ( heightExpression ) //1. priority: data defined setting on symbol layer level
424  {
425  height = heightExpression->evaluate( const_cast<QgsFeature*>( f ) ).toDouble();
426  }
427  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) //2. priority: is data defined size on symbol level
428  {
429  height = mSize;
430  }
431  else //3. priority: global height setting
432  {
433  height = mSymbolHeight;
434  }
436 
437  if ( symbolName == "circle" )
438  {
439  mPainterPath.addEllipse( QRectF( -width / 2.0, -height / 2.0, width, height ) );
440  }
441  else if ( symbolName == "rectangle" )
442  {
443  mPainterPath.addRect( QRectF( -width / 2.0, -height / 2.0, width, height ) );
444  }
445  else if ( symbolName == "cross" )
446  {
447  mPainterPath.moveTo( 0, -height / 2.0 );
448  mPainterPath.lineTo( 0, height / 2.0 );
449  mPainterPath.moveTo( -width / 2.0, 0 );
450  mPainterPath.lineTo( width / 2.0, 0 );
451  }
452  else if ( symbolName == "triangle" )
453  {
454  mPainterPath.moveTo( 0, -height / 2.0 );
455  mPainterPath.lineTo( -width / 2.0, height / 2.0 );
456  mPainterPath.lineTo( width / 2.0, height / 2.0 );
457  mPainterPath.lineTo( 0, -height / 2.0 );
458  }
459 }
460 
462 {
463  mSymbolWidthUnit = unit;
464  mSymbolHeightUnit = unit;
465  mOutlineWidthUnit = unit;
466 }
467 
469 {
471  if ( mSymbolHeightUnit != unit || mOutlineWidthUnit != unit )
472  {
473  return QgsSymbolV2::Mixed;
474  }
475  return unit;
476 }