Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #223 from homann/custom_ellipsoid
Custom ellipsoid for measurement and corresponding UI changes
  • Loading branch information
homann committed Sep 6, 2012
2 parents e109101 + 0748867 commit fb84775
Show file tree
Hide file tree
Showing 4 changed files with 358 additions and 248 deletions.
221 changes: 139 additions & 82 deletions src/app/qgsoptions.cpp
Expand Up @@ -16,6 +16,7 @@
* *
***************************************************************************/
#include "qgsapplication.h"
#include "qgsdistancearea.h"
#include "qgsoptions.h"
#include "qgis.h"
#include "qgisapp.h"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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() )
{
Expand Down Expand Up @@ -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 )
Expand All @@ -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;

//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 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;

}
// Add all items to selector

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 )
foreach ( i, mEllipsoidList )
{
if ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
myName = QString(( const char * )sqlite3_column_text( myPreparedStatement, 0 ) );
cmbEllipsoid->addItem( i.description );
}
// close the sqlite3 statement
sqlite3_finalize( myPreparedStatement );
sqlite3_close( myDatabase );
return myName;

}

QStringList QgsOptions::i18nList()
Expand Down Expand Up @@ -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" ) );
}
}
26 changes: 20 additions & 6 deletions src/app/qgsoptions.h
Expand Up @@ -25,6 +25,7 @@

#include <qgscoordinatereferencesystem.h>

#include <QList>

/**
* \class QgsOptions
Expand Down Expand Up @@ -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();
Expand All @@ -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<EllipsoidDefs> mEllipsoidList;
int mEllipsoidIndex;

//! Populates list with ellipsoids from Sqlite3 db
void populateEllipsoidList();

static const char * GEO_NONE_DESC;
};

Expand Down
24 changes: 23 additions & 1 deletion src/core/qgsdistancearea.cpp
Expand Up @@ -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:<semi-major axis>:<semi minor axis>
// 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 )
Expand Down Expand Up @@ -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 );
Expand Down

0 comments on commit fb84775

Please sign in to comment.