Quantum GIS API Documentation
1.7.4
|
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 ) 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 }