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