Quantum GIS API Documentation
1.7.4
|
00001 00002 #include "qgsmarkersymbollayerv2.h" 00003 #include "qgssymbollayerv2utils.h" 00004 00005 #include "qgsrendercontext.h" 00006 #include "qgsapplication.h" 00007 #include "qgslogger.h" 00008 #include "qgsproject.h" 00009 00010 #include <QPainter> 00011 #include <QSvgRenderer> 00012 #include <QFileInfo> 00013 #include <QDir> 00014 00015 #include <cmath> 00016 00017 // MSVC compiler doesn't have defined M_PI in math.h 00018 #ifndef M_PI 00019 #define M_PI 3.14159265358979323846 00020 #endif 00021 00022 #define DEG2RAD(x) ((x)*M_PI/180) 00023 00024 00025 static QPointF _rotatedOffset( const QPointF& offset, double angle ) 00026 { 00027 angle = DEG2RAD( angle ); 00028 double c = cos( angle ), s = sin( angle ); 00029 return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c ); 00030 } 00031 00033 00034 QgsSimpleMarkerSymbolLayerV2::QgsSimpleMarkerSymbolLayerV2( QString name, QColor color, QColor borderColor, double size, double angle ) 00035 { 00036 mName = name; 00037 mColor = color; 00038 mBorderColor = borderColor; 00039 mSize = size; 00040 mAngle = angle; 00041 mOffset = QPointF( 0, 0 ); 00042 } 00043 00044 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::create( const QgsStringMap& props ) 00045 { 00046 QString name = DEFAULT_SIMPLEMARKER_NAME; 00047 QColor color = DEFAULT_SIMPLEMARKER_COLOR; 00048 QColor borderColor = DEFAULT_SIMPLEMARKER_BORDERCOLOR; 00049 double size = DEFAULT_SIMPLEMARKER_SIZE; 00050 double angle = DEFAULT_SIMPLEMARKER_ANGLE; 00051 00052 if ( props.contains( "name" ) ) 00053 name = props["name"]; 00054 if ( props.contains( "color" ) ) 00055 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 00056 if ( props.contains( "color_border" ) ) 00057 borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] ); 00058 if ( props.contains( "size" ) ) 00059 size = props["size"].toDouble(); 00060 if ( props.contains( "angle" ) ) 00061 angle = props["angle"].toDouble(); 00062 00063 QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size, angle ); 00064 if ( props.contains( "offset" ) ) 00065 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 00066 return m; 00067 } 00068 00069 00070 QString QgsSimpleMarkerSymbolLayerV2::layerType() const 00071 { 00072 return "SimpleMarker"; 00073 } 00074 00075 void QgsSimpleMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00076 { 00077 QColor brushColor = mColor; 00078 QColor penColor = mBorderColor; 00079 if ( context.alpha() < 1 ) 00080 { 00081 penColor.setAlphaF( context.alpha() ); 00082 brushColor.setAlphaF( context.alpha() ); 00083 } 00084 mBrush = QBrush( brushColor ); 00085 mPen = QPen( penColor ); 00086 mPen.setWidthF( context.outputLineWidth( mPen.widthF() ) ); 00087 00088 QColor selBrushColor = context.selectionColor(); 00089 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mBorderColor; 00090 if ( context.alpha() < 1 ) 00091 { 00092 selBrushColor.setAlphaF( context.alpha() ); 00093 selPenColor.setAlphaF( context.alpha() ); 00094 } 00095 mSelBrush = QBrush( selBrushColor ); 00096 mSelPen = QPen( selPenColor ); 00097 mSelPen.setWidthF( context.outputLineWidth( mPen.widthF() ) ); 00098 00099 bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation; 00100 bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale; 00101 00102 // use caching only when: 00103 // - the size and rotation is not data-defined 00104 // - drawing to screen (not printer) 00105 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput(); 00106 00107 // use either QPolygonF or QPainterPath for drawing 00108 // TODO: find out whether drawing directly doesn't bring overhead - if not, use it for all shapes 00109 if ( !prepareShape() ) // drawing as a polygon 00110 { 00111 if ( preparePath() ) // drawing as a painter path 00112 { 00113 // some markers can't be drawn as a polygon (circle, cross) 00114 // For these set the selected border color to the selected color 00115 00116 if ( mName != "circle" ) 00117 mSelPen.setColor( selBrushColor ); 00118 } 00119 else 00120 { 00121 QgsDebugMsg( "unknown symbol" ); 00122 return; 00123 } 00124 } 00125 00126 QMatrix transform; 00127 00128 // scale the shape (if the size is not going to be modified) 00129 if ( !hasDataDefinedSize ) 00130 { 00131 double scaledSize = context.outputLineWidth( mSize ); 00132 if ( mUsingCache ) 00133 scaledSize *= context.renderContext().rasterScaleFactor(); 00134 double half = scaledSize / 2.0; 00135 transform.scale( half, half ); 00136 } 00137 00138 // rotate if the rotation is not going to be changed during the rendering 00139 if ( !hasDataDefinedRotation && mAngle != 0 ) 00140 { 00141 transform.rotate( mAngle ); 00142 } 00143 00144 if ( !mPolygon.isEmpty() ) 00145 mPolygon = transform.map( mPolygon ); 00146 else 00147 mPath = transform.map( mPath ); 00148 00149 if ( mUsingCache ) 00150 { 00151 prepareCache( context ); 00152 } 00153 else 00154 { 00155 mCache = QImage(); 00156 mSelCache = QImage(); 00157 } 00158 } 00159 00160 00161 void QgsSimpleMarkerSymbolLayerV2::prepareCache( QgsSymbolV2RenderContext& context ) 00162 { 00163 double scaledSize = context.outputPixelSize( mSize ); 00164 00165 // calculate necessary image size for the cache 00166 double pw = (( mPen.widthF() == 0 ? 1 : mPen.widthF() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen 00167 int imageSize = (( int ) scaledSize + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width 00168 00169 double center = (( double ) imageSize / 2 ) + 0.5; // add 1/2 pixel for proper rounding when the figure's coordinates are added 00170 00171 mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied ); 00172 mCache.fill( 0 ); 00173 00174 QPainter p; 00175 p.begin( &mCache ); 00176 p.setRenderHint( QPainter::Antialiasing ); 00177 p.setBrush( mBrush ); 00178 p.setPen( mPen ); 00179 p.translate( QPointF( center, center ) ); 00180 drawMarker( &p, context ); 00181 p.end(); 00182 00183 // Construct the selected version of the Cache 00184 00185 QColor selColor = context.selectionColor(); 00186 00187 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied ); 00188 mSelCache.fill( 0 ); 00189 00190 p.begin( &mSelCache ); 00191 p.setRenderHint( QPainter::Antialiasing ); 00192 p.setBrush( mSelBrush ); 00193 p.setPen( mSelPen ); 00194 p.translate( QPointF( center, center ) ); 00195 drawMarker( &p, context ); 00196 p.end(); 00197 00198 // Check that the selected version is different. If not, then re-render, 00199 // filling the background with the selection color and using the normal 00200 // colors for the symbol .. could be ugly! 00201 00202 if ( mSelCache == mCache ) 00203 { 00204 p.begin( &mSelCache ); 00205 p.setRenderHint( QPainter::Antialiasing ); 00206 p.fillRect( 0, 0, imageSize, imageSize, selColor ); 00207 p.setBrush( mBrush ); 00208 p.setPen( mPen ); 00209 p.translate( QPointF( center, center ) ); 00210 drawMarker( &p, context ); 00211 p.end(); 00212 } 00213 } 00214 00215 void QgsSimpleMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00216 { 00217 } 00218 00219 bool QgsSimpleMarkerSymbolLayerV2::prepareShape() 00220 { 00221 mPolygon.clear(); 00222 00223 if ( mName == "rectangle" ) 00224 { 00225 mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) ); 00226 return true; 00227 } 00228 else if ( mName == "diamond" ) 00229 { 00230 mPolygon << QPointF( -1, 0 ) << QPointF( 0, 1 ) 00231 << QPointF( 1, 0 ) << QPointF( 0, -1 ); 00232 return true; 00233 } 00234 else if ( mName == "pentagon" ) 00235 { 00236 mPolygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) ) 00237 << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) ) 00238 << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) ) 00239 << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) ) 00240 << QPointF( 0, -1 ); 00241 return true; 00242 } 00243 else if ( mName == "triangle" ) 00244 { 00245 mPolygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ); 00246 return true; 00247 } 00248 else if ( mName == "equilateral_triangle" ) 00249 { 00250 mPolygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) ) 00251 << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) ) 00252 << QPointF( 0, -1 ); 00253 return true; 00254 } 00255 else if ( mName == "star" ) 00256 { 00257 double sixth = 1.0 / 3; 00258 00259 mPolygon << QPointF( 0, -1 ) 00260 << QPointF( -sixth, -sixth ) 00261 << QPointF( -1, -sixth ) 00262 << QPointF( -sixth, 0 ) 00263 << QPointF( -1, 1 ) 00264 << QPointF( 0, + sixth ) 00265 << QPointF( 1, 1 ) 00266 << QPointF( + sixth, 0 ) 00267 << QPointF( 1, -sixth ) 00268 << QPointF( + sixth, -sixth ); 00269 return true; 00270 } 00271 else if ( mName == "regular_star" ) 00272 { 00273 double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) ); 00274 00275 mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324 00276 << QPointF( sin( DEG2RAD( 288.0 ) ) , - cos( DEG2RAD( 288 ) ) ) // 288 00277 << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252 00278 << QPointF( sin( DEG2RAD( 216.0 ) ) , - cos( DEG2RAD( 216.0 ) ) ) // 216 00279 << QPointF( 0, inner_r ) // 180 00280 << QPointF( sin( DEG2RAD( 144.0 ) ) , - cos( DEG2RAD( 144.0 ) ) ) // 144 00281 << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108 00282 << QPointF( sin( DEG2RAD( 72.0 ) ) , - cos( DEG2RAD( 72.0 ) ) ) // 72 00283 << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36 00284 << QPointF( 0, -1 ); // 0 00285 return true; 00286 } 00287 else if ( mName == "arrow" ) 00288 { 00289 mPolygon 00290 << QPointF( 0, -1 ) 00291 << QPointF( 0.5, -0.5 ) 00292 << QPointF( 0.25, -0.25 ) 00293 << QPointF( 0.25, 1 ) 00294 << QPointF( -0.25, 1 ) 00295 << QPointF( -0.25, -0.5 ) 00296 << QPointF( -0.5, -0.5 ); 00297 return true; 00298 } 00299 else if ( mName == "filled_arrowhead" ) 00300 { 00301 mPolygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ); 00302 return true; 00303 } 00304 00305 return false; 00306 } 00307 00308 bool QgsSimpleMarkerSymbolLayerV2::preparePath() 00309 { 00310 mPath = QPainterPath(); 00311 00312 if ( mName == "circle" ) 00313 { 00314 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h 00315 return true; 00316 } 00317 else if ( mName == "cross" ) 00318 { 00319 mPath.moveTo( -1, 0 ); 00320 mPath.lineTo( 1, 0 ); // horizontal 00321 mPath.moveTo( 0, -1 ); 00322 mPath.lineTo( 0, 1 ); // vertical 00323 return true; 00324 } 00325 else if ( mName == "cross2" ) 00326 { 00327 mPath.moveTo( -1, -1 ); 00328 mPath.lineTo( 1, 1 ); 00329 mPath.moveTo( 1, -1 ); 00330 mPath.lineTo( -1, 1 ); 00331 return true; 00332 } 00333 else if ( mName == "line" ) 00334 { 00335 mPath.moveTo( 0, -1 ); 00336 mPath.lineTo( 0, 1 ); // vertical line 00337 return true; 00338 } 00339 else if ( mName == "arrowhead" ) 00340 { 00341 mPath.moveTo( 0, 0 ); 00342 mPath.lineTo( -1, -1 ); 00343 mPath.moveTo( 0, 0 ); 00344 mPath.lineTo( -1, 1 ); 00345 return true; 00346 } 00347 00348 return false; 00349 } 00350 00351 void QgsSimpleMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00352 { 00353 QgsRenderContext& rc = context.renderContext(); 00354 QPainter* p = rc.painter(); 00355 if ( !p ) 00356 { 00357 return; 00358 } 00359 00360 QPointF off( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) ); 00361 if ( mAngle ) 00362 off = _rotatedOffset( off, mAngle ); 00363 00364 if ( mUsingCache ) 00365 { 00366 // we will use cached image 00367 QImage &img = context.selected() ? mSelCache : mCache; 00368 double s = img.width() / context.renderContext().rasterScaleFactor(); 00369 p->drawImage( QRectF( point.x() - s / 2.0 + off.x(), 00370 point.y() - s / 2.0 + off.y(), 00371 s, s ), img ); 00372 } 00373 else 00374 { 00375 QMatrix transform; 00376 00377 // bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation; 00378 bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale; 00379 00380 // move to the desired position 00381 transform.translate( point.x() + off.x(), point.y() + off.y() ); 00382 00383 // resize if necessary 00384 if ( hasDataDefinedSize ) 00385 { 00386 double scaledSize = context.outputLineWidth( mSize ); 00387 double half = scaledSize / 2.0; 00388 transform.scale( half, half ); 00389 } 00390 00391 // rotate if necessary 00392 if ( mAngle != 0 ) 00393 { 00394 transform.rotate( mAngle ); 00395 } 00396 00397 p->setBrush( context.selected() ? mSelBrush : mBrush ); 00398 p->setPen( context.selected() ? mSelPen : mPen ); 00399 00400 if ( !mPolygon.isEmpty() ) 00401 p->drawPolygon( transform.map( mPolygon ) ); 00402 else 00403 p->drawPath( transform.map( mPath ) ); 00404 } 00405 } 00406 00407 00408 QgsStringMap QgsSimpleMarkerSymbolLayerV2::properties() const 00409 { 00410 QgsStringMap map; 00411 map["name"] = mName; 00412 map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 00413 map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor ); 00414 map["size"] = QString::number( mSize ); 00415 map["angle"] = QString::number( mAngle ); 00416 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00417 return map; 00418 } 00419 00420 QgsSymbolLayerV2* QgsSimpleMarkerSymbolLayerV2::clone() const 00421 { 00422 QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( mName, mColor, mBorderColor, mSize, mAngle ); 00423 m->setOffset( mOffset ); 00424 return m; 00425 } 00426 00427 void QgsSimpleMarkerSymbolLayerV2::drawMarker( QPainter* p, QgsSymbolV2RenderContext& context ) 00428 { 00429 if ( mPolygon.count() != 0 ) 00430 { 00431 p->drawPolygon( mPolygon ); 00432 } 00433 else 00434 { 00435 p->drawPath( mPath ); 00436 } 00437 } 00438 00439 00441 00442 00443 QgsSvgMarkerSymbolLayerV2::QgsSvgMarkerSymbolLayerV2( QString name, double size, double angle ) 00444 { 00445 mPath = symbolNameToPath( name ); 00446 mSize = size; 00447 mAngle = angle; 00448 mOffset = QPointF( 0, 0 ); 00449 } 00450 00451 00452 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::create( const QgsStringMap& props ) 00453 { 00454 QString name = DEFAULT_SVGMARKER_NAME; 00455 double size = DEFAULT_SVGMARKER_SIZE; 00456 double angle = DEFAULT_SVGMARKER_ANGLE; 00457 00458 if ( props.contains( "name" ) ) 00459 name = props["name"]; 00460 if ( props.contains( "size" ) ) 00461 size = props["size"].toDouble(); 00462 if ( props.contains( "angle" ) ) 00463 angle = props["angle"].toDouble(); 00464 00465 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( name, size, angle ); 00466 if ( props.contains( "offset" ) ) 00467 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 00468 return m; 00469 } 00470 00471 00472 QString QgsSvgMarkerSymbolLayerV2::layerType() const 00473 { 00474 return "SvgMarker"; 00475 } 00476 00477 void QgsSvgMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00478 { 00479 double pictureSize = 0; 00480 QgsRenderContext& rc = context.renderContext(); 00481 00482 if ( rc.painter() && rc.painter()->device() ) 00483 { 00484 //correct QPictures DPI correction 00485 pictureSize = context.outputLineWidth( mSize ) / rc.painter()->device()->logicalDpiX() * mPicture.logicalDpiX(); 00486 } 00487 else 00488 { 00489 pictureSize = context.outputLineWidth( mSize ); 00490 } 00491 QRectF rect( QPointF( -pictureSize / 2.0, -pictureSize / 2.0 ), QSizeF( pictureSize, pictureSize ) ); 00492 QSvgRenderer renderer( mPath ); 00493 QPainter painter( &mPicture ); 00494 renderer.render( &painter, rect ); 00495 QPainter selPainter( &mSelPicture ); 00496 selPainter.setRenderHint( QPainter::Antialiasing ); 00497 selPainter.setBrush( QBrush( context.selectionColor() ) ); 00498 selPainter.setPen( Qt::NoPen ); 00499 selPainter.drawEllipse( QPointF( 0, 0 ), pictureSize*0.6, pictureSize*0.6 ); 00500 renderer.render( &selPainter, rect ); 00501 00502 mOrigSize = mSize; // save in case the size would be data defined 00503 } 00504 00505 void QgsSvgMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00506 { 00507 } 00508 00509 00510 void QgsSvgMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00511 { 00512 QPainter* p = context.renderContext().painter(); 00513 if ( !p ) 00514 { 00515 return; 00516 } 00517 00518 p->save(); 00519 QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) ); 00520 if ( mAngle ) 00521 outputOffset = _rotatedOffset( outputOffset, mAngle ); 00522 p->translate( point + outputOffset ); 00523 00524 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) 00525 { 00526 double s = mSize / mOrigSize; 00527 p->scale( s, s ); 00528 } 00529 00530 if ( mAngle != 0 ) 00531 p->rotate( mAngle ); 00532 00533 QPicture &pct = context.selected() ? mSelPicture : mPicture; 00534 p->drawPicture( 0, 0, pct ); 00535 00536 p->restore(); 00537 } 00538 00539 00540 QgsStringMap QgsSvgMarkerSymbolLayerV2::properties() const 00541 { 00542 QgsStringMap map; 00543 map["name"] = symbolPathToName( mPath ); 00544 map["size"] = QString::number( mSize ); 00545 map["angle"] = QString::number( mAngle ); 00546 map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00547 return map; 00548 } 00549 00550 QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2::clone() const 00551 { 00552 QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( mPath, mSize, mAngle ); 00553 m->setOffset( mOffset ); 00554 return m; 00555 } 00556 00557 00558 QStringList QgsSvgMarkerSymbolLayerV2::listSvgFiles() 00559 { 00560 // copied from QgsMarkerCatalogue - TODO: unify 00561 QStringList list; 00562 QStringList svgPaths = QgsApplication::svgPaths(); 00563 00564 for ( int i = 0; i < svgPaths.size(); i++ ) 00565 { 00566 QDir dir( svgPaths[i] ); 00567 foreach( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) ) 00568 { 00569 svgPaths.insert( i + 1, dir.path() + "/" + item ); 00570 } 00571 00572 foreach( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) ) 00573 { 00574 // TODO test if it is correct SVG 00575 list.append( dir.path() + "/" + item ); 00576 } 00577 } 00578 return list; 00579 } 00580 00581 QString QgsSvgMarkerSymbolLayerV2::symbolNameToPath( QString name ) 00582 { 00583 // copied from QgsSymbol::setNamedPointSymbol - TODO: unify 00584 00585 // we might have a full path... 00586 if ( QFile( name ).exists() ) 00587 return QFileInfo( name ).canonicalFilePath(); 00588 00589 // SVG symbol not found - probably a relative path was used 00590 00591 QStringList svgPaths = QgsApplication::svgPaths(); 00592 for ( int i = 0; i < svgPaths.size(); i++ ) 00593 { 00594 QgsDebugMsg( "SvgPath: " + svgPaths[i] ); 00595 QFileInfo myInfo( name ); 00596 QString myFileName = myInfo.fileName(); // foo.svg 00597 QString myLowestDir = myInfo.dir().dirName(); 00598 QString myLocalPath = svgPaths[i] + "/" + myLowestDir + "/" + myFileName; 00599 00600 QgsDebugMsg( "Alternative svg path: " + myLocalPath ); 00601 if ( QFile( myLocalPath ).exists() ) 00602 { 00603 QgsDebugMsg( "Svg found in alternative path" ); 00604 return QFileInfo( myLocalPath ).canonicalFilePath(); 00605 } 00606 else if ( myInfo.isRelative() ) 00607 { 00608 QFileInfo pfi( QgsProject::instance()->fileName() ); 00609 QString alternatePath = pfi.canonicalPath() + QDir::separator() + name; 00610 if ( pfi.exists() && QFile( alternatePath ).exists() ) 00611 { 00612 QgsDebugMsg( "Svg found in alternative path" ); 00613 return QFileInfo( alternatePath ).canonicalFilePath(); 00614 } 00615 else 00616 { 00617 QgsDebugMsg( "Svg not found in project path" ); 00618 } 00619 } 00620 else 00621 { 00622 //couldnt find the file, no happy ending :-( 00623 QgsDebugMsg( "Computed alternate path but no svg there either" ); 00624 } 00625 } 00626 return QString(); 00627 } 00628 00629 QString QgsSvgMarkerSymbolLayerV2::symbolPathToName( QString path ) 00630 { 00631 // copied from QgsSymbol::writeXML 00632 00633 QFileInfo fi( path ); 00634 if ( !fi.exists() ) 00635 return path; 00636 00637 path = fi.canonicalFilePath(); 00638 00639 QStringList svgPaths = QgsApplication::svgPaths(); 00640 00641 for ( int i = 0; i < svgPaths.size(); i++ ) 00642 { 00643 QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath(); 00644 00645 if ( !dir.isEmpty() && path.startsWith( dir ) ) 00646 { 00647 path = path.mid( dir.size() ); 00648 break; 00649 } 00650 } 00651 00652 return path; 00653 } 00654 00655 00657 00658 QgsFontMarkerSymbolLayerV2::QgsFontMarkerSymbolLayerV2( QString fontFamily, QChar chr, double pointSize, QColor color, double angle ) 00659 { 00660 mFontFamily = fontFamily; 00661 mChr = chr; 00662 mColor = color; 00663 mAngle = angle; 00664 mSize = pointSize; 00665 mOffset = QPointF( 0, 0 ); 00666 } 00667 00668 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::create( const QgsStringMap& props ) 00669 { 00670 QString fontFamily = DEFAULT_FONTMARKER_FONT; 00671 QChar chr = DEFAULT_FONTMARKER_CHR; 00672 double pointSize = DEFAULT_FONTMARKER_SIZE; 00673 QColor color = DEFAULT_FONTMARKER_COLOR; 00674 double angle = DEFAULT_FONTMARKER_ANGLE; 00675 00676 if ( props.contains( "font" ) ) 00677 fontFamily = props["font"]; 00678 if ( props.contains( "chr" ) && props["chr"].length() > 0 ) 00679 chr = props["chr"].at( 0 ); 00680 if ( props.contains( "size" ) ) 00681 pointSize = props["size"].toDouble(); 00682 if ( props.contains( "color" ) ) 00683 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 00684 if ( props.contains( "angle" ) ) 00685 angle = props["angle"].toDouble(); 00686 00687 QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, pointSize, color, angle ); 00688 if ( props.contains( "offset" ) ) 00689 m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) ); 00690 return m; 00691 } 00692 00693 QString QgsFontMarkerSymbolLayerV2::layerType() const 00694 { 00695 return "FontMarker"; 00696 } 00697 00698 void QgsFontMarkerSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00699 { 00700 mFont = QFont( mFontFamily ); 00701 mFont.setPixelSize( context.outputLineWidth( mSize ) ); 00702 QFontMetrics fm( mFont ); 00703 mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 ); 00704 00705 mOrigSize = mSize; // save in case the size would be data defined 00706 } 00707 00708 void QgsFontMarkerSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00709 { 00710 } 00711 00712 void QgsFontMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2RenderContext& context ) 00713 { 00714 QPainter* p = context.renderContext().painter(); 00715 QColor penColor = context.selected() ? context.selectionColor() : mColor; 00716 penColor.setAlphaF( context.alpha() ); 00717 p->setPen( penColor ); 00718 p->setFont( mFont ); 00719 00720 00721 p->save(); 00722 QPointF outputOffset = QPointF( context.outputLineWidth( mOffset.x() ), context.outputLineWidth( mOffset.y() ) ); 00723 if ( mAngle ) 00724 outputOffset = _rotatedOffset( outputOffset, mAngle ); 00725 p->translate( point + outputOffset ); 00726 00727 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) 00728 { 00729 double s = mSize / mOrigSize; 00730 p->scale( s, s ); 00731 } 00732 00733 if ( mAngle != 0 ) 00734 p->rotate( mAngle ); 00735 00736 p->drawText( -mChrOffset, mChr ); 00737 p->restore(); 00738 } 00739 00740 QgsStringMap QgsFontMarkerSymbolLayerV2::properties() const 00741 { 00742 QgsStringMap props; 00743 props["font"] = mFontFamily; 00744 props["chr"] = mChr; 00745 props["size"] = QString::number( mSize ); 00746 props["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 00747 props["angle"] = QString::number( mAngle ); 00748 props["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset ); 00749 return props; 00750 } 00751 00752 QgsSymbolLayerV2* QgsFontMarkerSymbolLayerV2::clone() const 00753 { 00754 QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( mFontFamily, mChr, mSize, mColor, mAngle ); 00755 m->setOffset( mOffset ); 00756 return m; 00757 }