Quantum GIS API Documentation
1.8
|
00001 /*************************************************************************** 00002 qgsmarkersymbollayerv2.cpp 00003 --------------------- 00004 begin : November 2009 00005 copyright : (C) 2009 by Martin Dobias 00006 email : wonder.sk at gmail.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 "qgsrendercontext.h" 00020 #include "qgsapplication.h" 00021 #include "qgslogger.h" 00022 #include "qgsproject.h" 00023 #include "qgssvgcache.h" 00024 00025 #include <QPainter> 00026 #include <QSvgRenderer> 00027 #include <QFileInfo> 00028 #include <QDir> 00029 #include <QDomDocument> 00030 #include <QDomElement> 00031 00032 #include <cmath> 00033 00034 // MSVC compiler doesn't have defined M_PI in math.h 00035 #ifndef M_PI 00036 #define M_PI 3.14159265358979323846 00037 #endif 00038 00039 #define DEG2RAD(x) ((x)*M_PI/180) 00040 00041 00042 static QPointF _rotatedOffset( const QPointF& offset, double angle ) 00043 { 00044 angle = DEG2RAD( angle ); 00045 double c = cos( angle ), s = sin( angle ); 00046 return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c ); 00047 } 00048 00050 00051 QgsSimpleMarkerSymbolLayerV2::QgsSimpleMarkerSymbolLayerV2( QString name, QColor color, QColor borderColor, double size, double angle ) 00052 { 00053 mName = name; 00054 mColor = color; 00055 mBorderColor = borderColor; 00056 mSize = size; 00057 mAngle = angle; 00058 mOffset = QPointF( 0, 0 ); 00059 } 00060 00061 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::create( const QgsStringMap& props ) 00062 { 00063 QString name = DEFAULT_SIMPLEMARKER_NAME; 00064 QColor color = DEFAULT_SIMPLEMARKER_COLOR; 00065 QColor borderColor = DEFAULT_SIMPLEMARKER_BORDERCOLOR; 00066 double size = DEFAULT_SIMPLEMARKER_SIZE; 00067 double angle = DEFAULT_SIMPLEMARKER_ANGLE; 00068 00069 if ( props.contains( "name" ) ) 00070 name = props["name"]; 00071 if ( props.contains( "color" ) ) 00072 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 00073 if ( props.contains( "color_border" ) ) 00074 borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] ); 00075 if ( props.contains( "size" ) ) 00076 size = props["size"].toDouble(); 00077 if ( props.contains( "angle" ) ) 00078 angle = props["angle"].toDouble(); 00079 00080 QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size, angle ); 00081 if ( props.contains( "offset" ) ) 00082 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 00083 return m; 00084 } 00085 00086 00087 QString QgsSimpleMarkerSymbolLayerV2::layerType() const 00088 { 00089 return "SimpleMarker"; 00090 } 00091 00092 void QgsSimpleMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00093 { 00094 QColor brushColor = mColor; 00095 QColor penColor = mBorderColor; 00096 if ( context.alpha() < 1 ) 00097 { 00098 penColor.setAlphaF( context.alpha() ); 00099 brushColor.setAlphaF( context.alpha() ); 00100 } 00101 mBrush = QBrush( brushColor ); 00102 mPen = QPen( penColor ); 00103 mPen.setWidthF( context.outputLineWidth( mPen.widthF() ) ); 00104 00105 QColor selBrushColor = context.selectionColor(); 00106 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mBorderColor; 00107 if ( context.alpha() < 1 ) 00108 { 00109 selBrushColor.setAlphaF( context.alpha() ); 00110 selPenColor.setAlphaF( context.alpha() ); 00111 } 00112 mSelBrush = QBrush( selBrushColor ); 00113 mSelPen = QPen( selPenColor ); 00114 mSelPen.setWidthF( context.outputLineWidth( mPen.widthF() ) ); 00115 00116 bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation; 00117 bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale; 00118 00119 // use caching only when: 00120 // - the size and rotation is not data-defined 00121 // - drawing to screen (not printer) 00122 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput(); 00123 00124 // use either QPolygonF or QPainterPath for drawing 00125 // TODO: find out whether drawing directly doesn't bring overhead - if not, use it for all shapes 00126 if ( !prepareShape() ) // drawing as a polygon 00127 { 00128 if ( preparePath() ) // drawing as a painter path 00129 { 00130 // some markers can't be drawn as a polygon (circle, cross) 00131 // For these set the selected border color to the selected color 00132 00133 if ( mName != "circle" ) 00134 mSelPen.setColor( selBrushColor ); 00135 } 00136 else 00137 { 00138 QgsDebugMsg( "unknown symbol" ); 00139 return; 00140 } 00141 } 00142 00143 QMatrix transform; 00144 00145 // scale the shape (if the size is not going to be modified) 00146 if ( !hasDataDefinedSize ) 00147 { 00148 double scaledSize = context.outputLineWidth( mSize ); 00149 if ( mUsingCache ) 00150 scaledSize *= context.renderContext().rasterScaleFactor(); 00151 double half = scaledSize / 2.0; 00152 transform.scale( half, half ); 00153 } 00154 00155 // rotate if the rotation is not going to be changed during the rendering 00156 if ( !hasDataDefinedRotation && mAngle != 0 ) 00157 { 00158 transform.rotate( mAngle ); 00159 } 00160 00161 if ( !mPolygon.isEmpty() ) 00162 mPolygon = transform.map( mPolygon ); 00163 else 00164 mPath = transform.map( mPath ); 00165 00166 if ( mUsingCache ) 00167 { 00168 prepareCache( context ); 00169 } 00170 else 00171 { 00172 mCache = QImage(); 00173 mSelCache = QImage(); 00174 } 00175 } 00176 00177 00178 void QgsSimpleMarkerSymbolLayerV2::prepareCache( QgsSymbolV2RenderContext& context ) 00179 { 00180 double scaledSize = context.outputPixelSize( mSize ); 00181 00182 // calculate necessary image size for the cache 00183 double pw = (( mPen.widthF() == 0 ? 1 : mPen.widthF() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen 00184 int imageSize = (( int ) scaledSize + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width 00185 00186 double center = (( double ) imageSize / 2 ) + 0.5; // add 1/2 pixel for proper rounding when the figure's coordinates are added 00187 00188 mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied ); 00189 mCache.fill( 0 ); 00190 00191 QPainter p; 00192 p.begin( &mCache ); 00193 p.setRenderHint( QPainter::Antialiasing ); 00194 p.setBrush( mBrush ); 00195 p.setPen( mPen ); 00196 p.translate( QPointF( center, center ) ); 00197 drawMarker( &p, context ); 00198 p.end(); 00199 00200 // Construct the selected version of the Cache 00201 00202 QColor selColor = context.selectionColor(); 00203 00204 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied ); 00205 mSelCache.fill( 0 ); 00206 00207 p.begin( &mSelCache ); 00208 p.setRenderHint( QPainter::Antialiasing ); 00209 p.setBrush( mSelBrush ); 00210 p.setPen( mSelPen ); 00211 p.translate( QPointF( center, center ) ); 00212 drawMarker( &p, context ); 00213 p.end(); 00214 00215 // Check that the selected version is different. If not, then re-render, 00216 // filling the background with the selection color and using the normal 00217 // colors for the symbol .. could be ugly! 00218 00219 if ( mSelCache == mCache ) 00220 { 00221 p.begin( &mSelCache ); 00222 p.setRenderHint( QPainter::Antialiasing ); 00223 p.fillRect( 0, 0, imageSize, imageSize, selColor ); 00224 p.setBrush( mBrush ); 00225 p.setPen( mPen ); 00226 p.translate( QPointF( center, center ) ); 00227 drawMarker( &p, context ); 00228 p.end(); 00229 } 00230 } 00231 00232 void QgsSimpleMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00233 { 00234 Q_UNUSED( context ); 00235 } 00236 00237 bool QgsSimpleMarkerSymbolLayerV2::prepareShape() 00238 { 00239 mPolygon.clear(); 00240 00241 if ( mName == "square" || mName == "rectangle" ) 00242 { 00243 mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) ); 00244 return true; 00245 } 00246 else if ( mName == "diamond" ) 00247 { 00248 mPolygon << QPointF( -1, 0 ) << QPointF( 0, 1 ) 00249 << QPointF( 1, 0 ) << QPointF( 0, -1 ); 00250 return true; 00251 } 00252 else if ( mName == "pentagon" ) 00253 { 00254 mPolygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) ) 00255 << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) ) 00256 << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) ) 00257 << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) ) 00258 << QPointF( 0, -1 ); 00259 return true; 00260 } 00261 else if ( mName == "triangle" ) 00262 { 00263 mPolygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ); 00264 return true; 00265 } 00266 else if ( mName == "equilateral_triangle" ) 00267 { 00268 mPolygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) ) 00269 << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) ) 00270 << QPointF( 0, -1 ); 00271 return true; 00272 } 00273 else if ( mName == "star" ) 00274 { 00275 double sixth = 1.0 / 3; 00276 00277 mPolygon << QPointF( 0, -1 ) 00278 << QPointF( -sixth, -sixth ) 00279 << QPointF( -1, -sixth ) 00280 << QPointF( -sixth, 0 ) 00281 << QPointF( -1, 1 ) 00282 << QPointF( 0, + sixth ) 00283 << QPointF( 1, 1 ) 00284 << QPointF( + sixth, 0 ) 00285 << QPointF( 1, -sixth ) 00286 << QPointF( + sixth, -sixth ); 00287 return true; 00288 } 00289 else if ( mName == "regular_star" ) 00290 { 00291 double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) ); 00292 00293 mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324 00294 << QPointF( sin( DEG2RAD( 288.0 ) ) , - cos( DEG2RAD( 288 ) ) ) // 288 00295 << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252 00296 << QPointF( sin( DEG2RAD( 216.0 ) ) , - cos( DEG2RAD( 216.0 ) ) ) // 216 00297 << QPointF( 0, inner_r ) // 180 00298 << QPointF( sin( DEG2RAD( 144.0 ) ) , - cos( DEG2RAD( 144.0 ) ) ) // 144 00299 << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108 00300 << QPointF( sin( DEG2RAD( 72.0 ) ) , - cos( DEG2RAD( 72.0 ) ) ) // 72 00301 << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36 00302 << QPointF( 0, -1 ); // 0 00303 return true; 00304 } 00305 else if ( mName == "arrow" ) 00306 { 00307 mPolygon 00308 << QPointF( 0, -1 ) 00309 << QPointF( 0.5, -0.5 ) 00310 << QPointF( 0.25, -0.25 ) 00311 << QPointF( 0.25, 1 ) 00312 << QPointF( -0.25, 1 ) 00313 << QPointF( -0.25, -0.5 ) 00314 << QPointF( -0.5, -0.5 ); 00315 return true; 00316 } 00317 else if ( mName == "filled_arrowhead" ) 00318 { 00319 mPolygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ); 00320 return true; 00321 } 00322 00323 return false; 00324 } 00325 00326 bool QgsSimpleMarkerSymbolLayerV2::preparePath() 00327 { 00328 mPath = QPainterPath(); 00329 00330 if ( mName == "circle" ) 00331 { 00332 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h 00333 return true; 00334 } 00335 else if ( mName == "cross" ) 00336 { 00337 mPath.moveTo( -1, 0 ); 00338 mPath.lineTo( 1, 0 ); // horizontal 00339 mPath.moveTo( 0, -1 ); 00340 mPath.lineTo( 0, 1 ); // vertical 00341 return true; 00342 } 00343 else if ( mName == "x" || mName == "cross2" ) 00344 { 00345 mPath.moveTo( -1, -1 ); 00346 mPath.lineTo( 1, 1 ); 00347 mPath.moveTo( 1, -1 ); 00348 mPath.lineTo( -1, 1 ); 00349 return true; 00350 } 00351 else if ( mName == "line" ) 00352 { 00353 mPath.moveTo( 0, -1 ); 00354 mPath.lineTo( 0, 1 ); // vertical line 00355 return true; 00356 } 00357 else if ( mName == "arrowhead" ) 00358 { 00359 mPath.moveTo( 0, 0 ); 00360 mPath.lineTo( -1, -1 ); 00361 mPath.moveTo( 0, 0 ); 00362 mPath.lineTo( -1, 1 ); 00363 return true; 00364 } 00365 00366 return false; 00367 } 00368 00369 void QgsSimpleMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00370 { 00371 QgsRenderContext& rc = context.renderContext(); 00372 QPainter* p = rc.painter(); 00373 if ( !p ) 00374 { 00375 return; 00376 } 00377 00378 QPointF off( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) ); 00379 if ( mAngle ) 00380 off = _rotatedOffset( off, mAngle ); 00381 00382 if ( mUsingCache ) 00383 { 00384 // we will use cached image 00385 QImage &img = context.selected() ? mSelCache : mCache; 00386 double s = img.width() / context.renderContext().rasterScaleFactor(); 00387 p->drawImage( QRectF( point.x() - s / 2.0 + off.x(), 00388 point.y() - s / 2.0 + off.y(), 00389 s, s ), img ); 00390 } 00391 else 00392 { 00393 QMatrix transform; 00394 00395 bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation; 00396 bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale; 00397 00398 // move to the desired position 00399 transform.translate( point.x() + off.x(), point.y() + off.y() ); 00400 00401 // resize if necessary 00402 if ( hasDataDefinedSize ) 00403 { 00404 double scaledSize = context.outputLineWidth( mSize ); 00405 double half = scaledSize / 2.0; 00406 transform.scale( half, half ); 00407 } 00408 00409 // rotate if necessary 00410 if ( mAngle != 0 && hasDataDefinedRotation ) 00411 { 00412 transform.rotate( mAngle ); 00413 } 00414 00415 p->setBrush( context.selected() ? mSelBrush : mBrush ); 00416 p->setPen( context.selected() ? mSelPen : mPen ); 00417 00418 if ( !mPolygon.isEmpty() ) 00419 p->drawPolygon( transform.map( mPolygon ) ); 00420 else 00421 p->drawPath( transform.map( mPath ) ); 00422 } 00423 } 00424 00425 00426 QgsStringMap QgsSimpleMarkerSymbolLayerV2::properties() const 00427 { 00428 QgsStringMap map; 00429 map["name"] = mName; 00430 map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 00431 map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor ); 00432 map["size"] = QString::number( mSize ); 00433 map["angle"] = QString::number( mAngle ); 00434 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00435 return map; 00436 } 00437 00438 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::clone() const 00439 { 00440 QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( mName, mColor, mBorderColor, mSize, mAngle ); 00441 m->setOffset( mOffset ); 00442 return m; 00443 } 00444 00445 void QgsSimpleMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00446 { 00447 // <Graphic> 00448 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 00449 element.appendChild( graphicElem ); 00450 00451 QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, mName, mColor, mBorderColor, -1, mSize ); 00452 00453 // <Rotation> 00454 QString angleFunc; 00455 bool ok; 00456 double angle = props.value( "angle", "0" ).toDouble( &ok ); 00457 if ( !ok ) 00458 { 00459 angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle ); 00460 } 00461 else if ( angle + mAngle != 0 ) 00462 { 00463 angleFunc = QString::number( angle + mAngle ); 00464 } 00465 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 00466 00467 // <Displacement> 00468 QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset ); 00469 } 00470 00471 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::createFromSld( QDomElement &element ) 00472 { 00473 QgsDebugMsg( "Entered." ); 00474 00475 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00476 if ( graphicElem.isNull() ) 00477 return NULL; 00478 00479 QString name = "square"; 00480 QColor color, borderColor; 00481 double borderWidth, size; 00482 00483 if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, color, borderColor, borderWidth, size ) ) 00484 return NULL; 00485 00486 double angle = 0.0; 00487 QString angleFunc; 00488 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 00489 { 00490 bool ok; 00491 double d = angleFunc.toDouble( &ok ); 00492 if ( ok ) 00493 angle = d; 00494 } 00495 00496 QPointF offset; 00497 QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset ); 00498 00499 QgsMarkerSymbolLayerV2 *m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size ); 00500 m->setAngle( angle ); 00501 m->setOffset( offset ); 00502 return m; 00503 } 00504 00505 void QgsSimpleMarkerSymbolLayerV2::drawMarker( QPainter* p, QgsSymbolV2RenderContext& context ) 00506 { 00507 Q_UNUSED( context ); 00508 00509 if ( mPolygon.count() != 0 ) 00510 { 00511 p->drawPolygon( mPolygon ); 00512 } 00513 else 00514 { 00515 p->drawPath( mPath ); 00516 } 00517 } 00518 00519 00521 00522 00523 QgsSvgMarkerSymbolLayerV2::QgsSvgMarkerSymbolLayerV2( QString name, double size, double angle ) 00524 { 00525 mPath = symbolNameToPath( name ); 00526 mSize = size; 00527 mAngle = angle; 00528 mOffset = QPointF( 0, 0 ); 00529 mOutlineWidth = 1.0; 00530 mFillColor = QColor( Qt::black ); 00531 mOutlineColor = QColor( Qt::black ); 00532 } 00533 00534 00535 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::create( const QgsStringMap& props ) 00536 { 00537 QString name = DEFAULT_SVGMARKER_NAME; 00538 double size = DEFAULT_SVGMARKER_SIZE; 00539 double angle = DEFAULT_SVGMARKER_ANGLE; 00540 00541 if ( props.contains( "name" ) ) 00542 name = props["name"]; 00543 if ( props.contains( "size" ) ) 00544 size = props["size"].toDouble(); 00545 if ( props.contains( "angle" ) ) 00546 angle = props["angle"].toDouble(); 00547 00548 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( name, size, angle ); 00549 00550 //we only check the svg default parameters if necessary, since it could be expensive 00551 if ( !props.contains( "fill" ) && !props.contains( "outline" ) && !props.contains( "outline-width" ) ) 00552 { 00553 QColor fillColor, outlineColor; 00554 double outlineWidth; 00555 bool hasFillParam, hasOutlineParam, hasOutlineWidthParam; 00556 QgsSvgCache::instance()->containsParams( name, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth ); 00557 if ( hasFillParam ) 00558 { 00559 m->setFillColor( fillColor ); 00560 } 00561 if ( hasOutlineParam ) 00562 { 00563 m->setOutlineColor( outlineColor ); 00564 } 00565 if ( hasOutlineWidthParam ) 00566 { 00567 m->setOutlineWidth( outlineWidth ); 00568 } 00569 } 00570 00571 if ( props.contains( "offset" ) ) 00572 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 00573 if ( props.contains( "fill" ) ) 00574 m->setFillColor( QColor( props["fill"] ) ); 00575 if ( props.contains( "outline" ) ) 00576 m->setOutlineColor( QColor( props["outline"] ) ); 00577 if ( props.contains( "outline-width" ) ) 00578 m->setOutlineWidth( props["outline-width"].toDouble() ); 00579 return m; 00580 } 00581 00582 void QgsSvgMarkerSymbolLayerV2::setPath( QString path ) 00583 { 00584 mPath = path; 00585 QColor fillColor, outlineColor; 00586 double outlineWidth; 00587 bool hasFillParam, hasOutlineParam, hasOutlineWidthParam; 00588 QgsSvgCache::instance()->containsParams( path, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth ); 00589 if ( hasFillParam ) 00590 { 00591 setFillColor( fillColor ); 00592 } 00593 if ( hasOutlineParam ) 00594 { 00595 setOutlineColor( outlineColor ); 00596 } 00597 if ( hasOutlineWidthParam ) 00598 { 00599 setOutlineWidth( outlineWidth ); 00600 } 00601 } 00602 00603 00604 QString QgsSvgMarkerSymbolLayerV2::layerType() const 00605 { 00606 return "SvgMarker"; 00607 } 00608 00609 void QgsSvgMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00610 { 00611 mOrigSize = mSize; // save in case the size would be data defined 00612 Q_UNUSED( context ); 00613 } 00614 00615 void QgsSvgMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00616 { 00617 Q_UNUSED( context ); 00618 } 00619 00620 void QgsSvgMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00621 { 00622 QPainter* p = context.renderContext().painter(); 00623 if ( !p ) 00624 { 00625 return; 00626 } 00627 00628 p->save(); 00629 QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) ); 00630 if ( mAngle ) 00631 outputOffset = _rotatedOffset( outputOffset, mAngle ); 00632 p->translate( point + outputOffset ); 00633 00634 int size = ( int )( context.outputLineWidth( mSize ) ); 00635 if ( size < 1 ) //don't render symbols with size below one pixel 00636 { 00637 return; 00638 } 00639 00640 bool rotated = !doubleNear( mAngle, 0 ); 00641 bool drawOnScreen = doubleNear( context.renderContext().rasterScaleFactor(), 1.0, 0.1 ); 00642 if ( rotated ) 00643 p->rotate( mAngle ); 00644 00645 if ( drawOnScreen && !rotated ) 00646 { 00647 const QImage& img = QgsSvgCache::instance()->svgAsImage( mPath, size, mFillColor, mOutlineColor, mOutlineWidth, 00648 context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() ); 00649 //consider transparency 00650 if ( !doubleNear( context.alpha(), 1.0 ) ) 00651 { 00652 QImage transparentImage = img.copy(); 00653 QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() ); 00654 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage ); 00655 } 00656 else 00657 { 00658 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img ); 00659 } 00660 } 00661 else 00662 { 00663 p->setOpacity( context.alpha( ) ); 00664 const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( mPath, size, mFillColor, mOutlineColor, mOutlineWidth, 00665 context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() ); 00666 p->drawPicture( 0, 0, pct ); 00667 } 00668 00669 if ( context.selected() ) 00670 { 00671 QPen pen( context.selectionColor() ); 00672 pen.setWidth( context.outputLineWidth( 1.0 ) ); 00673 p->setPen( pen ); 00674 p->setBrush( Qt::NoBrush ); 00675 double sizePixel = context.outputLineWidth( mSize ); 00676 p->drawRect( QRectF( -sizePixel / 2.0, -sizePixel / 2.0, sizePixel, sizePixel ) ); 00677 } 00678 00679 p->restore(); 00680 } 00681 00682 00683 QgsStringMap QgsSvgMarkerSymbolLayerV2::properties() const 00684 { 00685 QgsStringMap map; 00686 map["name"] = symbolPathToName( mPath ); 00687 map["size"] = QString::number( mSize ); 00688 map["angle"] = QString::number( mAngle ); 00689 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00690 map["fill"] = mFillColor.name(); 00691 map["outline"] = mOutlineColor.name(); 00692 map["outline-width"] = QString::number( mOutlineWidth ); 00693 return map; 00694 } 00695 00696 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::clone() const 00697 { 00698 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( mPath, mSize, mAngle ); 00699 m->setFillColor( mFillColor ); 00700 m->setOutlineColor( mOutlineColor ); 00701 m->setOutlineWidth( mOutlineWidth ); 00702 m->setOffset( mOffset ); 00703 return m; 00704 } 00705 00706 void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00707 { 00708 // <Graphic> 00709 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 00710 element.appendChild( graphicElem ); 00711 00712 QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mFillColor, mSize ); 00713 00714 // <Rotation> 00715 QString angleFunc; 00716 bool ok; 00717 double angle = props.value( "angle", "0" ).toDouble( &ok ); 00718 if ( !ok ) 00719 { 00720 angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle ); 00721 } 00722 else if ( angle + mAngle != 0 ) 00723 { 00724 angleFunc = QString::number( angle + mAngle ); 00725 } 00726 00727 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 00728 00729 // <Displacement> 00730 QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset ); 00731 } 00732 00733 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::createFromSld( QDomElement &element ) 00734 { 00735 QgsDebugMsg( "Entered." ); 00736 00737 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 00738 if ( graphicElem.isNull() ) 00739 return NULL; 00740 00741 QString path, mimeType; 00742 QColor fillColor; 00743 double size; 00744 00745 if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) ) 00746 return NULL; 00747 00748 if ( mimeType != "image/svg+xml" ) 00749 return NULL; 00750 00751 double angle = 0.0; 00752 QString angleFunc; 00753 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 00754 { 00755 bool ok; 00756 double d = angleFunc.toDouble( &ok ); 00757 if ( ok ) 00758 angle = d; 00759 } 00760 00761 QPointF offset; 00762 QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset ); 00763 00764 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( path, size ); 00765 m->setFillColor( fillColor ); 00766 //m->setOutlineColor( outlineColor ); 00767 //m->setOutlineWidth( outlineWidth ); 00768 m->setAngle( angle ); 00769 m->setOffset( offset ); 00770 return m; 00771 } 00772 00773 00774 QStringList QgsSvgMarkerSymbolLayerV2::listSvgFiles() 00775 { 00776 // copied from QgsMarkerCatalogue - TODO: unify 00777 QStringList list; 00778 QStringList svgPaths = QgsApplication::svgPaths(); 00779 00780 for ( int i = 0; i < svgPaths.size(); i++ ) 00781 { 00782 QDir dir( svgPaths[i] ); 00783 foreach( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) ) 00784 { 00785 svgPaths.insert( i + 1, dir.path() + "/" + item ); 00786 } 00787 00788 foreach( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) ) 00789 { 00790 // TODO test if it is correct SVG 00791 list.append( dir.path() + "/" + item ); 00792 } 00793 } 00794 return list; 00795 } 00796 00797 QString QgsSvgMarkerSymbolLayerV2::symbolNameToPath( QString name ) 00798 { 00799 // copied from QgsSymbol::setNamedPointSymbol - TODO: unify 00800 00801 // we might have a full path... 00802 if ( QFile( name ).exists() ) 00803 return QFileInfo( name ).canonicalFilePath(); 00804 00805 // SVG symbol not found - probably a relative path was used 00806 00807 QStringList svgPaths = QgsApplication::svgPaths(); 00808 for ( int i = 0; i < svgPaths.size(); i++ ) 00809 { 00810 QgsDebugMsg( "SvgPath: " + svgPaths[i] ); 00811 QFileInfo myInfo( name ); 00812 QString myFileName = myInfo.fileName(); // foo.svg 00813 QString myLowestDir = myInfo.dir().dirName(); 00814 QString myLocalPath = svgPaths[i] + "/" + myLowestDir + "/" + myFileName; 00815 00816 QgsDebugMsg( "Alternative svg path: " + myLocalPath ); 00817 if ( QFile( myLocalPath ).exists() ) 00818 { 00819 QgsDebugMsg( "Svg found in alternative path" ); 00820 return QFileInfo( myLocalPath ).canonicalFilePath(); 00821 } 00822 else if ( myInfo.isRelative() ) 00823 { 00824 QFileInfo pfi( QgsProject::instance()->fileName() ); 00825 QString alternatePath = pfi.canonicalPath() + QDir::separator() + name; 00826 if ( pfi.exists() && QFile( alternatePath ).exists() ) 00827 { 00828 QgsDebugMsg( "Svg found in alternative path" ); 00829 return QFileInfo( alternatePath ).canonicalFilePath(); 00830 } 00831 else 00832 { 00833 QgsDebugMsg( "Svg not found in project path" ); 00834 } 00835 } 00836 else 00837 { 00838 //couldnt find the file, no happy ending :-( 00839 QgsDebugMsg( "Computed alternate path but no svg there either" ); 00840 } 00841 } 00842 return QString(); 00843 } 00844 00845 QString QgsSvgMarkerSymbolLayerV2::symbolPathToName( QString path ) 00846 { 00847 // copied from QgsSymbol::writeXML 00848 00849 QFileInfo fi( path ); 00850 if ( !fi.exists() ) 00851 return path; 00852 00853 path = fi.canonicalFilePath(); 00854 00855 QStringList svgPaths = QgsApplication::svgPaths(); 00856 00857 for ( int i = 0; i < svgPaths.size(); i++ ) 00858 { 00859 QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath(); 00860 00861 if ( !dir.isEmpty() && path.startsWith( dir ) ) 00862 { 00863 path = path.mid( dir.size() ); 00864 break; 00865 } 00866 } 00867 00868 return path; 00869 } 00870 00871 00873 00874 QgsFontMarkerSymbolLayerV2::QgsFontMarkerSymbolLayerV2( QString fontFamily, QChar chr, double pointSize, QColor color, double angle ) 00875 { 00876 mFontFamily = fontFamily; 00877 mChr = chr; 00878 mColor = color; 00879 mAngle = angle; 00880 mSize = pointSize; 00881 mOffset = QPointF( 0, 0 ); 00882 } 00883 00884 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::create( const QgsStringMap& props ) 00885 { 00886 QString fontFamily = DEFAULT_FONTMARKER_FONT; 00887 QChar chr = DEFAULT_FONTMARKER_CHR; 00888 double pointSize = DEFAULT_FONTMARKER_SIZE; 00889 QColor color = DEFAULT_FONTMARKER_COLOR; 00890 double angle = DEFAULT_FONTMARKER_ANGLE; 00891 00892 if ( props.contains( "font" ) ) 00893 fontFamily = props["font"]; 00894 if ( props.contains( "chr" ) && props["chr"].length() > 0 ) 00895 chr = props["chr"].at( 0 ); 00896 if ( props.contains( "size" ) ) 00897 pointSize = props["size"].toDouble(); 00898 if ( props.contains( "color" ) ) 00899 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 00900 if ( props.contains( "angle" ) ) 00901 angle = props["angle"].toDouble(); 00902 00903 QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, pointSize, color, angle ); 00904 if ( props.contains( "offset" ) ) 00905 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 00906 return m; 00907 } 00908 00909 QString QgsFontMarkerSymbolLayerV2::layerType() const 00910 { 00911 return "FontMarker"; 00912 } 00913 00914 void QgsFontMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00915 { 00916 mFont = QFont( mFontFamily ); 00917 mFont.setPixelSize( context.outputLineWidth( mSize ) ); 00918 QFontMetrics fm( mFont ); 00919 mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 ); 00920 00921 mOrigSize = mSize; // save in case the size would be data defined 00922 } 00923 00924 void QgsFontMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00925 { 00926 Q_UNUSED( context ); 00927 } 00928 00929 void QgsFontMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00930 { 00931 QPainter* p = context.renderContext().painter(); 00932 QColor penColor = context.selected() ? context.selectionColor() : mColor; 00933 penColor.setAlphaF( context.alpha() ); 00934 p->setPen( penColor ); 00935 p->setFont( mFont ); 00936 00937 00938 p->save(); 00939 QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) ); 00940 if ( mAngle ) 00941 outputOffset = _rotatedOffset( outputOffset, mAngle ); 00942 p->translate( point + outputOffset ); 00943 00944 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) 00945 { 00946 double s = mSize / mOrigSize; 00947 p->scale( s, s ); 00948 } 00949 00950 if ( mAngle != 0 ) 00951 p->rotate( mAngle ); 00952 00953 p->drawText( -mChrOffset, mChr ); 00954 p->restore(); 00955 } 00956 00957 QgsStringMap QgsFontMarkerSymbolLayerV2::properties() const 00958 { 00959 QgsStringMap props; 00960 props["font"] = mFontFamily; 00961 props["chr"] = mChr; 00962 props["size"] = QString::number( mSize ); 00963 props["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 00964 props["angle"] = QString::number( mAngle ); 00965 props["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00966 return props; 00967 } 00968 00969 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::clone() const 00970 { 00971 QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( mFontFamily, mChr, mSize, mColor, mAngle ); 00972 m->setOffset( mOffset ); 00973 return m; 00974 } 00975 00976 void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00977 { 00978 // <Graphic> 00979 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 00980 element.appendChild( graphicElem ); 00981 00982 QString fontPath = QString( "ttf://%1" ).arg( mFontFamily ); 00983 int markIndex = mChr.unicode(); 00984 QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize ); 00985 00986 // <Rotation> 00987 QString angleFunc; 00988 bool ok; 00989 double angle = props.value( "angle", "0" ).toDouble( &ok ); 00990 if ( !ok ) 00991 { 00992 angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle ); 00993 } 00994 else if ( angle + mAngle != 0 ) 00995 { 00996 angleFunc = QString::number( angle + mAngle ); 00997 } 00998 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc ); 00999 01000 // <Displacement> 01001 QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, mOffset ); 01002 } 01003 01004 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::createFromSld( QDomElement &element ) 01005 { 01006 QgsDebugMsg( "Entered." ); 01007 01008 QDomElement graphicElem = element.firstChildElement( "Graphic" ); 01009 if ( graphicElem.isNull() ) 01010 return NULL; 01011 01012 QString name, format; 01013 QColor color; 01014 double size; 01015 int chr; 01016 01017 if ( !QgsSymbolLayerV2Utils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) ) 01018 return NULL; 01019 01020 if ( !name.startsWith( "ttf://" ) || format != "ttf" ) 01021 return NULL; 01022 01023 QString fontFamily = name.mid( 6 ); 01024 01025 double angle = 0.0; 01026 QString angleFunc; 01027 if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) ) 01028 { 01029 bool ok; 01030 double d = angleFunc.toDouble( &ok ); 01031 if ( ok ) 01032 angle = d; 01033 } 01034 01035 QPointF offset; 01036 QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, offset ); 01037 01038 QgsMarkerSymbolLayerV2 *m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, size, color ); 01039 m->setAngle( angle ); 01040 m->setOffset( offset ); 01041 return m; 01042 }