|
QGIS API Documentation
master-59fd5e0
|
00001 /*************************************************************************** 00002 qgsmarkersymbollayerv2.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 "qgsmarkersymbollayerv2.h" 00017 #include "qgssymbollayerv2utils.h" 00018 00019 #include "qgsexpression.h" 00020 #include "qgsrendercontext.h" 00021 #include "qgslogger.h" 00022 #include "qgssvgcache.h" 00023 00024 #include <QPainter> 00025 #include <QSvgRenderer> 00026 #include <QFileInfo> 00027 #include <QDir> 00028 #include <QDomDocument> 00029 #include <QDomElement> 00030 00031 #include <cmath> 00032 00034 00035 QgsSimpleMarkerSymbolLayerV2::QgsSimpleMarkerSymbolLayerV2( QString name, QColor color, QColor borderColor, double size, double angle, QgsSymbolV2::ScaleMethod scaleMethod ) 00036 : mOutlineWidth( 0 ), mOutlineWidthUnit( QgsSymbolV2::MM ) 00037 { 00038 mName = name; 00039 mColor = color; 00040 mBorderColor = borderColor; 00041 mSize = size; 00042 mAngle = angle; 00043 mOffset = QPointF( 0, 0 ); 00044 mScaleMethod = scaleMethod; 00045 mSizeUnit = QgsSymbolV2::MM; 00046 mOffsetUnit = QgsSymbolV2::MM; 00047 } 00048 00049 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::create( const QgsStringMap& props ) 00050 { 00051 QString name = DEFAULT_SIMPLEMARKER_NAME; 00052 QColor color = DEFAULT_SIMPLEMARKER_COLOR; 00053 QColor borderColor = DEFAULT_SIMPLEMARKER_BORDERCOLOR; 00054 double size = DEFAULT_SIMPLEMARKER_SIZE; 00055 double angle = DEFAULT_SIMPLEMARKER_ANGLE; 00056 QgsSymbolV2::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD; 00057 00058 if ( props.contains( "name" ) ) 00059 name = props["name"]; 00060 if ( props.contains( "color" ) ) 00061 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 00062 if ( props.contains( "color_border" ) ) 00063 borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] ); 00064 if ( props.contains( "size" ) ) 00065 size = props["size"].toDouble(); 00066 if ( props.contains( "angle" ) ) 00067 angle = props["angle"].toDouble(); 00068 if ( props.contains( "scale_method" ) ) 00069 scaleMethod = QgsSymbolLayerV2Utils::decodeScaleMethod( props["scale_method"] ); 00070 00071 QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size, angle, scaleMethod ); 00072 if ( props.contains( "offset" ) ) 00073 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 00074 if ( props.contains( "offset_unit" ) ) 00075 m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) ); 00076 if ( props.contains( "size_unit" ) ) 00077 m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) ); 00078 00079 if ( props.contains( "outline_width" ) ) 00080 { 00081 m->setOutlineWidth( props["outline_width"].toDouble() ); 00082 } 00083 if ( props.contains( "outline_width_unit" ) ) 00084 { 00085 m->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) ); 00086 } 00087 00088 //data defined properties 00089 if ( props.contains( "name_expression" ) ) 00090 { 00091 m->setDataDefinedProperty( "name", props["name_expression"] ); 00092 } 00093 if ( props.contains( "color_expression" ) ) 00094 { 00095 m->setDataDefinedProperty( "color", props["color_expression"] ); 00096 } 00097 if ( props.contains( "color_border_expression" ) ) 00098 { 00099 m->setDataDefinedProperty( "color_border", props["color_border_expression"] ); 00100 } 00101 if ( props.contains( "outline_width_expression" ) ) 00102 { 00103 m->setDataDefinedProperty( "outline_width", props["outline_width_expression"] ); 00104 } 00105 if ( props.contains( "size_expression" ) ) 00106 { 00107 m->setDataDefinedProperty( "size", props["size_expression"] ); 00108 } 00109 if ( props.contains( "angle_expression" ) ) 00110 { 00111 m->setDataDefinedProperty( "angle", props["angle_expression"] ); 00112 } 00113 if ( props.contains( "offset_expression" ) ) 00114 { 00115 m->setDataDefinedProperty( "offset", props["offset_expression"] ); 00116 } 00117 return m; 00118 } 00119 00120 00121 QString QgsSimpleMarkerSymbolLayerV2::layerType() const 00122 { 00123 return "SimpleMarker"; 00124 } 00125 00126 void QgsSimpleMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00127 { 00128 QColor brushColor = mColor; 00129 QColor penColor = mBorderColor; 00130 00131 brushColor.setAlphaF( mColor.alphaF() * context.alpha() ); 00132 penColor.setAlphaF( mBorderColor.alphaF() * context.alpha() ); 00133 00134 mBrush = QBrush( brushColor ); 00135 mPen = QPen( penColor ); 00136 mPen.setWidthF( mOutlineWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit ) ); 00137 00138 QColor selBrushColor = context.renderContext().selectionColor(); 00139 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mBorderColor; 00140 if ( context.alpha() < 1 ) 00141 { 00142 selBrushColor.setAlphaF( context.alpha() ); 00143 selPenColor.setAlphaF( context.alpha() ); 00144 } 00145 mSelBrush = QBrush( selBrushColor ); 00146 mSelPen = QPen( selPenColor ); 00147 mSelPen.setWidthF( mOutlineWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit ) ); 00148 00149 bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation || dataDefinedProperty( "angle" ); 00150 bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || dataDefinedProperty( "size" ); 00151 00152 // use caching only when: 00153 // - size, rotation, shape, color, border color is not data-defined 00154 // - drawing to screen (not printer) 00155 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput() 00156 && !dataDefinedProperty( "name" ) && !dataDefinedProperty( "color" ) && !dataDefinedProperty( "color_border" ) && !dataDefinedProperty( "outline_width" ) && 00157 !dataDefinedProperty( "size" ); 00158 00159 // use either QPolygonF or QPainterPath for drawing 00160 // TODO: find out whether drawing directly doesn't bring overhead - if not, use it for all shapes 00161 if ( !prepareShape() ) // drawing as a polygon 00162 { 00163 if ( preparePath() ) // drawing as a painter path 00164 { 00165 // some markers can't be drawn as a polygon (circle, cross) 00166 // For these set the selected border color to the selected color 00167 00168 if ( mName != "circle" ) 00169 mSelPen.setColor( selBrushColor ); 00170 } 00171 else 00172 { 00173 QgsDebugMsg( "unknown symbol" ); 00174 return; 00175 } 00176 } 00177 00178 QMatrix transform; 00179 00180 // scale the shape (if the size is not going to be modified) 00181 if ( !hasDataDefinedSize ) 00182 { 00183 double scaledSize = mSize * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit ); 00184 if ( mUsingCache ) 00185 scaledSize *= context.renderContext().rasterScaleFactor(); 00186 double half = scaledSize / 2.0; 00187 transform.scale( half, half ); 00188 } 00189 00190 // rotate if the rotation is not going to be changed during the rendering 00191 if ( !hasDataDefinedRotation && mAngle != 0 ) 00192 { 00193 transform.rotate( mAngle ); 00194 } 00195 00196 if ( !mPolygon.isEmpty() ) 00197 mPolygon = transform.map( mPolygon ); 00198 else 00199 mPath = transform.map( mPath ); 00200 00201 if ( mUsingCache ) 00202 { 00203 prepareCache( context ); 00204 } 00205 else 00206 { 00207 mCache = QImage(); 00208 mSelCache = QImage(); 00209 } 00210 00211 prepareExpressions( context.layer() ); 00212 } 00213 00214 00215 void QgsSimpleMarkerSymbolLayerV2::prepareCache( QgsSymbolV2RenderContext& context ) 00216 { 00217 double scaledSize = mSize * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit ); 00218 00219 // calculate necessary image size for the cache 00220 double pw = (( mPen.widthF() == 0 ? 1 : mPen.widthF() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen 00221 int imageSize = (( int ) scaledSize + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width 00222 double center = imageSize / 2.0; 00223 00224 mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied ); 00225 mCache.fill( 0 ); 00226 00227 QPainter p; 00228 p.begin( &mCache ); 00229 p.setRenderHint( QPainter::Antialiasing ); 00230 p.setBrush( mBrush ); 00231 p.setPen( mPen ); 00232 p.translate( QPointF( center, center ) ); 00233 drawMarker( &p, context ); 00234 p.end(); 00235 00236 // Construct the selected version of the Cache 00237 00238 QColor selColor = context.renderContext().selectionColor(); 00239 00240 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied ); 00241 mSelCache.fill( 0 ); 00242 00243 p.begin( &mSelCache ); 00244 p.setRenderHint( QPainter::Antialiasing ); 00245 p.setBrush( mSelBrush ); 00246 p.setPen( mSelPen ); 00247 p.translate( QPointF( center, center ) ); 00248 drawMarker( &p, context ); 00249 p.end(); 00250 00251 // Check that the selected version is different. If not, then re-render, 00252 // filling the background with the selection color and using the normal 00253 // colors for the symbol .. could be ugly! 00254 00255 if ( mSelCache == mCache ) 00256 { 00257 p.begin( &mSelCache ); 00258 p.setRenderHint( QPainter::Antialiasing ); 00259 p.fillRect( 0, 0, imageSize, imageSize, selColor ); 00260 p.setBrush( mBrush ); 00261 p.setPen( mPen ); 00262 p.translate( QPointF( center, center ) ); 00263 drawMarker( &p, context ); 00264 p.end(); 00265 } 00266 } 00267 00268 void QgsSimpleMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00269 { 00270 Q_UNUSED( context ); 00271 } 00272 00273 bool QgsSimpleMarkerSymbolLayerV2::prepareShape( QString name ) 00274 { 00275 mPolygon.clear(); 00276 00277 if ( name.isNull() ) 00278 { 00279 name = mName; 00280 } 00281 00282 if ( name == "square" || name == "rectangle" ) 00283 { 00284 mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) ); 00285 return true; 00286 } 00287 else if ( name == "diamond" ) 00288 { 00289 mPolygon << QPointF( -1, 0 ) << QPointF( 0, 1 ) 00290 << QPointF( 1, 0 ) << QPointF( 0, -1 ); 00291 return true; 00292 } 00293 else if ( name == "pentagon" ) 00294 { 00295 mPolygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) ) 00296 << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) ) 00297 << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) ) 00298 << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) ) 00299 << QPointF( 0, -1 ); 00300 return true; 00301 } 00302 else if ( name == "triangle" ) 00303 { 00304 mPolygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ); 00305 return true; 00306 } 00307 else if ( name == "equilateral_triangle" ) 00308 { 00309 mPolygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) ) 00310 << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) ) 00311 << QPointF( 0, -1 ); 00312 return true; 00313 } 00314 else if ( name == "star" ) 00315 { 00316 double sixth = 1.0 / 3; 00317 00318 mPolygon << QPointF( 0, -1 ) 00319 << QPointF( -sixth, -sixth ) 00320 << QPointF( -1, -sixth ) 00321 << QPointF( -sixth, 0 ) 00322 << QPointF( -1, 1 ) 00323 << QPointF( 0, + sixth ) 00324 << QPointF( 1, 1 ) 00325 << QPointF( + sixth, 0 ) 00326 << QPointF( 1, -sixth ) 00327 << QPointF( + sixth, -sixth ); 00328 return true; 00329 } 00330 else if ( name == "regular_star" ) 00331 { 00332 double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) ); 00333 00334 mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324 00335 << QPointF( sin( DEG2RAD( 288.0 ) ) , - cos( DEG2RAD( 288 ) ) ) // 288 00336 << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252 00337 << QPointF( sin( DEG2RAD( 216.0 ) ) , - cos( DEG2RAD( 216.0 ) ) ) // 216 00338 << QPointF( 0, inner_r ) // 180 00339 << QPointF( sin( DEG2RAD( 144.0 ) ) , - cos( DEG2RAD( 144.0 ) ) ) // 144 00340 << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108 00341 << QPointF( sin( DEG2RAD( 72.0 ) ) , - cos( DEG2RAD( 72.0 ) ) ) // 72 00342 << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36 00343 << QPointF( 0, -1 ); // 0 00344 return true; 00345 } 00346 else if ( name == "arrow" ) 00347 { 00348 mPolygon 00349 << QPointF( 0, -1 ) 00350 << QPointF( 0.5, -0.5 ) 00351 << QPointF( 0.25, -0.25 ) 00352 << QPointF( 0.25, 1 ) 00353 << QPointF( -0.25, 1 ) 00354 << QPointF( -0.25, -0.5 ) 00355 << QPointF( -0.5, -0.5 ); 00356 return true; 00357 } 00358 else if ( name == "filled_arrowhead" ) 00359 { 00360 mPolygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ); 00361 return true; 00362 } 00363 00364 return false; 00365 } 00366 00367 bool QgsSimpleMarkerSymbolLayerV2::preparePath( QString name ) 00368 { 00369 mPath = QPainterPath(); 00370 if ( name.isNull() ) 00371 { 00372 name = mName; 00373 } 00374 00375 if ( name == "circle" ) 00376 { 00377 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h 00378 return true; 00379 } 00380 else if ( name == "cross" ) 00381 { 00382 mPath.moveTo( -1, 0 ); 00383 mPath.lineTo( 1, 0 ); // horizontal 00384 mPath.moveTo( 0, -1 ); 00385 mPath.lineTo( 0, 1 ); // vertical 00386 return true; 00387 } 00388 else if ( name == "x" || name == "cross2" ) 00389 { 00390 mPath.moveTo( -1, -1 ); 00391 mPath.lineTo( 1, 1 ); 00392 mPath.moveTo( 1, -1 ); 00393 mPath.lineTo( -1, 1 ); 00394 return true; 00395 } 00396 else if ( name == "line" ) 00397 { 00398 mPath.moveTo( 0, -1 ); 00399 mPath.lineTo( 0, 1 ); // vertical line 00400 return true; 00401 } 00402 else if ( name == "arrowhead" ) 00403 { 00404 mPath.moveTo( 0, 0 ); 00405 mPath.lineTo( -1, -1 ); 00406 mPath.moveTo( 0, 0 ); 00407 mPath.lineTo( -1, 1 ); 00408 return true; 00409 } 00410 00411 return false; 00412 } 00413 00414 void QgsSimpleMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00415 { 00416 QgsRenderContext& rc = context.renderContext(); 00417 QPainter* p = rc.painter(); 00418 if ( !p ) 00419 { 00420 return; 00421 } 00422 00423 //offset 00424 double offsetX = 0; 00425 double offsetY = 0; 00426 markerOffset( context, offsetX, offsetY ); 00427 QPointF off( offsetX, offsetY ); 00428 00429 //angle 00430 double angle = mAngle; 00431 QgsExpression* angleExpression = expression( "angle" ); 00432 if ( angleExpression ) 00433 { 00434 angle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00435 } 00436 if ( angle ) 00437 off = _rotatedOffset( off, angle ); 00438 00439 //data defined shape? 00440 QgsExpression* nameExpression = expression( "name" ); 00441 if ( nameExpression ) 00442 { 00443 QString name = nameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString(); 00444 if ( !prepareShape( name ) ) // drawing as a polygon 00445 { 00446 preparePath( name ); // drawing as a painter path 00447 } 00448 } 00449 00450 if ( mUsingCache ) 00451 { 00452 // we will use cached image 00453 QImage &img = context.selected() ? mSelCache : mCache; 00454 double s = img.width() / context.renderContext().rasterScaleFactor(); 00455 p->drawImage( QRectF( point.x() - s / 2.0 + off.x(), 00456 point.y() - s / 2.0 + off.y(), 00457 s, s ), img ); 00458 } 00459 else 00460 { 00461 QMatrix transform; 00462 00463 00464 bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation || angleExpression; 00465 QgsExpression* sizeExpression = expression( "size" ); 00466 bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression; 00467 00468 // move to the desired position 00469 transform.translate( point.x() + off.x(), point.y() + off.y() ); 00470 00471 // resize if necessary 00472 if ( hasDataDefinedSize ) 00473 { 00474 double scaledSize = mSize; 00475 if ( sizeExpression ) 00476 { 00477 scaledSize = sizeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00478 } 00479 scaledSize *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit ); 00480 00481 switch ( mScaleMethod ) 00482 { 00483 case QgsSymbolV2::ScaleArea: 00484 scaledSize = sqrt( scaledSize ); 00485 break; 00486 case QgsSymbolV2::ScaleDiameter: 00487 break; 00488 } 00489 00490 double half = scaledSize / 2.0; 00491 transform.scale( half, half ); 00492 } 00493 00494 // rotate if necessary 00495 if ( angle != 0 && hasDataDefinedRotation ) 00496 { 00497 transform.rotate( angle ); 00498 } 00499 00500 QgsExpression* colorExpression = expression( "color" ); 00501 QgsExpression* colorBorderExpression = expression( "color_border" ); 00502 QgsExpression* outlineWidthExpression = expression( "outline_width" ); 00503 if ( colorExpression ) 00504 { 00505 mBrush.setColor( QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) ); 00506 } 00507 if ( colorBorderExpression ) 00508 { 00509 mPen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) ); 00510 mSelPen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) ); 00511 } 00512 if ( outlineWidthExpression ) 00513 { 00514 double outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00515 mPen.setWidthF( outlineWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit ) ); 00516 mSelPen.setWidthF( outlineWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit ) ); 00517 } 00518 00519 p->setBrush( context.selected() ? mSelBrush : mBrush ); 00520 p->setPen( context.selected() ? mSelPen : mPen ); 00521 00522 if ( !mPolygon.isEmpty() ) 00523 p->drawPolygon( transform.map( mPolygon ) ); 00524 else 00525 p->drawPath( transform.map( mPath ) ); 00526 } 00527 } 00528 00529 00530 QgsStringMap QgsSimpleMarkerSymbolLayerV2::properties() const 00531 { 00532 QgsStringMap map; 00533 map["name"] = mName; 00534 map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 00535 map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor ); 00536 map["size"] = QString::number( mSize ); 00537 map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit ); 00538 map["angle"] = QString::number( mAngle ); 00539 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00540 map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ); 00541 map["scale_method"] = QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ); 00542 map["outline_width"] = QString::number( mOutlineWidth ); 00543 map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ); 00544 00545 //data define properties 00546 saveDataDefinedProperties( map ); 00547 return map; 00548 } 00549 00550 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::clone() const 00551 { 00552 QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( mName, mColor, mBorderColor, mSize, mAngle, mScaleMethod ); 00553 m->setOffset( mOffset ); 00554 m->setSizeUnit( mSizeUnit ); 00555 m->setOffsetUnit( mOffsetUnit ); 00556 m->setOutlineWidth( mOutlineWidth ); 00557 m->setOutlineWidthUnit( mOutlineWidthUnit ); 00558 copyDataDefinedProperties( m ); 00559 return m; 00560 } 00561 00562 void QgsSimpleMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00563 { 00564 // <Graphic> 00565 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 00566 element.appendChild( graphicElem ); 00567 00568 QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mName, mColor, mBorderColor, -1, mSize ); 00569 00570 // <Rotation> 00571 QString angleFunc; 00572 bool ok; 00573 double angle = props.value( "angle", "0" ).toDouble( &ok ); 00574 if ( !ok ) 00575 { 00576 angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle ); 00577 } 00578 else if ( angle + mAngle != 0 ) 00579 { 00580 angleFunc = QString::number( angle + mAngle ); 00581 } 00582 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 00583 00584 // <Displacement> 00585 QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset ); 00586 } 00587 00588 QString QgsSimpleMarkerSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const 00589 { 00590 Q_UNUSED( mmScaleFactor ); 00591 Q_UNUSED( mapUnitScaleFactor ); 00592 #if 0 00593 QString ogrType = "3"; //default is circle 00594 if ( mName == "square" ) 00595 { 00596 ogrType = "5"; 00597 } 00598 else if ( mName == "triangle" ) 00599 { 00600 ogrType = "7"; 00601 } 00602 else if ( mName == "star" ) 00603 { 00604 ogrType = "9"; 00605 } 00606 else if ( mName == "circle" ) 00607 { 00608 ogrType = "3"; 00609 } 00610 else if ( mName == "cross" ) 00611 { 00612 ogrType = "0"; 00613 } 00614 else if ( mName == "x" || mName == "cross2" ) 00615 { 00616 ogrType = "1"; 00617 } 00618 else if ( mName == "line" ) 00619 { 00620 ogrType = "10"; 00621 } 00622 00623 QString ogrString; 00624 ogrString.append( "SYMBOL(" ); 00625 ogrString.append( "id:" ); 00626 ogrString.append( "\"" ); 00627 ogrString.append( "ogr-sym-" ); 00628 ogrString.append( ogrType ); 00629 ogrString.append( "\"" ); 00630 ogrString.append( ",c:" ); 00631 ogrString.append( mColor.name() ); 00632 ogrString.append( ",o:" ); 00633 ogrString.append( mBorderColor.name() ); 00634 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) ); 00635 ogrString.append( ")" ); 00636 return ogrString; 00637 #endif //0 00638 00639 QString ogrString; 00640 ogrString.append( "PEN(" ); 00641 ogrString.append( "c:" ); 00642 ogrString.append( mColor.name() ); 00643 ogrString.append( ",w:" ); 00644 ogrString.append( QString::number( mSize ) ); 00645 ogrString.append( "mm" ); 00646 ogrString.append( ")" ); 00647 return ogrString; 00648 } 00649 00650 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::createFromSld( QDomElement &element ) 00651 { 00652 QgsDebugMsg( "Entered." ); 00653 00654 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00655 if ( graphicElem.isNull() ) 00656 return NULL; 00657 00658 QString name = "square"; 00659 QColor color, borderColor; 00660 double borderWidth, size; 00661 00662 if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, color, borderColor, borderWidth, size ) ) 00663 return NULL; 00664 00665 double angle = 0.0; 00666 QString angleFunc; 00667 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 00668 { 00669 bool ok; 00670 double d = angleFunc.toDouble( &ok ); 00671 if ( ok ) 00672 angle = d; 00673 } 00674 00675 QPointF offset; 00676 QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset ); 00677 00678 QgsMarkerSymbolLayerV2 *m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size ); 00679 m->setAngle( angle ); 00680 m->setOffset( offset ); 00681 return m; 00682 } 00683 00684 void QgsSimpleMarkerSymbolLayerV2::drawMarker( QPainter* p, QgsSymbolV2RenderContext& context ) 00685 { 00686 Q_UNUSED( context ); 00687 00688 if ( mPolygon.count() != 0 ) 00689 { 00690 p->drawPolygon( mPolygon ); 00691 } 00692 else 00693 { 00694 p->drawPath( mPath ); 00695 } 00696 } 00697 00699 00700 00701 QgsSvgMarkerSymbolLayerV2::QgsSvgMarkerSymbolLayerV2( QString name, double size, double angle ) 00702 { 00703 mPath = QgsSymbolLayerV2Utils::symbolNameToPath( name ); 00704 mSize = size; 00705 mAngle = angle; 00706 mOffset = QPointF( 0, 0 ); 00707 mOutlineWidth = 1.0; 00708 mOutlineWidthUnit = QgsSymbolV2::MM; 00709 mFillColor = QColor( Qt::black ); 00710 mOutlineColor = QColor( Qt::black ); 00711 } 00712 00713 00714 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::create( const QgsStringMap& props ) 00715 { 00716 QString name = DEFAULT_SVGMARKER_NAME; 00717 double size = DEFAULT_SVGMARKER_SIZE; 00718 double angle = DEFAULT_SVGMARKER_ANGLE; 00719 00720 if ( props.contains( "name" ) ) 00721 name = props["name"]; 00722 if ( props.contains( "size" ) ) 00723 size = props["size"].toDouble(); 00724 if ( props.contains( "angle" ) ) 00725 angle = props["angle"].toDouble(); 00726 00727 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( name, size, angle ); 00728 00729 //we only check the svg default parameters if necessary, since it could be expensive 00730 if ( !props.contains( "fill" ) && !props.contains( "outline" ) && !props.contains( "outline-width" ) ) 00731 { 00732 QColor fillColor, outlineColor; 00733 double outlineWidth; 00734 bool hasFillParam, hasOutlineParam, hasOutlineWidthParam; 00735 QgsSvgCache::instance()->containsParams( name, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth ); 00736 if ( hasFillParam ) 00737 { 00738 m->setFillColor( fillColor ); 00739 } 00740 if ( hasOutlineParam ) 00741 { 00742 m->setOutlineColor( outlineColor ); 00743 } 00744 if ( hasOutlineWidthParam ) 00745 { 00746 m->setOutlineWidth( outlineWidth ); 00747 } 00748 } 00749 00750 if ( props.contains( "size_unit" ) ) 00751 m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) ); 00752 if ( props.contains( "offset" ) ) 00753 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 00754 if ( props.contains( "offset_unit" ) ) 00755 m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) ); 00756 if ( props.contains( "fill" ) ) 00757 m->setFillColor( QColor( props["fill"] ) ); 00758 if ( props.contains( "outline" ) ) 00759 m->setOutlineColor( QColor( props["outline"] ) ); 00760 if ( props.contains( "outline-width" ) ) 00761 m->setOutlineWidth( props["outline-width"].toDouble() ); 00762 if ( props.contains( "outline_width_unit" ) ) 00763 m->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) ); 00764 00765 //data defined properties 00766 if ( props.contains( "size_expression" ) ) 00767 { 00768 m->setDataDefinedProperty( "size", props["size_expression"] ); 00769 } 00770 if ( props.contains( "outline-width_expression" ) ) 00771 { 00772 m->setDataDefinedProperty( "outline-width", props["outline-width_expression"] ); 00773 } 00774 if ( props.contains( "angle_expression" ) ) 00775 { 00776 m->setDataDefinedProperty( "angle", props["angle_expression"] ); 00777 } 00778 if ( props.contains( "offset_expression" ) ) 00779 { 00780 m->setDataDefinedProperty( "offset", props["offset_expression"] ); 00781 } 00782 if ( props.contains( "name_expression" ) ) 00783 { 00784 m->setDataDefinedProperty( "name", props["name_expression"] ); 00785 } 00786 if ( props.contains( "fill_expression" ) ) 00787 { 00788 m->setDataDefinedProperty( "fill", props["fill_expression"] ); 00789 } 00790 if ( props.contains( "outline_expression" ) ) 00791 { 00792 m->setDataDefinedProperty( "outline", props["outline_expression"] ); 00793 } 00794 return m; 00795 } 00796 00797 void QgsSvgMarkerSymbolLayerV2::setPath( QString path ) 00798 { 00799 mPath = path; 00800 QColor fillColor, outlineColor; 00801 double outlineWidth; 00802 bool hasFillParam, hasOutlineParam, hasOutlineWidthParam; 00803 QgsSvgCache::instance()->containsParams( path, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth ); 00804 if ( hasFillParam ) 00805 { 00806 setFillColor( fillColor ); 00807 } 00808 if ( hasOutlineParam ) 00809 { 00810 setOutlineColor( outlineColor ); 00811 } 00812 if ( hasOutlineWidthParam ) 00813 { 00814 setOutlineWidth( outlineWidth ); 00815 } 00816 } 00817 00818 00819 QString QgsSvgMarkerSymbolLayerV2::layerType() const 00820 { 00821 return "SvgMarker"; 00822 } 00823 00824 void QgsSvgMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00825 { 00826 mOrigSize = mSize; // save in case the size would be data defined 00827 Q_UNUSED( context ); 00828 prepareExpressions( context.layer() ); 00829 } 00830 00831 void QgsSvgMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00832 { 00833 Q_UNUSED( context ); 00834 } 00835 00836 void QgsSvgMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00837 { 00838 QPainter* p = context.renderContext().painter(); 00839 if ( !p ) 00840 { 00841 return; 00842 } 00843 00844 double size = mSize; 00845 QgsExpression* sizeExpression = expression( "size" ); 00846 bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression; 00847 00848 if ( sizeExpression ) 00849 { 00850 size = sizeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00851 } 00852 size *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit ); 00853 00854 if ( hasDataDefinedSize ) 00855 { 00856 switch ( mScaleMethod ) 00857 { 00858 case QgsSymbolV2::ScaleArea: 00859 size = sqrt( size ); 00860 break; 00861 case QgsSymbolV2::ScaleDiameter: 00862 break; 00863 } 00864 } 00865 00866 //don't render symbols with size below one or above 10,000 pixels 00867 if (( int )size < 1 || 10000.0 < size ) 00868 { 00869 return; 00870 } 00871 00872 p->save(); 00873 00874 QPointF offset = mOffset; 00875 QgsExpression* offsetExpression = expression( "offset" ); 00876 if ( offsetExpression ) 00877 { 00878 QString offsetString = offsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString(); 00879 offset = QgsSymbolLayerV2Utils::decodePoint( offsetString ); 00880 } 00881 double offsetX = offset.x() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ); 00882 double offsetY = offset.y() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ); 00883 QPointF outputOffset( offsetX, offsetY ); 00884 00885 double angle = mAngle; 00886 QgsExpression* angleExpression = expression( "angle" ); 00887 if ( angleExpression ) 00888 { 00889 angle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00890 } 00891 if ( angle ) 00892 outputOffset = _rotatedOffset( outputOffset, angle ); 00893 p->translate( point + outputOffset ); 00894 00895 bool rotated = !qgsDoubleNear( angle, 0 ); 00896 bool drawOnScreen = qgsDoubleNear( context.renderContext().rasterScaleFactor(), 1.0, 0.1 ); 00897 if ( rotated ) 00898 p->rotate( angle ); 00899 00900 QString path = mPath; 00901 QgsExpression* nameExpression = expression( "name" ); 00902 if ( nameExpression ) 00903 { 00904 path = nameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString(); 00905 } 00906 00907 double outlineWidth = mOutlineWidth; 00908 QgsExpression* outlineWidthExpression = expression( "outline_width" ); 00909 if ( outlineWidthExpression ) 00910 { 00911 outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00912 } 00913 00914 QColor fillColor = mFillColor; 00915 QgsExpression* fillExpression = expression( "fill" ); 00916 if ( fillExpression ) 00917 { 00918 fillColor = QgsSymbolLayerV2Utils::decodeColor( fillExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ); 00919 } 00920 00921 QColor outlineColor = mOutlineColor; 00922 QgsExpression* outlineExpression = expression( "outline" ); 00923 if ( outlineExpression ) 00924 { 00925 outlineColor = QgsSymbolLayerV2Utils::decodeColor( outlineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ); 00926 } 00927 00928 00929 bool fitsInCache = true; 00930 bool usePict = true; 00931 double hwRatio = 1.0; 00932 if ( drawOnScreen && !rotated ) 00933 { 00934 usePict = false; 00935 const QImage& img = QgsSvgCache::instance()->svgAsImage( path, size, fillColor, outlineColor, outlineWidth, 00936 context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache ); 00937 if ( fitsInCache && img.width() > 1 ) 00938 { 00939 //consider transparency 00940 if ( !qgsDoubleNear( context.alpha(), 1.0 ) ) 00941 { 00942 QImage transparentImage = img.copy(); 00943 QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() ); 00944 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage ); 00945 hwRatio = ( double )transparentImage.height() / ( double )transparentImage.width(); 00946 } 00947 else 00948 { 00949 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img ); 00950 hwRatio = ( double )img.height() / ( double )img.width(); 00951 } 00952 } 00953 } 00954 00955 if ( usePict || !fitsInCache ) 00956 { 00957 p->setOpacity( context.alpha() ); 00958 const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( path, size, fillColor, outlineColor, outlineWidth, 00959 context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() ); 00960 00961 if ( pct.width() > 1 ) 00962 { 00963 p->drawPicture( 0, 0, pct ); 00964 hwRatio = ( double )pct.height() / ( double )pct.width(); 00965 } 00966 } 00967 00968 if ( context.selected() ) 00969 { 00970 QPen pen( context.renderContext().selectionColor() ); 00971 double penWidth = QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), QgsSymbolV2::MM ); 00972 if ( penWidth > size / 20 ) 00973 { 00974 // keep the pen width from covering symbol 00975 penWidth = size / 20; 00976 } 00977 double penOffset = penWidth / 2; 00978 pen.setWidth( penWidth ); 00979 p->setPen( pen ); 00980 p->setBrush( Qt::NoBrush ); 00981 double wSize = size + penOffset; 00982 double hSize = size * hwRatio + penOffset; 00983 p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) ); 00984 } 00985 00986 p->restore(); 00987 } 00988 00989 00990 QgsStringMap QgsSvgMarkerSymbolLayerV2::properties() const 00991 { 00992 QgsStringMap map; 00993 map["name"] = QgsSymbolLayerV2Utils::symbolPathToName( mPath ); 00994 map["size"] = QString::number( mSize ); 00995 map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit ); 00996 map["angle"] = QString::number( mAngle ); 00997 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00998 map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ); 00999 map["fill"] = mFillColor.name(); 01000 map["outline"] = mOutlineColor.name(); 01001 map["outline-width"] = QString::number( mOutlineWidth ); 01002 map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ); 01003 saveDataDefinedProperties( map ); 01004 return map; 01005 } 01006 01007 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::clone() const 01008 { 01009 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( mPath, mSize, mAngle ); 01010 m->setFillColor( mFillColor ); 01011 m->setOutlineColor( mOutlineColor ); 01012 m->setOutlineWidth( mOutlineWidth ); 01013 m->setOutlineWidthUnit( mOutlineWidthUnit ); 01014 m->setOffset( mOffset ); 01015 m->setOffsetUnit( mOffsetUnit ); 01016 m->setSizeUnit( mSizeUnit ); 01017 copyDataDefinedProperties( m ); 01018 return m; 01019 } 01020 01021 void QgsSvgMarkerSymbolLayerV2::setOutputUnit( QgsSymbolV2::OutputUnit unit ) 01022 { 01023 mSizeUnit = unit; 01024 mOffsetUnit = unit; 01025 mOutlineWidthUnit = unit; 01026 } 01027 01028 QgsSymbolV2::OutputUnit QgsSvgMarkerSymbolLayerV2::outputUnit() const 01029 { 01030 QgsSymbolV2::OutputUnit unit = mSizeUnit; 01031 if ( unit != mOffsetUnit || unit != mOutlineWidthUnit ) 01032 { 01033 return QgsSymbolV2::Mixed; 01034 } 01035 return unit; 01036 } 01037 01038 void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 01039 { 01040 // <Graphic> 01041 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 01042 element.appendChild( graphicElem ); 01043 01044 QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mFillColor, mSize ); 01045 01046 // <Rotation> 01047 QString angleFunc; 01048 bool ok; 01049 double angle = props.value( "angle", "0" ).toDouble( &ok ); 01050 if ( !ok ) 01051 { 01052 angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle ); 01053 } 01054 else if ( angle + mAngle != 0 ) 01055 { 01056 angleFunc = QString::number( angle + mAngle ); 01057 } 01058 01059 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 01060 01061 // <Displacement> 01062 QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset ); 01063 } 01064 01065 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::createFromSld( QDomElement &element ) 01066 { 01067 QgsDebugMsg( "Entered." ); 01068 01069 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 01070 if ( graphicElem.isNull() ) 01071 return NULL; 01072 01073 QString path, mimeType; 01074 QColor fillColor; 01075 double size; 01076 01077 if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) ) 01078 return NULL; 01079 01080 if ( mimeType != "image/svg+xml" ) 01081 return NULL; 01082 01083 double angle = 0.0; 01084 QString angleFunc; 01085 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 01086 { 01087 bool ok; 01088 double d = angleFunc.toDouble( &ok ); 01089 if ( ok ) 01090 angle = d; 01091 } 01092 01093 QPointF offset; 01094 QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset ); 01095 01096 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( path, size ); 01097 m->setFillColor( fillColor ); 01098 //m->setOutlineColor( outlineColor ); 01099 //m->setOutlineWidth( outlineWidth ); 01100 m->setAngle( angle ); 01101 m->setOffset( offset ); 01102 return m; 01103 } 01104 01106 01107 QgsFontMarkerSymbolLayerV2::QgsFontMarkerSymbolLayerV2( QString fontFamily, QChar chr, double pointSize, QColor color, double angle ) 01108 { 01109 mFontFamily = fontFamily; 01110 mChr = chr; 01111 mColor = color; 01112 mAngle = angle; 01113 mSize = pointSize; 01114 mSizeUnit = QgsSymbolV2::MM; 01115 mOffset = QPointF( 0, 0 ); 01116 mOffsetUnit = QgsSymbolV2::MM; 01117 } 01118 01119 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::create( const QgsStringMap& props ) 01120 { 01121 QString fontFamily = DEFAULT_FONTMARKER_FONT; 01122 QChar chr = DEFAULT_FONTMARKER_CHR; 01123 double pointSize = DEFAULT_FONTMARKER_SIZE; 01124 QColor color = DEFAULT_FONTMARKER_COLOR; 01125 double angle = DEFAULT_FONTMARKER_ANGLE; 01126 01127 if ( props.contains( "font" ) ) 01128 fontFamily = props["font"]; 01129 if ( props.contains( "chr" ) && props["chr"].length() > 0 ) 01130 chr = props["chr"].at( 0 ); 01131 if ( props.contains( "size" ) ) 01132 pointSize = props["size"].toDouble(); 01133 if ( props.contains( "color" ) ) 01134 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 01135 if ( props.contains( "angle" ) ) 01136 angle = props["angle"].toDouble(); 01137 01138 QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, pointSize, color, angle ); 01139 if ( props.contains( "offset" ) ) 01140 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 01141 if ( props.contains( "offset_unit" ) ) 01142 m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit" ] ) ); 01143 if ( props.contains( "size_unit" ) ) 01144 m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) ); 01145 return m; 01146 } 01147 01148 QString QgsFontMarkerSymbolLayerV2::layerType() const 01149 { 01150 return "FontMarker"; 01151 } 01152 01153 void QgsFontMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 01154 { 01155 mFont = QFont( mFontFamily ); 01156 mFont.setPixelSize( mSize * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit ) ); 01157 QFontMetrics fm( mFont ); 01158 mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 ); 01159 01160 mOrigSize = mSize; // save in case the size would be data defined 01161 } 01162 01163 void QgsFontMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 01164 { 01165 Q_UNUSED( context ); 01166 } 01167 01168 void QgsFontMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 01169 { 01170 QPainter* p = context.renderContext().painter(); 01171 QColor penColor = context.selected() ? context.renderContext().selectionColor() : mColor; 01172 penColor.setAlphaF( mColor.alphaF() * context.alpha() ); 01173 p->setPen( penColor ); 01174 p->setFont( mFont ); 01175 01176 01177 p->save(); 01178 double offsetX = mOffset.x() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ); 01179 double offsetY = mOffset.y() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ); 01180 QPointF outputOffset( offsetX, offsetY ); 01181 if ( mAngle ) 01182 outputOffset = _rotatedOffset( outputOffset, mAngle ); 01183 p->translate( point + outputOffset ); 01184 01185 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) 01186 { 01187 double s = mSize / mOrigSize; 01188 p->scale( s, s ); 01189 } 01190 01191 if ( mAngle != 0 ) 01192 p->rotate( mAngle ); 01193 01194 p->drawText( -mChrOffset, mChr ); 01195 p->restore(); 01196 } 01197 01198 QgsStringMap QgsFontMarkerSymbolLayerV2::properties() const 01199 { 01200 QgsStringMap props; 01201 props["font"] = mFontFamily; 01202 props["chr"] = mChr; 01203 props["size"] = QString::number( mSize ); 01204 props["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit ); 01205 props["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 01206 props["angle"] = QString::number( mAngle ); 01207 props["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 01208 props["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ); 01209 return props; 01210 } 01211 01212 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::clone() const 01213 { 01214 QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( mFontFamily, mChr, mSize, mColor, mAngle ); 01215 m->setOffset( mOffset ); 01216 m->setOffsetUnit( mOffsetUnit ); 01217 m->setSizeUnit( mSizeUnit ); 01218 return m; 01219 } 01220 01221 void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 01222 { 01223 // <Graphic> 01224 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 01225 element.appendChild( graphicElem ); 01226 01227 QString fontPath = QString( "ttf://%1" ).arg( mFontFamily ); 01228 int markIndex = mChr.unicode(); 01229 QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize ); 01230 01231 // <Rotation> 01232 QString angleFunc; 01233 bool ok; 01234 double angle = props.value( "angle", "0" ).toDouble( &ok ); 01235 if ( !ok ) 01236 { 01237 angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle ); 01238 } 01239 else if ( angle + mAngle != 0 ) 01240 { 01241 angleFunc = QString::number( angle + mAngle ); 01242 } 01243 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 01244 01245 // <Displacement> 01246 QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset ); 01247 } 01248 01249 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::createFromSld( QDomElement &element ) 01250 { 01251 QgsDebugMsg( "Entered." ); 01252 01253 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 01254 if ( graphicElem.isNull() ) 01255 return NULL; 01256 01257 QString name, format; 01258 QColor color; 01259 double size; 01260 int chr; 01261 01262 if ( !QgsSymbolLayerV2Utils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) ) 01263 return NULL; 01264 01265 if ( !name.startsWith( "ttf://" ) || format != "ttf" ) 01266 return NULL; 01267 01268 QString fontFamily = name.mid( 6 ); 01269 01270 double angle = 0.0; 01271 QString angleFunc; 01272 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 01273 { 01274 bool ok; 01275 double d = angleFunc.toDouble( &ok ); 01276 if ( ok ) 01277 angle = d; 01278 } 01279 01280 QPointF offset; 01281 QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset ); 01282 01283 QgsMarkerSymbolLayerV2 *m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, size, color ); 01284 m->setAngle( angle ); 01285 m->setOffset( offset ); 01286 return m; 01287 }