|
QGIS API Documentation
master-3f58142
|
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 if ( sizeExpression ) 00847 { 00848 size = sizeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00849 } 00850 size *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit ); 00851 //don't render symbols with size below one or above 10,000 pixels 00852 if (( int )size < 1 || 10000.0 < size ) 00853 { 00854 return; 00855 } 00856 00857 p->save(); 00858 00859 QPointF offset = mOffset; 00860 QgsExpression* offsetExpression = expression( "offset" ); 00861 if ( offsetExpression ) 00862 { 00863 QString offsetString = offsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString(); 00864 offset = QgsSymbolLayerV2Utils::decodePoint( offsetString ); 00865 } 00866 double offsetX = offset.x() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ); 00867 double offsetY = offset.y() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ); 00868 QPointF outputOffset( offsetX, offsetY ); 00869 00870 double angle = mAngle; 00871 QgsExpression* angleExpression = expression( "angle" ); 00872 if ( angleExpression ) 00873 { 00874 angle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00875 } 00876 if ( angle ) 00877 outputOffset = _rotatedOffset( outputOffset, angle ); 00878 p->translate( point + outputOffset ); 00879 00880 bool rotated = !qgsDoubleNear( angle, 0 ); 00881 bool drawOnScreen = qgsDoubleNear( context.renderContext().rasterScaleFactor(), 1.0, 0.1 ); 00882 if ( rotated ) 00883 p->rotate( angle ); 00884 00885 QString path = mPath; 00886 QgsExpression* nameExpression = expression( "name" ); 00887 if ( nameExpression ) 00888 { 00889 path = nameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString(); 00890 } 00891 00892 double outlineWidth = mOutlineWidth; 00893 QgsExpression* outlineWidthExpression = expression( "outline_width" ); 00894 if ( outlineWidthExpression ) 00895 { 00896 outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); 00897 } 00898 00899 QColor fillColor = mFillColor; 00900 QgsExpression* fillExpression = expression( "fill" ); 00901 if ( fillExpression ) 00902 { 00903 fillColor = QgsSymbolLayerV2Utils::decodeColor( fillExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ); 00904 } 00905 00906 QColor outlineColor = mOutlineColor; 00907 QgsExpression* outlineExpression = expression( "outline" ); 00908 if ( outlineExpression ) 00909 { 00910 outlineColor = QgsSymbolLayerV2Utils::decodeColor( outlineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ); 00911 } 00912 00913 00914 bool fitsInCache = true; 00915 bool usePict = true; 00916 double hwRatio = 1.0; 00917 if ( drawOnScreen && !rotated ) 00918 { 00919 usePict = false; 00920 const QImage& img = QgsSvgCache::instance()->svgAsImage( path, size, fillColor, outlineColor, outlineWidth, 00921 context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache ); 00922 if ( fitsInCache && img.width() > 1 ) 00923 { 00924 //consider transparency 00925 if ( !qgsDoubleNear( context.alpha(), 1.0 ) ) 00926 { 00927 QImage transparentImage = img.copy(); 00928 QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() ); 00929 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage ); 00930 hwRatio = ( double )transparentImage.height() / ( double )transparentImage.width(); 00931 } 00932 else 00933 { 00934 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img ); 00935 hwRatio = ( double )img.height() / ( double )img.width(); 00936 } 00937 } 00938 } 00939 00940 if ( usePict || !fitsInCache ) 00941 { 00942 p->setOpacity( context.alpha() ); 00943 const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( path, size, fillColor, outlineColor, outlineWidth, 00944 context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() ); 00945 00946 if ( pct.width() > 1 ) 00947 { 00948 p->drawPicture( 0, 0, pct ); 00949 hwRatio = ( double )pct.height() / ( double )pct.width(); 00950 } 00951 } 00952 00953 if ( context.selected() ) 00954 { 00955 QPen pen( context.renderContext().selectionColor() ); 00956 double penWidth = QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), QgsSymbolV2::MM ); 00957 if ( penWidth > size / 20 ) 00958 { 00959 // keep the pen width from covering symbol 00960 penWidth = size / 20; 00961 } 00962 double penOffset = penWidth / 2; 00963 pen.setWidth( penWidth ); 00964 p->setPen( pen ); 00965 p->setBrush( Qt::NoBrush ); 00966 double wSize = size + penOffset; 00967 double hSize = size * hwRatio + penOffset; 00968 p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) ); 00969 } 00970 00971 p->restore(); 00972 } 00973 00974 00975 QgsStringMap QgsSvgMarkerSymbolLayerV2::properties() const 00976 { 00977 QgsStringMap map; 00978 map["name"] = QgsSymbolLayerV2Utils::symbolPathToName( mPath ); 00979 map["size"] = QString::number( mSize ); 00980 map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit ); 00981 map["angle"] = QString::number( mAngle ); 00982 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00983 map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ); 00984 map["fill"] = mFillColor.name(); 00985 map["outline"] = mOutlineColor.name(); 00986 map["outline-width"] = QString::number( mOutlineWidth ); 00987 map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ); 00988 saveDataDefinedProperties( map ); 00989 return map; 00990 } 00991 00992 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::clone() const 00993 { 00994 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( mPath, mSize, mAngle ); 00995 m->setFillColor( mFillColor ); 00996 m->setOutlineColor( mOutlineColor ); 00997 m->setOutlineWidth( mOutlineWidth ); 00998 m->setOutlineWidthUnit( mOutlineWidthUnit ); 00999 m->setOffset( mOffset ); 01000 m->setOffsetUnit( mOffsetUnit ); 01001 m->setSizeUnit( mSizeUnit ); 01002 copyDataDefinedProperties( m ); 01003 return m; 01004 } 01005 01006 void QgsSvgMarkerSymbolLayerV2::setOutputUnit( QgsSymbolV2::OutputUnit unit ) 01007 { 01008 mSizeUnit = unit; 01009 mOffsetUnit = unit; 01010 mOutlineWidthUnit = unit; 01011 } 01012 01013 QgsSymbolV2::OutputUnit QgsSvgMarkerSymbolLayerV2::outputUnit() const 01014 { 01015 QgsSymbolV2::OutputUnit unit = mSizeUnit; 01016 if ( unit != mOffsetUnit || unit != mOutlineWidthUnit ) 01017 { 01018 return QgsSymbolV2::Mixed; 01019 } 01020 return unit; 01021 } 01022 01023 void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 01024 { 01025 // <Graphic> 01026 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 01027 element.appendChild( graphicElem ); 01028 01029 QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mFillColor, mSize ); 01030 01031 // <Rotation> 01032 QString angleFunc; 01033 bool ok; 01034 double angle = props.value( "angle", "0" ).toDouble( &ok ); 01035 if ( !ok ) 01036 { 01037 angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle ); 01038 } 01039 else if ( angle + mAngle != 0 ) 01040 { 01041 angleFunc = QString::number( angle + mAngle ); 01042 } 01043 01044 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 01045 01046 // <Displacement> 01047 QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset ); 01048 } 01049 01050 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::createFromSld( QDomElement &element ) 01051 { 01052 QgsDebugMsg( "Entered." ); 01053 01054 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 01055 if ( graphicElem.isNull() ) 01056 return NULL; 01057 01058 QString path, mimeType; 01059 QColor fillColor; 01060 double size; 01061 01062 if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) ) 01063 return NULL; 01064 01065 if ( mimeType != "image/svg+xml" ) 01066 return NULL; 01067 01068 double angle = 0.0; 01069 QString angleFunc; 01070 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 01071 { 01072 bool ok; 01073 double d = angleFunc.toDouble( &ok ); 01074 if ( ok ) 01075 angle = d; 01076 } 01077 01078 QPointF offset; 01079 QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset ); 01080 01081 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( path, size ); 01082 m->setFillColor( fillColor ); 01083 //m->setOutlineColor( outlineColor ); 01084 //m->setOutlineWidth( outlineWidth ); 01085 m->setAngle( angle ); 01086 m->setOffset( offset ); 01087 return m; 01088 } 01089 01091 01092 QgsFontMarkerSymbolLayerV2::QgsFontMarkerSymbolLayerV2( QString fontFamily, QChar chr, double pointSize, QColor color, double angle ) 01093 { 01094 mFontFamily = fontFamily; 01095 mChr = chr; 01096 mColor = color; 01097 mAngle = angle; 01098 mSize = pointSize; 01099 mSizeUnit = QgsSymbolV2::MM; 01100 mOffset = QPointF( 0, 0 ); 01101 mOffsetUnit = QgsSymbolV2::MM; 01102 } 01103 01104 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::create( const QgsStringMap& props ) 01105 { 01106 QString fontFamily = DEFAULT_FONTMARKER_FONT; 01107 QChar chr = DEFAULT_FONTMARKER_CHR; 01108 double pointSize = DEFAULT_FONTMARKER_SIZE; 01109 QColor color = DEFAULT_FONTMARKER_COLOR; 01110 double angle = DEFAULT_FONTMARKER_ANGLE; 01111 01112 if ( props.contains( "font" ) ) 01113 fontFamily = props["font"]; 01114 if ( props.contains( "chr" ) && props["chr"].length() > 0 ) 01115 chr = props["chr"].at( 0 ); 01116 if ( props.contains( "size" ) ) 01117 pointSize = props["size"].toDouble(); 01118 if ( props.contains( "color" ) ) 01119 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 01120 if ( props.contains( "angle" ) ) 01121 angle = props["angle"].toDouble(); 01122 01123 QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, pointSize, color, angle ); 01124 if ( props.contains( "offset" ) ) 01125 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 01126 if ( props.contains( "offset_unit" ) ) 01127 m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit" ] ) ); 01128 if ( props.contains( "size_unit" ) ) 01129 m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) ); 01130 return m; 01131 } 01132 01133 QString QgsFontMarkerSymbolLayerV2::layerType() const 01134 { 01135 return "FontMarker"; 01136 } 01137 01138 void QgsFontMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 01139 { 01140 mFont = QFont( mFontFamily ); 01141 mFont.setPixelSize( mSize * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit ) ); 01142 QFontMetrics fm( mFont ); 01143 mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 ); 01144 01145 mOrigSize = mSize; // save in case the size would be data defined 01146 } 01147 01148 void QgsFontMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 01149 { 01150 Q_UNUSED( context ); 01151 } 01152 01153 void QgsFontMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 01154 { 01155 QPainter* p = context.renderContext().painter(); 01156 QColor penColor = context.selected() ? context.renderContext().selectionColor() : mColor; 01157 penColor.setAlphaF( mColor.alphaF() * context.alpha() ); 01158 p->setPen( penColor ); 01159 p->setFont( mFont ); 01160 01161 01162 p->save(); 01163 double offsetX = mOffset.x() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ); 01164 double offsetY = mOffset.y() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ); 01165 QPointF outputOffset( offsetX, offsetY ); 01166 if ( mAngle ) 01167 outputOffset = _rotatedOffset( outputOffset, mAngle ); 01168 p->translate( point + outputOffset ); 01169 01170 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) 01171 { 01172 double s = mSize / mOrigSize; 01173 p->scale( s, s ); 01174 } 01175 01176 if ( mAngle != 0 ) 01177 p->rotate( mAngle ); 01178 01179 p->drawText( -mChrOffset, mChr ); 01180 p->restore(); 01181 } 01182 01183 QgsStringMap QgsFontMarkerSymbolLayerV2::properties() const 01184 { 01185 QgsStringMap props; 01186 props["font"] = mFontFamily; 01187 props["chr"] = mChr; 01188 props["size"] = QString::number( mSize ); 01189 props["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit ); 01190 props["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 01191 props["angle"] = QString::number( mAngle ); 01192 props["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 01193 props["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ); 01194 return props; 01195 } 01196 01197 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::clone() const 01198 { 01199 QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( mFontFamily, mChr, mSize, mColor, mAngle ); 01200 m->setOffset( mOffset ); 01201 m->setOffsetUnit( mOffsetUnit ); 01202 m->setSizeUnit( mSizeUnit ); 01203 return m; 01204 } 01205 01206 void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 01207 { 01208 // <Graphic> 01209 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 01210 element.appendChild( graphicElem ); 01211 01212 QString fontPath = QString( "ttf://%1" ).arg( mFontFamily ); 01213 int markIndex = mChr.unicode(); 01214 QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize ); 01215 01216 // <Rotation> 01217 QString angleFunc; 01218 bool ok; 01219 double angle = props.value( "angle", "0" ).toDouble( &ok ); 01220 if ( !ok ) 01221 { 01222 angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle ); 01223 } 01224 else if ( angle + mAngle != 0 ) 01225 { 01226 angleFunc = QString::number( angle + mAngle ); 01227 } 01228 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 01229 01230 // <Displacement> 01231 QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset ); 01232 } 01233 01234 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::createFromSld( QDomElement &element ) 01235 { 01236 QgsDebugMsg( "Entered." ); 01237 01238 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 01239 if ( graphicElem.isNull() ) 01240 return NULL; 01241 01242 QString name, format; 01243 QColor color; 01244 double size; 01245 int chr; 01246 01247 if ( !QgsSymbolLayerV2Utils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) ) 01248 return NULL; 01249 01250 if ( !name.startsWith( "ttf://" ) || format != "ttf" ) 01251 return NULL; 01252 01253 QString fontFamily = name.mid( 6 ); 01254 01255 double angle = 0.0; 01256 QString angleFunc; 01257 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 01258 { 01259 bool ok; 01260 double d = angleFunc.toDouble( &ok ); 01261 if ( ok ) 01262 angle = d; 01263 } 01264 01265 QPointF offset; 01266 QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset ); 01267 01268 QgsMarkerSymbolLayerV2 *m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, size, color ); 01269 m->setAngle( angle ); 01270 m->setOffset( offset ); 01271 return m; 01272 }