diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 8e37ee9e7ed..e917f7434f1 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -16,6 +16,7 @@ * * ***************************************************************************/ #include "qgsapplication.h" +#include "qgsdistancearea.h" #include "qgsoptions.h" #include "qgis.h" #include "qgisapp.h" @@ -77,6 +78,8 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WFlags fl ) : connect( spinFontSize, SIGNAL( valueChanged( const QString& ) ), this, SLOT( fontSizeChanged( const QString& ) ) ); + connect( cmbEllipsoid, SIGNAL( currentIndexChanged( int ) ), this, SLOT( updateEllipsoidUI( int ) ) ); + #ifdef Q_WS_X11 connect( chkEnableBackbuffer, SIGNAL( stateChanged( int ) ), this, SLOT( toggleEnableBackbuffer( int ) ) ); #endif @@ -279,24 +282,34 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WFlags fl ) : leProjectGlobalCrs->setText( mDefaultCrs.authid() + " - " + mDefaultCrs.description() ); // populate combo box with ellipsoids + QgsDebugMsg( "Setting upp ellipsoid" ); - getEllipsoidList(); - // Pre-select current ellipsoid - QString myEllipsoidId = settings.value( "/qgis/measure/ellipsoid", GEO_NONE ).toString(); - cmbEllipsoid->setCurrentIndex( cmbEllipsoid->findText( getEllipsoidName( myEllipsoidId ), Qt::MatchExactly ) ); - // Check if CRS transformation is on, or else turn combobox off - if ( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() ) + mEllipsoidIndex = 0; + populateEllipsoidList(); + + // Reading ellipsoid from setttings + QStringList mySplitEllipsoid = settings.value( "/qgis/measure/ellipsoid", GEO_NONE ).toString().split( ':' ); + + int myIndex = 0; + int i; + for ( i = 0; i < mEllipsoidList.length(); i++ ) { - cmbEllipsoid->setEnabled( true ); - cmbEllipsoid->setToolTip( "" ); + if ( mEllipsoidList[ i ].acronym.startsWith( mySplitEllipsoid[ 0 ] ) ) + { + myIndex = i; + break; + } } - else + // Update paramaters if present. + if ( mySplitEllipsoid.length() >= 3 ) { - cmbEllipsoid->setEnabled( false ); - cmbEllipsoid->setToolTip( "Can only use ellipsoidal calculations when CRS transformation is enabled" ); + mEllipsoidList[ myIndex ].semiMajor = mySplitEllipsoid[ 1 ].toDouble(); + mEllipsoidList[ myIndex ].semiMinor = mySplitEllipsoid[ 2 ].toDouble(); } + updateEllipsoidUI( myIndex ); + // Set the units for measuring QGis::UnitType myDisplayUnits = QGis::fromLiteral( settings.value( "/qgis/measure/displayunits", QGis::toLiteral( QGis::Meters ) ).toString() ); if ( myDisplayUnits == QGis::Feet ) @@ -940,7 +953,25 @@ void QgsOptions::saveOptions() settings.setValue( "/Projections/otfTransformEnabled", chkOtfTransform->isChecked() ); settings.setValue( "/Projections/projectDefaultCrs", mDefaultCrs.authid() ); - settings.setValue( "/qgis/measure/ellipsoid", getEllipsoidAcronym( cmbEllipsoid->currentText() ) ); + if ( mEllipsoidList[ mEllipsoidIndex ].acronym.startsWith( "PARAMETER" ) ) + { + double major = mEllipsoidList[ mEllipsoidIndex ].semiMajor; + double minor = mEllipsoidList[ mEllipsoidIndex ].semiMinor; + // If the user fields have changed, use them instead. + if ( leSemiMajor->isModified() || leSemiMinor->isModified() ) + { + QgsDebugMsg( "Using paramteric major/minor" ); + major = QLocale::system().toDouble( leSemiMajor->text() ); + minor = QLocale::system().toDouble( leSemiMinor->text() ); + } + settings.setValue( "/qgis/measure/ellipsoid", QString( "PARAMETER:%1:%2" ) + .arg( major, 0, 'g', 17 ) + .arg( minor, 0, 'g', 17 ) ); + } + else + { + settings.setValue( "/qgis/measure/ellipsoid", mEllipsoidList[ mEllipsoidIndex ].acronym ); + } if ( radFeet->isChecked() ) { @@ -1163,21 +1194,29 @@ bool QgsOptions::newVisible() { return chkAddedVisibility->isChecked(); } - -void QgsOptions::getEllipsoidList() +void QgsOptions::populateEllipsoidList() { - // (copied from qgscustomprojectiondialog.cpp) - // - // Populate the ellipsoid combo + // Populate the ellipsoid list // sqlite3 *myDatabase; const char *myTail; sqlite3_stmt *myPreparedStatement; int myResult; + EllipsoidDefs myItem, i; + myItem.acronym = GEO_NONE; + myItem.description = tr( GEO_NONE_DESC ); + myItem.semiMajor = 0.0; + myItem.semiMinor = 0.0; + mEllipsoidList.append( myItem ); + + myItem.acronym = QString( "PARAMETER:6370997:6370997" ); + myItem.description = tr( "Parameters :" ); + myItem.semiMajor = 6370997.0; + myItem.semiMinor = 6370997.0; + mEllipsoidList.append( myItem ); - cmbEllipsoid->addItem( tr( GEO_NONE_DESC ) ); //check the db is available myResult = sqlite3_open_v2( QgsApplication::srsDbFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, NULL ); if ( myResult ) @@ -1189,86 +1228,53 @@ void QgsOptions::getEllipsoidList() } // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list - QString mySql = "select * from tbl_ellipsoid order by name"; + QString mySql = "select acronym, name, radius, parameter2 from tbl_ellipsoid order by name"; myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail ); // XXX Need to free memory from the error msg if one is set if ( myResult == SQLITE_OK ) { while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW ) { - cmbEllipsoid->addItem(( const char * )sqlite3_column_text( myPreparedStatement, 1 ) ); + QString para1, para2; + myItem.acronym = ( const char * )sqlite3_column_text( myPreparedStatement, 0 ); + myItem.description = ( const char * )sqlite3_column_text( myPreparedStatement, 1 ); + + // Copied from QgsDistanecArea. Should perhaps be moved there somehow? + // No error checking, this values are for show only, never used in calculations. + + // Fall-back values + myItem.semiMajor = 0.0; + myItem.semiMinor = 0.0; + // Crash if no column? + para1 = ( const char * )sqlite3_column_text( myPreparedStatement, 2 ); + para2 = ( const char * )sqlite3_column_text( myPreparedStatement, 3 ); + myItem.semiMajor = para1.mid( 2 ).toDouble(); + if ( para2.left( 2 ) == "b=" ) + { + myItem.semiMinor = para2.mid( 2 ).toDouble(); + } + else if ( para2.left( 3 ) == "rf=" ) + { + double invFlattening = para2.mid( 3 ).toDouble(); + if ( invFlattening != 0.0 ) + { + myItem.semiMinor = myItem.semiMajor - ( myItem.semiMajor / invFlattening ); + } + } + mEllipsoidList.append( myItem ); } } + // close the sqlite3 statement sqlite3_finalize( myPreparedStatement ); sqlite3_close( myDatabase ); -} -QString QgsOptions::getEllipsoidAcronym( QString theEllipsoidName ) -{ - sqlite3 *myDatabase; - const char *myTail; - sqlite3_stmt *myPreparedStatement; - int myResult; - QString myName = GEO_NONE; + // Add all items to selector - //check the db is available - myResult = sqlite3_open_v2( QgsApplication::srsDbFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, NULL ); - if ( myResult ) + foreach ( i, mEllipsoidList ) { - QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) ); - // XXX This will likely never happen since on open, sqlite creates the - // database if it does not exist. - Q_ASSERT( myResult == 0 ); + cmbEllipsoid->addItem( i.description ); } - // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list - QString mySql = "select acronym from tbl_ellipsoid where name='" + theEllipsoidName + "'"; - myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail ); - // XXX Need to free memory from the error msg if one is set - if ( myResult == SQLITE_OK ) - { - if ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW ) - myName = QString(( const char * )sqlite3_column_text( myPreparedStatement, 0 ) ); - } - // close the sqlite3 statement - sqlite3_finalize( myPreparedStatement ); - sqlite3_close( myDatabase ); - return myName; - -} - -QString QgsOptions::getEllipsoidName( QString theEllipsoidAcronym ) -{ - sqlite3 *myDatabase; - const char *myTail; - sqlite3_stmt *myPreparedStatement; - int myResult; - QString myName; - - myName = tr( GEO_NONE_DESC ); - //check the db is available - myResult = sqlite3_open_v2( QgsApplication::srsDbFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, NULL ); - if ( myResult ) - { - QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) ); - // XXX This will likely never happen since on open, sqlite creates the - // database if it does not exist. - Q_ASSERT( myResult == 0 ); - } - // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list - QString mySql = "select name from tbl_ellipsoid where acronym='" + theEllipsoidAcronym + "'"; - myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail ); - // XXX Need to free memory from the error msg if one is set - if ( myResult == SQLITE_OK ) - { - if ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW ) - myName = QString(( const char * )sqlite3_column_text( myPreparedStatement, 0 ) ); - } - // close the sqlite3 statement - sqlite3_finalize( myPreparedStatement ); - sqlite3_close( myDatabase ); - return myName; - } QStringList QgsOptions::i18nList() @@ -1678,3 +1684,54 @@ void QgsOptions::saveContrastEnhancement( QComboBox *cbox, QString name ) QString value = cbox->itemData( cbox->currentIndex() ).toString(); settings.setValue( "/Raster/defaultContrastEnhancementAlgorithm/" + name, value ); } + +void QgsOptions::updateEllipsoidUI( int newIndex ) +{ + // Called whenever settings change, adjusts the UI accordingly + // Pre-select current ellipsoid + + // Check if CRS transformation is on, or else turn everything off + double myMajor = mEllipsoidList[ newIndex ].semiMajor; + double myMinor = mEllipsoidList[ newIndex ].semiMinor; + + // If user has modified the radii (only possible if parametric!), before + // changing ellipsoid, save the modified coordinates + if ( leSemiMajor->isModified() || leSemiMinor->isModified() ) + { + QgsDebugMsg( "Saving major/minor" ); + mEllipsoidList[ mEllipsoidIndex ].semiMajor = QLocale::system().toDouble( leSemiMajor->text() ); + mEllipsoidList[ mEllipsoidIndex ].semiMinor = QLocale::system().toDouble( leSemiMinor->text() ); + } + + mEllipsoidIndex = newIndex; + leSemiMajor->setEnabled( false ); + leSemiMinor->setEnabled( false ); + leSemiMajor->setText( "" ); + leSemiMinor->setText( "" ); + if ( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() ) + { + cmbEllipsoid->setEnabled( true ); + cmbEllipsoid->setToolTip( "" ); + if ( mEllipsoidList[ mEllipsoidIndex ].acronym.startsWith( "PARAMETER:" ) ) + { + leSemiMajor->setEnabled( true ); + leSemiMinor->setEnabled( true ); + } + else + { + leSemiMajor->setToolTip( QString( "Select %1 from pull-down menu to adjust radii" ).arg( tr( "Parameters:" ) ) ); + leSemiMinor->setToolTip( QString( "Select %1 from pull-down menu to adjust radii" ).arg( tr( "Parameters:" ) ) ); + } + cmbEllipsoid->setCurrentIndex( mEllipsoidIndex ); // Not always necessary + if ( mEllipsoidList[ mEllipsoidIndex ].acronym != GEO_NONE ) + { + leSemiMajor->setText( QLocale::system().toString( myMajor, 'f', 3 ) ); + leSemiMinor->setText( QLocale::system().toString( myMinor, 'f', 3 ) ); + } + } + else + { + cmbEllipsoid->setEnabled( false ); + cmbEllipsoid->setToolTip( tr( "Can only use ellipsoidal calculations when CRS transformation is enabled" ) ); + } +} diff --git a/src/app/qgsoptions.h b/src/app/qgsoptions.h index 9a1efb62df6..346c690265b 100644 --- a/src/app/qgsoptions.h +++ b/src/app/qgsoptions.h @@ -25,6 +25,7 @@ #include +#include /** * \class QgsOptions @@ -180,12 +181,11 @@ class QgsOptions : public QDialog, private Ui::QgsOptionsBase */ void saveGdalDriverList(); - protected: - //! Populates combo box with ellipsoids - void getEllipsoidList(); - - QString getEllipsoidAcronym( QString theEllipsoidName ); - QString getEllipsoidName( QString theEllipsoidAcronym ); + /* Update ComboBox accorindg to the selected new index + * Also sets the new selected Ellipsoid. + * @note added in 2.0 + */ + void updateEllipsoidUI( int newIndex ); private: QStringList i18nList(); @@ -195,6 +195,20 @@ class QgsOptions : public QDialog, private Ui::QgsOptionsBase QgsCoordinateReferenceSystem mLayerDefaultCrs; bool mLoadedGdalDriverList; + // List for all ellispods, also None and Custom + struct EllipsoidDefs + { + QString acronym; + QString description; + double semiMajor; + double semiMinor; + }; + QList mEllipsoidList; + int mEllipsoidIndex; + + //! Populates list with ellipsoids from Sqlite3 db + void populateEllipsoidList(); + static const char * GEO_NONE_DESC; }; diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 6773b462699..4c2ee92301f 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -130,6 +130,28 @@ bool QgsDistanceArea::setEllipsoid( const QString& ellipsoid ) return true; } + // Check if we have a custom projection, and set from text string. + // Format is "PARAMETER:: + // Numbers must be with (optional) decimal point and no other separators (C locale) + // Distances in meters. Flattening is calculated. + if ( ellipsoid.startsWith( "PARAMETER" ) ) + { + QStringList paramList = ellipsoid.split( ":" ); + bool semiMajorOk, semiMinorOk; + double semiMajor = paramList[1].toDouble( & semiMajorOk ); + double semiMinor = paramList[2].toDouble( & semiMinorOk ); + if ( semiMajorOk && semiMinorOk ) + { + return setEllipsoid( semiMajor, semiMinor ); + } + else + { + return false; + } + } + + // Continue with PROJ.4 list of ellipsoids. + //check the db is available myResult = sqlite3_open_v2( QgsApplication::srsDbFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, NULL ); if ( myResult ) @@ -213,7 +235,7 @@ bool QgsDistanceArea::setEllipsoid( const QString& ellipsoid ) // Also, b = a-(a/invf) bool QgsDistanceArea::setEllipsoid( double semiMajor, double semiMinor ) { - mEllipsoid = "PARAMETER"; + mEllipsoid = QString( "PARAMETER:%1:%2" ).arg( semiMajor ).arg( semiMinor ); mSemiMajor = semiMajor; mSemiMinor = semiMinor; mInvFlattening = mSemiMajor / ( mSemiMajor - mSemiMinor ); diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 0631ee45229..496fca89da3 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -6,7 +6,7 @@ 0 0 - 810 + 808 674 @@ -33,7 +33,7 @@ - 0 + 4 @@ -66,7 +66,7 @@ 0 0 - 772 + 770 1062 @@ -1003,7 +1003,7 @@ 0 0 - 1012 + 1010 815 @@ -1518,8 +1518,8 @@ 0 0 - 772 - 724 + 770 + 757 @@ -1585,131 +1585,6 @@ - - - - Measure tool - - - - - - Ellipsoid for distance calculations - - - - - - - - - - Rubberband color - - - - - - - - 100 - 0 - - - - - - - - - - - Qt::Horizontal - - - - 191 - 20 - - - - - - - - Preferred measurements units - - - - - - - Meters - - - - - - - Feet - - - - - - - Preferred angle units - - - - - - - Degrees - - - - - - - Radians - - - - - - - Gon - - - - - - - Decimal places - - - - - - - - - - Keep base unit - - - - - - - - - - - - - @@ -1719,30 +1594,6 @@ 11 - - - - - Zoom - - - - - Zoom and recenter - - - - - Zoom to mouse cursor - - - - - Nothing - - - - @@ -1770,6 +1621,30 @@ + + + + + Zoom + + + + + Zoom and recenter + + + + + Zoom to mouse cursor + + + + + Nothing + + + + @@ -1857,6 +1732,151 @@ + + + + Measure tool + + + + + + Semi-minor + + + + + + + Rubberband color + + + + + + + + 100 + 0 + + + + + + + + + + + Preferred measurements units + + + + + + + Meters + + + + + + + Feet + + + + + + + Preferred angle units + + + + + + + Degrees + + + + + + + Radians + + + + + + + Decimal places + + + + + + + + + + Keep base unit + + + + + + + + + + + + + + + + + Semi-major + + + + + + + Gon + + + + + + + + + + + + + Ellipsoid for distance calculations + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + @@ -1888,7 +1908,7 @@ 0 0 - 788 + 786 585 @@ -1969,7 +1989,7 @@ 0 0 - 772 + 770 606 @@ -2345,7 +2365,7 @@ 0 0 - 788 + 786 585 @@ -2532,7 +2552,7 @@ 0 0 - 788 + 786 585 @@ -2629,7 +2649,7 @@ 0 0 - 788 + 786 585 @@ -2920,9 +2940,6 @@ radFeet mDegreesRadioButton mRadiansRadioButton - mGonRadioButton - cmbWheelAction - spinZoomFactor scrollArea_4 mOverlayAlgorithmComboBox scrollArea_5