|
Quantum GIS API Documentation
master-ce49b66
|
00001 /*************************************************************************** 00002 qgsvectorcolorrampv2.cpp 00003 --------------------- 00004 begin : November 2009 00005 copyright : (C) 2009 by Martin Dobias 00006 email : wonder dot sk at gmail dot com 00007 *************************************************************************** 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 ***************************************************************************/ 00015 00016 #include "qgsvectorcolorrampv2.h" 00017 #include "qgscolorbrewerpalette.h" 00018 #include "qgscptcityarchive.h" 00019 00020 #include "qgssymbollayerv2utils.h" 00021 #include "qgsapplication.h" 00022 #include "qgslogger.h" 00023 00024 #include <stdlib.h> // for random() 00025 #include <QTime> 00026 00028 00029 static QColor _interpolate( QColor c1, QColor c2, double value ) 00030 { 00031 int r = ( int )( c1.red() + value * ( c2.red() - c1.red() ) ); 00032 int g = ( int )( c1.green() + value * ( c2.green() - c1.green() ) ); 00033 int b = ( int )( c1.blue() + value * ( c2.blue() - c1.blue() ) ); 00034 int a = ( int )( c1.alpha() + value * ( c2.alpha() - c1.alpha() ) ); 00035 00036 return QColor::fromRgb( r, g, b, a ); 00037 } 00038 00040 00041 QgsVectorGradientColorRampV2::QgsVectorGradientColorRampV2( QColor color1, QColor color2, 00042 bool discrete, QgsGradientStopsList stops ) 00043 : mColor1( color1 ), mColor2( color2 ), mDiscrete( discrete ), mStops( stops ) 00044 { 00045 } 00046 00047 QgsVectorColorRampV2* QgsVectorGradientColorRampV2::create( const QgsStringMap& props ) 00048 { 00049 // color1 and color2 00050 QColor color1 = DEFAULT_GRADIENT_COLOR1; 00051 QColor color2 = DEFAULT_GRADIENT_COLOR2; 00052 if ( props.contains( "color1" ) ) 00053 color1 = QgsSymbolLayerV2Utils::decodeColor( props["color1"] ); 00054 if ( props.contains( "color2" ) ) 00055 color2 = QgsSymbolLayerV2Utils::decodeColor( props["color2"] ); 00056 00057 //stops 00058 QgsGradientStopsList stops; 00059 if ( props.contains( "stops" ) ) 00060 { 00061 foreach ( QString stop, props["stops"].split( ':' ) ) 00062 { 00063 int i = stop.indexOf( ';' ); 00064 if ( i == -1 ) 00065 continue; 00066 00067 QColor c = QgsSymbolLayerV2Utils::decodeColor( stop.mid( i + 1 ) ); 00068 stops.append( QgsGradientStop( stop.left( i ).toDouble(), c ) ); 00069 } 00070 } 00071 00072 // discrete vs. continuous 00073 bool discrete = false; 00074 if ( props.contains( "discrete" ) ) 00075 { 00076 if ( props["discrete"] == "1" ) 00077 discrete = true; 00078 } 00079 00080 // search for information keys starting with "info_" 00081 QgsStringMap info; 00082 for ( QgsStringMap::const_iterator it = props.constBegin(); 00083 it != props.constEnd(); ++it ) 00084 { 00085 if ( it.key().startsWith( "info_" ) ) 00086 info[ it.key().mid( 5 )] = it.value(); 00087 } 00088 00089 QgsVectorGradientColorRampV2* r = new QgsVectorGradientColorRampV2( color1, color2, discrete, stops ); 00090 r->setInfo( info ); 00091 return r; 00092 } 00093 00094 double QgsVectorGradientColorRampV2::value( int index ) const 00095 { 00096 if ( index <= 0 ) 00097 { 00098 return 0; 00099 } 00100 else if ( index >= mStops.size() + 1 ) 00101 { 00102 return 1; 00103 } 00104 else 00105 { 00106 return mStops[index-1].offset; 00107 } 00108 } 00109 00110 QColor QgsVectorGradientColorRampV2::color( double value ) const 00111 { 00112 if ( mStops.isEmpty() ) 00113 { 00114 if ( mDiscrete ) 00115 return mColor1; 00116 return _interpolate( mColor1, mColor2, value ); 00117 } 00118 else 00119 { 00120 double lower = 0, upper = 0; 00121 QColor c1 = mColor1, c2; 00122 for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it ) 00123 { 00124 if ( it->offset > value ) 00125 { 00126 if ( mDiscrete ) 00127 return c1; 00128 00129 upper = it->offset; 00130 c2 = it->color; 00131 00132 return upper == lower ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) ); 00133 } 00134 lower = it->offset; 00135 c1 = it->color; 00136 } 00137 00138 if ( mDiscrete ) 00139 return c1; 00140 00141 upper = 1; 00142 c2 = mColor2; 00143 return upper == lower ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) ); 00144 } 00145 } 00146 00147 QgsVectorColorRampV2* QgsVectorGradientColorRampV2::clone() const 00148 { 00149 QgsVectorGradientColorRampV2* r = new QgsVectorGradientColorRampV2( mColor1, mColor2, 00150 mDiscrete, mStops ); 00151 r->setInfo( mInfo ); 00152 return r; 00153 } 00154 00155 QgsStringMap QgsVectorGradientColorRampV2::properties() const 00156 { 00157 QgsStringMap map; 00158 map["color1"] = QgsSymbolLayerV2Utils::encodeColor( mColor1 ); 00159 map["color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 ); 00160 if ( !mStops.isEmpty() ) 00161 { 00162 QStringList lst; 00163 for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it ) 00164 { 00165 lst.append( QString( "%1;%2" ).arg( it->offset ).arg( QgsSymbolLayerV2Utils::encodeColor( it->color ) ) ); 00166 } 00167 map["stops"] = lst.join( ":" ); 00168 } 00169 00170 map["discrete"] = mDiscrete ? "1" : "0"; 00171 00172 for ( QgsStringMap::const_iterator it = mInfo.constBegin(); 00173 it != mInfo.constEnd(); ++it ) 00174 { 00175 map["info_" + it.key()] = it.value(); 00176 } 00177 00178 return map; 00179 } 00180 void QgsVectorGradientColorRampV2::convertToDiscrete( bool discrete ) 00181 { 00182 if ( discrete == mDiscrete ) 00183 return; 00184 00185 // if going to/from Discrete, re-arrange stops 00186 // this will only work when stops are equally-spaced 00187 QgsGradientStopsList newStops; 00188 if ( discrete ) 00189 { 00190 // re-arrange stops offset 00191 int numStops = mStops.count() + 2; 00192 int i = 1; 00193 for ( QgsGradientStopsList::const_iterator it = mStops.begin(); 00194 it != mStops.end(); ++it ) 00195 { 00196 newStops.append( QgsGradientStop(( double ) i / numStops, it->color ) ); 00197 if ( i == numStops - 1 ) 00198 break; 00199 i++; 00200 } 00201 // replicate last color 00202 newStops.append( QgsGradientStop(( double ) i / numStops, mColor2 ) ); 00203 } 00204 else 00205 { 00206 // re-arrange stops offset, remove duplicate last color 00207 int numStops = mStops.count() + 2; 00208 int i = 1; 00209 for ( QgsGradientStopsList::const_iterator it = mStops.begin(); 00210 it != mStops.end(); ++it ) 00211 { 00212 newStops.append( QgsGradientStop(( double ) i / ( numStops - 2 ), it->color ) ); 00213 if ( i == numStops - 3 ) 00214 break; 00215 i++; 00216 } 00217 } 00218 mStops = newStops; 00219 mDiscrete = discrete; 00220 } 00221 00223 00224 00225 QgsVectorRandomColorRampV2::QgsVectorRandomColorRampV2( int count, int hueMin, int hueMax, 00226 int satMin, int satMax, int valMin, int valMax ) 00227 : mCount( count ), mHueMin( hueMin ), mHueMax( hueMax ), 00228 mSatMin( satMin ), mSatMax( satMax ), mValMin( valMin ), mValMax( valMax ) 00229 { 00230 updateColors(); 00231 } 00232 00233 QgsVectorColorRampV2* QgsVectorRandomColorRampV2::create( const QgsStringMap& props ) 00234 { 00235 int count = DEFAULT_RANDOM_COUNT; 00236 int hueMin = DEFAULT_RANDOM_HUE_MIN, hueMax = DEFAULT_RANDOM_HUE_MAX; 00237 int satMin = DEFAULT_RANDOM_SAT_MIN, satMax = DEFAULT_RANDOM_SAT_MAX; 00238 int valMin = DEFAULT_RANDOM_VAL_MIN, valMax = DEFAULT_RANDOM_VAL_MAX; 00239 00240 if ( props.contains( "count" ) ) count = props["count"].toInt(); 00241 if ( props.contains( "hueMin" ) ) hueMin = props["hueMin"].toInt(); 00242 if ( props.contains( "hueMax" ) ) hueMax = props["hueMax"].toInt(); 00243 if ( props.contains( "satMin" ) ) satMin = props["satMin"].toInt(); 00244 if ( props.contains( "satMax" ) ) satMax = props["satMax"].toInt(); 00245 if ( props.contains( "valMin" ) ) valMin = props["valMin"].toInt(); 00246 if ( props.contains( "valMax" ) ) valMax = props["valMax"].toInt(); 00247 00248 return new QgsVectorRandomColorRampV2( count, hueMin, hueMax, satMin, satMax, valMin, valMax ); 00249 } 00250 00251 double QgsVectorRandomColorRampV2::value( int index ) const 00252 { 00253 if ( mColors.size() < 1 ) return 0; 00254 return index / mColors.size() - 1; 00255 } 00256 00257 QColor QgsVectorRandomColorRampV2::color( double value ) const 00258 { 00259 int colorCnt = mColors.count(); 00260 int colorIdx = ( int )( value * colorCnt ); 00261 00262 if ( colorIdx >= 0 && colorIdx < colorCnt ) 00263 return mColors.at( colorIdx ); 00264 00265 return QColor(); 00266 } 00267 00268 QgsVectorColorRampV2* QgsVectorRandomColorRampV2::clone() const 00269 { 00270 return new QgsVectorRandomColorRampV2( mCount, mHueMin, mHueMax, mSatMin, mSatMax, mValMin, mValMax ); 00271 } 00272 00273 QgsStringMap QgsVectorRandomColorRampV2::properties() const 00274 { 00275 QgsStringMap map; 00276 map["count"] = QString::number( mCount ); 00277 map["hueMin"] = QString::number( mHueMin ); 00278 map["hueMax"] = QString::number( mHueMax ); 00279 map["satMin"] = QString::number( mSatMin ); 00280 map["satMax"] = QString::number( mSatMax ); 00281 map["valMin"] = QString::number( mValMin ); 00282 map["valMax"] = QString::number( mValMax ); 00283 return map; 00284 } 00285 00286 void QgsVectorRandomColorRampV2::updateColors() 00287 { 00288 int h, s, v; 00289 00290 mColors.clear(); 00291 for ( int i = 0; i < mCount; i++ ) 00292 { 00293 h = ( rand() % ( mHueMax - mHueMin + 1 ) ) + mHueMin; 00294 s = ( rand() % ( mSatMax - mSatMin + 1 ) ) + mSatMin; 00295 v = ( rand() % ( mValMax - mValMin + 1 ) ) + mValMin; 00296 mColors.append( QColor::fromHsv( h, s, v ) ); 00297 } 00298 } 00299 00300 00302 00303 QgsVectorColorBrewerColorRampV2::QgsVectorColorBrewerColorRampV2( QString schemeName, int colors ) 00304 : mSchemeName( schemeName ), mColors( colors ) 00305 { 00306 loadPalette(); 00307 } 00308 00309 QgsVectorColorRampV2* QgsVectorColorBrewerColorRampV2::create( const QgsStringMap& props ) 00310 { 00311 QString schemeName = DEFAULT_COLORBREWER_SCHEMENAME; 00312 int colors = DEFAULT_COLORBREWER_COLORS; 00313 00314 if ( props.contains( "schemeName" ) ) 00315 schemeName = props["schemeName"]; 00316 if ( props.contains( "colors" ) ) 00317 colors = props["colors"].toInt(); 00318 00319 return new QgsVectorColorBrewerColorRampV2( schemeName, colors ); 00320 } 00321 00322 void QgsVectorColorBrewerColorRampV2::loadPalette() 00323 { 00324 mPalette = QgsColorBrewerPalette::listSchemeColors( mSchemeName, mColors ); 00325 } 00326 00327 QStringList QgsVectorColorBrewerColorRampV2::listSchemeNames() 00328 { 00329 return QgsColorBrewerPalette::listSchemes(); 00330 } 00331 00332 QList<int> QgsVectorColorBrewerColorRampV2::listSchemeVariants( QString schemeName ) 00333 { 00334 return QgsColorBrewerPalette::listSchemeVariants( schemeName ); 00335 } 00336 00337 double QgsVectorColorBrewerColorRampV2::value( int index ) const 00338 { 00339 if ( mPalette.size() < 1 ) return 0; 00340 return index / mPalette.size() - 1; 00341 } 00342 00343 QColor QgsVectorColorBrewerColorRampV2::color( double value ) const 00344 { 00345 if ( mPalette.isEmpty() || value < 0 || value > 1 ) 00346 return QColor( 255, 0, 0 ); // red color as a warning :) 00347 00348 int paletteEntry = ( int )( value * mPalette.count() ); 00349 if ( paletteEntry >= mPalette.count() ) 00350 paletteEntry = mPalette.count() - 1; 00351 return mPalette.at( paletteEntry ); 00352 } 00353 00354 QgsVectorColorRampV2* QgsVectorColorBrewerColorRampV2::clone() const 00355 { 00356 return new QgsVectorColorBrewerColorRampV2( mSchemeName, mColors ); 00357 } 00358 00359 QgsStringMap QgsVectorColorBrewerColorRampV2::properties() const 00360 { 00361 QgsStringMap map; 00362 map["schemeName"] = mSchemeName; 00363 map["colors"] = QString::number( mColors ); 00364 return map; 00365 } 00366 00367 00369 00370 00371 QgsCptCityColorRampV2::QgsCptCityColorRampV2( QString schemeName, QString variantName, 00372 bool doLoadFile ) 00373 : QgsVectorGradientColorRampV2(), 00374 mSchemeName( schemeName ), mVariantName( variantName ), 00375 mVariantList( QStringList() ), mFileLoaded( false ), mMultiStops( false ) 00376 { 00377 // TODO replace this with hard-coded data in the default case 00378 // don't load file if variant is missing 00379 if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) ) 00380 loadFile(); 00381 } 00382 00383 QgsCptCityColorRampV2::QgsCptCityColorRampV2( QString schemeName, QStringList variantList, 00384 QString variantName, bool doLoadFile ) 00385 : QgsVectorGradientColorRampV2(), 00386 mSchemeName( schemeName ), mVariantName( variantName ), 00387 mVariantList( variantList ), mFileLoaded( false ), mMultiStops( false ) 00388 { 00389 mVariantList = variantList; 00390 00391 // TODO replace this with hard-coded data in the default case 00392 // don't load file if variant is missing 00393 if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) ) 00394 loadFile(); 00395 } 00396 00397 QgsVectorColorRampV2* QgsCptCityColorRampV2::create( const QgsStringMap& props ) 00398 { 00399 QString schemeName = DEFAULT_CPTCITY_SCHEMENAME; 00400 QString variantName = DEFAULT_CPTCITY_VARIANTNAME; 00401 00402 if ( props.contains( "schemeName" ) ) 00403 schemeName = props["schemeName"]; 00404 if ( props.contains( "variantName" ) ) 00405 variantName = props["variantName"]; 00406 00407 return new QgsCptCityColorRampV2( schemeName, variantName ); 00408 } 00409 00410 QgsVectorColorRampV2* QgsCptCityColorRampV2::clone() const 00411 { 00412 QgsCptCityColorRampV2* ramp = new QgsCptCityColorRampV2( "", "", false ); 00413 ramp->copy( this ); 00414 return ramp; 00415 } 00416 00417 void QgsCptCityColorRampV2::copy( const QgsCptCityColorRampV2* other ) 00418 { 00419 if ( ! other ) 00420 return; 00421 mColor1 = other->color1(); 00422 mColor2 = other->color2(); 00423 mDiscrete = other->isDiscrete(); 00424 mStops = other->stops(); 00425 mSchemeName = other->mSchemeName; 00426 mVariantName = other->mVariantName; 00427 mVariantList = other->mVariantList; 00428 mFileLoaded = other->mFileLoaded; 00429 } 00430 00431 QgsVectorGradientColorRampV2* QgsCptCityColorRampV2::cloneGradientRamp() const 00432 { 00433 QgsVectorGradientColorRampV2* ramp = 00434 new QgsVectorGradientColorRampV2( mColor1, mColor2, mDiscrete, mStops ); 00435 // add author and copyright information 00436 // TODO also add COPYING.xml file/link? 00437 QgsStringMap info = copyingInfo(); 00438 info["cpt-city-gradient"] = "<cpt-city>/" + mSchemeName + mVariantName + ".svg"; 00439 QString copyingFilename = copyingFileName(); 00440 copyingFilename.remove( QgsCptCityArchive::defaultBaseDir() ); 00441 info["cpt-city-license"] = "<cpt-city>" + copyingFilename; 00442 ramp->setInfo( info ); 00443 return ramp; 00444 } 00445 00446 00447 QgsStringMap QgsCptCityColorRampV2::properties() const 00448 { 00449 QgsStringMap map; 00450 map["schemeName"] = mSchemeName; 00451 map["variantName"] = mVariantName; 00452 return map; 00453 } 00454 00455 00456 QString QgsCptCityColorRampV2::fileName() const 00457 { 00458 if ( mSchemeName == "" ) 00459 return QString(); 00460 else 00461 { 00462 return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mSchemeName + mVariantName + ".svg"; 00463 } 00464 } 00465 00466 QString QgsCptCityColorRampV2::copyingFileName() const 00467 { 00468 return QgsCptCityArchive::findFileName( "COPYING.xml", QFileInfo( fileName() ).dir().path(), 00469 QgsCptCityArchive::defaultBaseDir() ); 00470 } 00471 00472 QString QgsCptCityColorRampV2::descFileName() const 00473 { 00474 return QgsCptCityArchive::findFileName( "DESC.xml", QFileInfo( fileName() ).dir().path(), 00475 QgsCptCityArchive::defaultBaseDir() ); 00476 } 00477 00478 QgsStringMap QgsCptCityColorRampV2::copyingInfo( ) const 00479 { 00480 return QgsCptCityArchive::copyingInfo( copyingFileName() ); 00481 } 00482 00483 bool QgsCptCityColorRampV2::loadFile() 00484 { 00485 if ( mFileLoaded ) 00486 { 00487 QgsDebugMsg( "File already loaded for " + mSchemeName + mVariantName ); 00488 return true; 00489 } 00490 00491 // get filename 00492 QString filename = fileName(); 00493 if ( filename.isNull() ) 00494 { 00495 QgsDebugMsg( "Couldn't get fileName() for " + mSchemeName + mVariantName ); 00496 return false; 00497 } 00498 00499 QgsDebugMsg( QString( "filename= %1 loaded=%2" ).arg( filename ).arg( mFileLoaded ) ); 00500 00501 // get color ramp from svg file 00502 QMap< double, QPair<QColor, QColor> > colorMap = 00503 QgsCptCityArchive::gradientColorMap( filename ); 00504 00505 // add colors to palette 00506 mFileLoaded = false; 00507 mStops.clear(); 00508 QMap<double, QPair<QColor, QColor> >::const_iterator it, prev; 00509 // first detect if file is gradient is continuous or dicrete 00510 // discrete: stop contains 2 colors and first color is identical to previous second 00511 // multi: stop contains 2 colors and no relation with previous stop 00512 mDiscrete = false; 00513 mMultiStops = false; 00514 it = prev = colorMap.constBegin(); 00515 while ( it != colorMap.constEnd() ) 00516 { 00517 // look for stops that contain multiple values 00518 if ( it != colorMap.constBegin() && ( it.value().first != it.value().second ) ) 00519 { 00520 if ( it.value().first == prev.value().second ) 00521 { 00522 mDiscrete = true; 00523 break; 00524 } 00525 else 00526 { 00527 mMultiStops = true; 00528 break; 00529 } 00530 } 00531 prev = it; 00532 ++it; 00533 } 00534 00535 // fill all stops 00536 it = prev = colorMap.constBegin(); 00537 while ( it != colorMap.constEnd() ) 00538 { 00539 if ( mDiscrete ) 00540 { 00541 // mPalette << qMakePair( it.key(), it.value().second ); 00542 mStops.append( QgsGradientStop( it.key(), it.value().second ) ); 00543 } 00544 else 00545 { 00546 // mPalette << qMakePair( it.key(), it.value().first ); 00547 mStops.append( QgsGradientStop( it.key(), it.value().first ) ); 00548 if (( mMultiStops ) && 00549 ( it.key() != 0.0 && it.key() != 1.0 ) ) 00550 { 00551 mStops.append( QgsGradientStop( it.key(), it.value().second ) ); 00552 } 00553 } 00554 prev = it; 00555 ++it; 00556 } 00557 00558 // remove first and last items (mColor1 and mColor2) 00559 if ( ! mStops.isEmpty() && mStops.first().offset == 0.0 ) 00560 mColor1 = mStops.takeFirst().color; 00561 if ( ! mStops.isEmpty() && mStops.last().offset == 1.0 ) 00562 mColor2 = mStops.takeLast().color; 00563 00564 mFileLoaded = true; 00565 return true; 00566 } 00567