Quantum GIS API Documentation  1.7.4
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                : [email protected]
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 ) size = 1;
00209     myPicture = QPicture( size );
00210   }
00211 
00212   QPainter myPainter( &myPicture );
00213   myPainter.setRenderHint( QPainter::Antialiasing );
00214   myPainter.setOpacity( opacity );
00215 
00216   //
00217   // Now pass the paintdevice along to have the marker rndered on it
00218   //
00219   if ( fullName.left( 4 ) == "svg:" )
00220   {
00221     if ( svgMarker( &myPainter, fullName.mid( 4 ), size ) )
00222       return myPicture;
00223 
00224     QgsDebugMsg( QString( "%1 not found - replacing with hard:circle" ).arg( fullName ) );
00225     fullName = "hard:circle";
00226   }
00227 
00228   if ( fullName.left( 5 ) == "hard:" )
00229   {
00230     hardMarker( &myPainter, ( int ) size, fullName.mid( 5 ), size, pen, brush );
00231     return myPicture;
00232   }
00233 
00234   return QPicture(); // empty
00235 }
00236 
00237 bool QgsMarkerCatalogue::fontMarker( QPainter *thepPainter, QString fullName, double scaleFactor )
00238 {
00239   QStringList args = fullName.split( "," );
00240   if ( args.size() == 0 )
00241     return false;
00242 
00243   QChar c;
00244 
00245   if ( args.size() > 0 )
00246   {
00247     if ( args[0] == "#" )
00248     {
00249       c = QChar( '#' );
00250     }
00251     else if ( args[0].startsWith( "#" ) )
00252     {
00253       c = QChar( args[0].mid( 1 ).toInt() );
00254     }
00255     else
00256     {
00257       c = args[0][0];
00258     }
00259   }
00260 
00261   QString family = args.size() >= 2 ? args[1] : "Helvetica";
00262   int weight = args.size() >= 3 ? args[2].toInt() : -1;
00263   int italic = args.size() >= 4 ? args[3].toInt() != 0 : false;
00264 
00265   thepPainter->setFont( QFont( family, scaleFactor, weight, italic ) );
00266   thepPainter->drawText( 0, 0, c );
00267 
00268   return true;
00269 }
00270 
00271 bool QgsMarkerCatalogue::svgMarker( QPainter * thepPainter, QString fileName, double scaleFactor )
00272 {
00273   QSvgRenderer mySVG;
00274   if ( !mySVG.load( fileName ) )
00275     return false;
00276 
00277   mySVG.render( thepPainter );
00278 
00279   return true;
00280 }
00281 
00282 void QgsMarkerCatalogue::hardMarker( QPainter * thepPainter, int imageSize, QString name, double s, QPen pen, QBrush brush )
00283 {
00284   // Size of polygon symbols is calculated so that the boundingbox is circumscribed
00285   // around a circle with diameter mPointSize
00286 
00287 #if 0
00288   s = s - pen.widthF(); // to make the overall size of the symbol at the specified size
00289 #else
00290   // the size of the base symbol is at the specified size; the outline is applied additionally
00291 #endif
00292 
00293   // Circle radius, is used for other figures also, when compensating for line
00294   // width is necessary.
00295   double r = s / 2; // get half the size of the figure to be rendered (the radius)
00296 
00297   QgsDebugMsgLevel( QString( "Hard marker size %1" ).arg( s ), 3 );
00298 
00299   // Find out center coordinates of the QImage to draw on.
00300   double x_c = ( double )( imageSize / 2 ) + 0.5; // add 1/2 pixel for proper rounding when the figure's coordinates are added
00301   double y_c = x_c;  // is square image
00302 
00303   thepPainter->setPen( pen );
00304   thepPainter->setBrush( brush );
00305 
00306   QgsDebugMsgLevel( QString( "Hard marker radius %1" ).arg( r ), 3 );
00307 
00308   if ( name == "circle" )
00309   {
00310     // "A stroked ellipse has a size of rectangle.size() plus the pen width."
00311     // (from Qt doc)
00312 
00313     thepPainter->drawEllipse( QRectF( x_c - r, y_c - r, s, s ) ); // x,y,w,h
00314   }
00315   else if ( name == "rectangle" )
00316   {
00317     thepPainter->drawRect( QRectF( x_c - r, y_c - r, s, s ) );  // x,y,w,h
00318   }
00319   else if ( name == "diamond" )
00320   {
00321     QPolygonF pa;
00322     pa << QPointF( x_c - r, y_c )
00323     << QPointF( x_c, y_c + r )
00324     << QPointF( x_c + r, y_c )
00325     << QPointF( x_c, y_c - r );
00326     thepPainter->drawPolygon( pa );
00327   }
00328   else if ( name == "pentagon" )
00329   {
00330     QPolygonF pa;
00331     pa << QPointF( x_c + ( r * sin( DEG2RAD( 288.0 ) ) ), y_c - ( r * cos( DEG2RAD( 288.0 ) ) ) )
00332     << QPointF( x_c + ( r * sin( DEG2RAD( 216.0 ) ) ), y_c - ( r * cos( DEG2RAD( 216.0 ) ) ) )
00333     << QPointF( x_c + ( r * sin( DEG2RAD( 144.0 ) ) ), y_c - ( r * cos( DEG2RAD( 144.0 ) ) ) )
00334     << QPointF( x_c + ( r * sin( DEG2RAD( 72.0 ) ) ), y_c - ( r * cos( DEG2RAD( 72.0 ) ) ) )
00335     << QPointF( x_c, y_c - r );
00336     thepPainter->drawPolygon( pa );
00337   }
00338   else if ( name == "cross" )
00339   {
00340     thepPainter->drawLine( QPointF( x_c - r, y_c ), QPointF( x_c + r, y_c ) ); // horizontal
00341     thepPainter->drawLine( QPointF( x_c, y_c - r ), QPointF( x_c, y_c + r ) ); // vertical
00342   }
00343   else if ( name == "cross2" )
00344   {
00345     thepPainter->drawLine( QPointF( x_c - r, y_c - r ), QPointF( x_c + r, y_c + r ) );
00346     thepPainter->drawLine( QPointF( x_c - r, y_c + r ), QPointF( x_c + r, y_c - r ) );
00347   }
00348   else if ( name == "triangle" )
00349   {
00350     QPolygonF pa;
00351     pa << QPointF( x_c - r, y_c + r )
00352     << QPointF( x_c + r, y_c + r )
00353     << QPointF( x_c, y_c - r );
00354     thepPainter->drawPolygon( pa );
00355   }
00356   else if ( name == "equilateral_triangle" )
00357   {
00358     QPolygonF pa;
00359     pa << QPointF( x_c + ( r * sin( DEG2RAD( 240.0 ) ) ), y_c - ( r * cos( DEG2RAD( 240.0 ) ) ) )
00360     << QPointF( x_c + ( r * sin( DEG2RAD( 120.0 ) ) ), y_c - ( r * cos( DEG2RAD( 120.0 ) ) ) )
00361     << QPointF( x_c, y_c - r ); // 0
00362     thepPainter->drawPolygon( pa );
00363   }
00364   else if ( name == "star" )
00365   {
00366     double oneSixth = 2 * r / 6;
00367 
00368     QPolygonF pa;
00369     pa << QPointF( x_c, y_c - r )
00370     << QPointF( x_c - oneSixth, y_c - oneSixth )
00371     << QPointF( x_c - r, y_c - oneSixth )
00372     << QPointF( x_c - oneSixth, y_c )
00373     << QPointF( x_c - r, y_c + r )
00374     << QPointF( x_c, y_c + oneSixth )
00375     << QPointF( x_c + r, y_c + r )
00376     << QPointF( x_c + oneSixth, y_c )
00377     << QPointF( x_c + r, y_c - oneSixth )
00378     << QPointF( x_c + oneSixth, y_c - oneSixth );
00379     thepPainter->drawPolygon( pa );
00380   }
00381   else if ( name == "regular_star" )
00382   {
00383     // control the 'fatness' of the star:  cos(72)/cos(36) gives the classic star shape
00384     double inner_r = r * cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) );
00385 
00386     QPolygonF pa;
00387     pa << QPointF( x_c + ( inner_r * sin( DEG2RAD( 324.0 ) ) ), y_c - ( inner_r * cos( DEG2RAD( 324.0 ) ) ) ) // 324
00388     << QPointF( x_c + ( r * sin( DEG2RAD( 288.0 ) ) ), y_c - ( r * cos( DEG2RAD( 288 ) ) ) )   // 288
00389     << QPointF( x_c + ( inner_r * sin( DEG2RAD( 252.0 ) ) ), y_c - ( inner_r * cos( DEG2RAD( 252.0 ) ) ) ) // 252
00390     << QPointF( x_c + ( r * sin( DEG2RAD( 216.0 ) ) ), y_c - ( r * cos( DEG2RAD( 216.0 ) ) ) )  // 216
00391     << QPointF( x_c, y_c + ( inner_r ) )         // 180
00392     << QPointF( x_c + ( r * sin( DEG2RAD( 144.0 ) ) ), y_c - ( r * cos( DEG2RAD( 144.0 ) ) ) )  // 144
00393     << QPointF( x_c + ( inner_r * sin( DEG2RAD( 108.0 ) ) ), y_c - ( inner_r * cos( DEG2RAD( 108.0 ) ) ) ) // 108
00394     << QPointF( x_c + ( r * sin( DEG2RAD( 72.0 ) ) ), y_c - ( r * cos( DEG2RAD( 72.0 ) ) ) )   //  72
00395     << QPointF( x_c + ( inner_r * sin( DEG2RAD( 36.0 ) ) ), y_c - ( inner_r * cos( DEG2RAD( 36.0 ) ) ) ) //  36
00396     << QPointF( x_c, y_c - r );          //   0
00397     thepPainter->drawPolygon( pa );
00398   }
00399   else if ( name == "arrow" )
00400   {
00401     double oneEight = r / 4;
00402     double quarter = r / 2;
00403 
00404     QPolygonF pa;
00405     pa << QPointF( x_c, y_c - r )
00406     << QPointF( x_c + quarter,  y_c - quarter )
00407     << QPointF( x_c + oneEight, y_c - quarter )
00408     << QPointF( x_c + oneEight, y_c + r )
00409     << QPointF( x_c - oneEight, y_c + r )
00410     << QPointF( x_c - oneEight, y_c - quarter )
00411     << QPointF( x_c - quarter,  y_c - quarter );
00412     thepPainter->drawPolygon( pa );
00413   }
00414   thepPainter->end();
00415 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines