Quantum GIS API Documentation  1.8
src/core/symbology/qgsmarkercatalogue.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   qgsmarkercatalogue.cpp
00003   -------------------
00004 begin                : March 2005
00005 copyright            : (C) 2005 by Radim Blazek
00006 email                : blazek@itc.it
00007  ***************************************************************************/
00008 /***************************************************************************
00009  *                                                                         *
00010  *   This program is free software; you can redistribute it and/or modify  *
00011  *   it under the terms of the GNU General Public License as published by  *
00012  *   the Free Software Foundation; either version 2 of the License, or     *
00013  *   (at your option) any later version.                                   *
00014  *                                                                         *
00015  ***************************************************************************/
00016 #include <cmath>
00017 #include <assert.h>
00018 
00019 #include <QPen>
00020 #include <QBrush>
00021 #include <QPainter>
00022 #include <QImage>
00023 #include <QString>
00024 #include <QStringList>
00025 #include <QRect>
00026 #include <QPointF>
00027 #include <QPolygonF>
00028 #include <QDir>
00029 #include <QPicture>
00030 #include <QSvgRenderer>
00031 
00032 #include "qgis.h"
00033 #include "qgsapplication.h"
00034 #include "qgsmarkercatalogue.h"
00035 #include "qgslogger.h"
00036 
00037 // MSVC compiler doesn't have defined M_PI in math.h
00038 #ifndef M_PI
00039 #define M_PI          3.14159265358979323846
00040 #endif
00041 
00042 #define DEG2RAD(x)    ((x)*M_PI/180)
00043 
00044 //#define IMAGEDEBUG
00045 
00046 QgsMarkerCatalogue *QgsMarkerCatalogue::mMarkerCatalogue = 0;
00047 
00048 QgsMarkerCatalogue::QgsMarkerCatalogue()
00049 {
00050   refreshList();
00051 }
00052 
00053 void QgsMarkerCatalogue::refreshList()
00054 {
00055   // Init list
00056   mList.clear();
00057 
00058   // Hardcoded markers
00059   mList.append( "hard:circle" );
00060   mList.append( "hard:rectangle" );
00061   mList.append( "hard:diamond" );
00062   mList.append( "hard:pentagon" );
00063   mList.append( "hard:cross" );
00064   mList.append( "hard:cross2" );
00065   mList.append( "hard:triangle" );
00066   mList.append( "hard:equilateral_triangle" );
00067   mList.append( "hard:star" );
00068   mList.append( "hard:regular_star" );
00069   mList.append( "hard:arrow" );
00070 
00071   // SVG
00072   QStringList svgPaths = QgsApplication::svgPaths();
00073   QgsDebugMsg( QString( "Application SVG Search paths: \n%1" ).arg( svgPaths.join( "\n" ) ) );
00074 
00075   for ( int i = 0; i < svgPaths.size(); i++ )
00076   {
00077     QDir dir( svgPaths[i] );
00078     foreach( QString item, dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ) )
00079     {
00080       svgPaths.insert( i + 1, dir.path() + "/" + item );
00081     }
00082 
00083     QgsDebugMsg( QString( "Looking for svgs in %1" ).arg( dir.path() ) );
00084 
00085     foreach( QString item, dir.entryList( QStringList( "*.svg" ), QDir::Files ) )
00086     {
00087       // TODO test if it is correct SVG
00088       mList.append( "svg:" + dir.path() + "/" + item );
00089     }
00090   }
00091 
00092   emit markersRefreshed();
00093 }
00094 
00095 QStringList QgsMarkerCatalogue::list()
00096 {
00097   return mList;
00098 }
00099 
00100 QgsMarkerCatalogue::~QgsMarkerCatalogue()
00101 {
00102 }
00103 
00104 QgsMarkerCatalogue *QgsMarkerCatalogue::instance()
00105 {
00106   if ( !QgsMarkerCatalogue::mMarkerCatalogue )
00107   {
00108     QgsMarkerCatalogue::mMarkerCatalogue = new QgsMarkerCatalogue();
00109   }
00110 
00111   return QgsMarkerCatalogue::mMarkerCatalogue;
00112 }
00113 
00114 QImage QgsMarkerCatalogue::imageMarker( QString fullName, double size, QPen pen, QBrush brush, double opacity )
00115 {
00116 
00117   //
00118   // First prepare the paintdevice that the marker will be drawn onto
00119   //
00120 
00121   QImage myImage;
00122   int imageSize;
00123   if ( fullName.startsWith( "hard:" ) )
00124   {
00125     int pw = (( pen.width() == 0 ? 1 : pen.width() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen
00126     imageSize = (( int ) size + pw ) / 2 * 2 + 1; //  make image width, height odd; account for pen width
00127     myImage = QImage( imageSize, imageSize, QImage::Format_ARGB32_Premultiplied );
00128   }
00129   else
00130   {
00131     // TODO Change this logic so width is size and height is same
00132     // proportion of scale factor as in oritignal SVG TS XXX
00133     //QPixmap myPixmap = QPixmap(width,height);
00134     imageSize = (( int ) size ) / 2 * 2 + 1; //  make image width, height odd
00135     myImage = QImage( imageSize, imageSize, QImage::Format_ARGB32_Premultiplied );
00136   }
00137 
00138   // starting with transparent QImage
00139   myImage.fill( 0 );
00140 
00141   QPainter myPainter;
00142   myPainter.begin( &myImage );
00143   myPainter.setRenderHint( QPainter::Antialiasing );
00144   myPainter.setOpacity( opacity );
00145 
00146   //
00147   // Now pass the paintdevice along to have the marker rendered on it
00148   //
00149   if ( fullName.startsWith( "svg:" ) )
00150   {
00151     if ( svgMarker( &myPainter, fullName.mid( 4 ), size ) )
00152       return myImage;
00153 
00154     QgsDebugMsg( QString( "%1 not found - replacing with hard:circle" ).arg( fullName ) );
00155     fullName = "hard:circle";
00156   }
00157 
00158   if ( fullName.startsWith( "font:" ) )
00159   {
00160     if ( fontMarker( &myPainter, fullName.mid( 5 ), size ) )
00161       return myImage;
00162 
00163     QgsDebugMsg( QString( "%1 not found - replacing with hard:circle" ).arg( fullName ) );
00164     fullName = "hard:circle";
00165   }
00166 
00167   if ( fullName.endsWith( ".svg", Qt::CaseInsensitive ) )
00168   {
00169     if ( svgMarker( &myPainter, fullName, size ) )
00170       return myImage;
00171 
00172     QgsDebugMsg( QString( "%1 not found - replacing with hard:circle" ).arg( fullName ) );
00173     fullName = "hard:circle";
00174   }
00175 
00176   if ( fullName.startsWith( "hard:" ) )
00177   {
00178     hardMarker( &myPainter, imageSize, fullName.mid( 5 ), size, pen, brush );
00179 #ifdef IMAGEDEBUG
00180     QgsDebugMsg( "*** Saving hard marker to hardMarker.png ***" );
00181 #ifdef QGISDEBUG
00182     myImage.save( "hardMarker.png" );
00183 #endif
00184 #endif
00185     return myImage;
00186   }
00187 
00188   return QImage(); // empty
00189 }
00190 
00191 QPicture QgsMarkerCatalogue::pictureMarker( QString fullName, double size, QPen pen, QBrush brush, double opacity )
00192 {
00193 
00194   //
00195   // First prepare the paintdevice that the marker will be drawn onto
00196   //
00197   QPicture myPicture;
00198   if ( fullName.left( 5 ) == "hard:" )
00199   {
00200     //Note teh +1 offset below is required because the
00201     //otherwise the icons are getting clipped
00202     myPicture = QPicture( size + 1 );
00203   }
00204   else
00205   {
00206     // TODO Change this logic so width is size and height is same
00207     // proportion of scale factor as in oritignal SVG TS XXX
00208     if ( size < 1 )
00209       size = 1;
00210     myPicture = QPicture( size );
00211   }
00212 
00213   QPainter myPainter( &myPicture );
00214   myPainter.setRenderHint( QPainter::Antialiasing );
00215   myPainter.setOpacity( opacity );
00216 
00217   //
00218   // Now pass the paintdevice along to have the marker rndered on it
00219   //
00220   if ( fullName.left( 4 ) == "svg:" )
00221   {
00222     if ( svgMarker( &myPainter, fullName.mid( 4 ), size ) )
00223       return myPicture;
00224 
00225     QgsDebugMsg( QString( "%1 not found - replacing with hard:circle" ).arg( fullName ) );
00226     fullName = "hard:circle";
00227   }
00228 
00229   if ( fullName.left( 5 ) == "hard:" )
00230   {
00231     hardMarker( &myPainter, ( int ) size, fullName.mid( 5 ), size, pen, brush );
00232     return myPicture;
00233   }
00234 
00235   return QPicture(); // empty
00236 }
00237 
00238 bool QgsMarkerCatalogue::fontMarker( QPainter *thepPainter, QString fullName, double scaleFactor )
00239 {
00240   QStringList args = fullName.split( "," );
00241   if ( args.size() == 0 )
00242     return false;
00243 
00244   QChar c;
00245 
00246   if ( args.size() > 0 )
00247   {
00248     if ( args[0] == "#" )
00249     {
00250       c = QChar( '#' );
00251     }
00252     else if ( args[0].startsWith( "#" ) )
00253     {
00254       c = QChar( args[0].mid( 1 ).toInt() );
00255     }
00256     else
00257     {
00258       c = args[0][0];
00259     }
00260   }
00261 
00262   QString family = args.size() >= 2 ? args[1] : "Helvetica";
00263   int weight = args.size() >= 3 ? args[2].toInt() : -1;
00264   int italic = args.size() >= 4 ? args[3].toInt() != 0 : false;
00265 
00266   thepPainter->setFont( QFont( family, scaleFactor, weight, italic ) );
00267   thepPainter->drawText( 0, 0, c );
00268 
00269   return true;
00270 }
00271 
00272 bool QgsMarkerCatalogue::svgMarker( QPainter * thepPainter, QString fileName, double scaleFactor )
00273 {
00274   Q_UNUSED( scaleFactor );
00275 
00276   QSvgRenderer mySVG;
00277   if ( !mySVG.load( fileName ) )
00278     return false;
00279 
00280   mySVG.render( thepPainter );
00281 
00282   return true;
00283 }
00284 
00285 void QgsMarkerCatalogue::hardMarker( QPainter * thepPainter, int imageSize, QString name, double s, QPen pen, QBrush brush )
00286 {
00287   // Size of polygon symbols is calculated so that the boundingbox is circumscribed
00288   // around a circle with diameter mPointSize
00289 
00290 #if 0
00291   s = s - pen.widthF(); // to make the overall size of the symbol at the specified size
00292 #else
00293   // the size of the base symbol is at the specified size; the outline is applied additionally
00294 #endif
00295 
00296   // Circle radius, is used for other figures also, when compensating for line
00297   // width is necessary.
00298   double r = s / 2; // get half the size of the figure to be rendered (the radius)
00299 
00300   QgsDebugMsgLevel( QString( "Hard marker size %1" ).arg( s ), 3 );
00301 
00302   // Find out center coordinates of the QImage to draw on.
00303   double x_c = ( double )( imageSize / 2 ) + 0.5; // add 1/2 pixel for proper rounding when the figure's coordinates are added
00304   double y_c = x_c;  // is square image
00305 
00306   thepPainter->setPen( pen );
00307   thepPainter->setBrush( brush );
00308 
00309   QgsDebugMsgLevel( QString( "Hard marker radius %1" ).arg( r ), 3 );
00310 
00311   if ( name == "circle" )
00312   {
00313     // "A stroked ellipse has a size of rectangle.size() plus the pen width."
00314     // (from Qt doc)
00315 
00316     thepPainter->drawEllipse( QRectF( x_c - r, y_c - r, s, s ) ); // x,y,w,h
00317   }
00318   else if ( name == "rectangle" )
00319   {
00320     thepPainter->drawRect( QRectF( x_c - r, y_c - r, s, s ) );  // x,y,w,h
00321   }
00322   else if ( name == "diamond" )
00323   {
00324     QPolygonF pa;
00325     pa << QPointF( x_c - r, y_c )
00326     << QPointF( x_c, y_c + r )
00327     << QPointF( x_c + r, y_c )
00328     << QPointF( x_c, y_c - r );
00329     thepPainter->drawPolygon( pa );
00330   }
00331   else if ( name == "pentagon" )
00332   {
00333     QPolygonF pa;
00334     pa << QPointF( x_c + ( r * sin( DEG2RAD( 288.0 ) ) ), y_c - ( r * cos( DEG2RAD( 288.0 ) ) ) )
00335     << QPointF( x_c + ( r * sin( DEG2RAD( 216.0 ) ) ), y_c - ( r * cos( DEG2RAD( 216.0 ) ) ) )
00336     << QPointF( x_c + ( r * sin( DEG2RAD( 144.0 ) ) ), y_c - ( r * cos( DEG2RAD( 144.0 ) ) ) )
00337     << QPointF( x_c + ( r * sin( DEG2RAD( 72.0 ) ) ), y_c - ( r * cos( DEG2RAD( 72.0 ) ) ) )
00338     << QPointF( x_c, y_c - r );
00339     thepPainter->drawPolygon( pa );
00340   }
00341   else if ( name == "cross" )
00342   {
00343     thepPainter->drawLine( QPointF( x_c - r, y_c ), QPointF( x_c + r, y_c ) ); // horizontal
00344     thepPainter->drawLine( QPointF( x_c, y_c - r ), QPointF( x_c, y_c + r ) ); // vertical
00345   }
00346   else if ( name == "cross2" )
00347   {
00348     thepPainter->drawLine( QPointF( x_c - r, y_c - r ), QPointF( x_c + r, y_c + r ) );
00349     thepPainter->drawLine( QPointF( x_c - r, y_c + r ), QPointF( x_c + r, y_c - r ) );
00350   }
00351   else if ( name == "triangle" )
00352   {
00353     QPolygonF pa;
00354     pa << QPointF( x_c - r, y_c + r )
00355     << QPointF( x_c + r, y_c + r )
00356     << QPointF( x_c, y_c - r );
00357     thepPainter->drawPolygon( pa );
00358   }
00359   else if ( name == "equilateral_triangle" )
00360   {
00361     QPolygonF pa;
00362     pa << QPointF( x_c + ( r * sin( DEG2RAD( 240.0 ) ) ), y_c - ( r * cos( DEG2RAD( 240.0 ) ) ) )
00363     << QPointF( x_c + ( r * sin( DEG2RAD( 120.0 ) ) ), y_c - ( r * cos( DEG2RAD( 120.0 ) ) ) )
00364     << QPointF( x_c, y_c - r ); // 0
00365     thepPainter->drawPolygon( pa );
00366   }
00367   else if ( name == "star" )
00368   {
00369     double oneSixth = 2 * r / 6;
00370 
00371     QPolygonF pa;
00372     pa << QPointF( x_c, y_c - r )
00373     << QPointF( x_c - oneSixth, y_c - oneSixth )
00374     << QPointF( x_c - r, y_c - oneSixth )
00375     << QPointF( x_c - oneSixth, y_c )
00376     << QPointF( x_c - r, y_c + r )
00377     << QPointF( x_c, y_c + oneSixth )
00378     << QPointF( x_c + r, y_c + r )
00379     << QPointF( x_c + oneSixth, y_c )
00380     << QPointF( x_c + r, y_c - oneSixth )
00381     << QPointF( x_c + oneSixth, y_c - oneSixth );
00382     thepPainter->drawPolygon( pa );
00383   }
00384   else if ( name == "regular_star" )
00385   {
00386     // control the 'fatness' of the star:  cos(72)/cos(36) gives the classic star shape
00387     double inner_r = r * cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) );
00388 
00389     QPolygonF pa;
00390     pa << QPointF( x_c + ( inner_r * sin( DEG2RAD( 324.0 ) ) ), y_c - ( inner_r * cos( DEG2RAD( 324.0 ) ) ) ) // 324
00391     << QPointF( x_c + ( r * sin( DEG2RAD( 288.0 ) ) ), y_c - ( r * cos( DEG2RAD( 288 ) ) ) )   // 288
00392     << QPointF( x_c + ( inner_r * sin( DEG2RAD( 252.0 ) ) ), y_c - ( inner_r * cos( DEG2RAD( 252.0 ) ) ) ) // 252
00393     << QPointF( x_c + ( r * sin( DEG2RAD( 216.0 ) ) ), y_c - ( r * cos( DEG2RAD( 216.0 ) ) ) )  // 216
00394     << QPointF( x_c, y_c + ( inner_r ) )         // 180
00395     << QPointF( x_c + ( r * sin( DEG2RAD( 144.0 ) ) ), y_c - ( r * cos( DEG2RAD( 144.0 ) ) ) )  // 144
00396     << QPointF( x_c + ( inner_r * sin( DEG2RAD( 108.0 ) ) ), y_c - ( inner_r * cos( DEG2RAD( 108.0 ) ) ) ) // 108
00397     << QPointF( x_c + ( r * sin( DEG2RAD( 72.0 ) ) ), y_c - ( r * cos( DEG2RAD( 72.0 ) ) ) )   //  72
00398     << QPointF( x_c + ( inner_r * sin( DEG2RAD( 36.0 ) ) ), y_c - ( inner_r * cos( DEG2RAD( 36.0 ) ) ) ) //  36
00399     << QPointF( x_c, y_c - r );          //   0
00400     thepPainter->drawPolygon( pa );
00401   }
00402   else if ( name == "arrow" )
00403   {
00404     double oneEight = r / 4;
00405     double quarter = r / 2;
00406 
00407     QPolygonF pa;
00408     pa << QPointF( x_c, y_c - r )
00409     << QPointF( x_c + quarter,  y_c - quarter )
00410     << QPointF( x_c + oneEight, y_c - quarter )
00411     << QPointF( x_c + oneEight, y_c + r )
00412     << QPointF( x_c - oneEight, y_c + r )
00413     << QPointF( x_c - oneEight, y_c - quarter )
00414     << QPointF( x_c - quarter,  y_c - quarter );
00415     thepPainter->drawPolygon( pa );
00416   }
00417   thepPainter->end();
00418 }
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines