diff --git a/python/core/qgsdistancearea.sip b/python/core/qgsdistancearea.sip index c1be0e29c40..ff025d56387 100644 --- a/python/core/qgsdistancearea.sip +++ b/python/core/qgsdistancearea.sip @@ -49,6 +49,9 @@ class QgsDistanceArea //! general measurement (line distance or polygon area) double measure(QgsGeometry* geometry); + + //! measurement perimater of polygon + double measurePerimeter(QgsGeometry* geometry); //! measures line with more segments double measureLine(const QList& points); diff --git a/src/app/qgsfieldcalculator.cpp b/src/app/qgsfieldcalculator.cpp index 71925285fc4..d4d6c444f12 100644 --- a/src/app/qgsfieldcalculator.cpp +++ b/src/app/qgsfieldcalculator.cpp @@ -35,6 +35,7 @@ QgsFieldCalculator::QgsFieldCalculator( QgsVectorLayer* vl ) populateFields(); populateOutputFieldTypes(); + //default values for field width and precision mOuputFieldWidthSpinBox->setValue( 10 ); mOutputFieldPrecisionSpinBox->setValue( 3 ); @@ -42,6 +43,8 @@ QgsFieldCalculator::QgsFieldCalculator( QgsVectorLayer* vl ) //disable ok button until there is text for output field and expression mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false ); + mExistingFieldComboBox->setDisabled( true ); + // disable creation of new fields if not supported by data provider if ( !( vl->dataProvider()->capabilities() & QgsVectorDataProvider::AddAttributes ) ) { @@ -151,7 +154,12 @@ void QgsFieldCalculator::accept() // block layerModified signals (that would trigger table update) mVectorLayer->blockSignals( true ); - bool useGeometry = calcString.contains( "$area" ) || calcString.contains( "$length" ); + bool useGeometry = + calcString.contains( "$area" ) || + calcString.contains( "$length" ) || + calcString.contains( "$perimeter" ) || + calcString.contains( "$x" ) || + calcString.contains( "$y" ); int rownum = 1; mVectorLayer->select( mVectorLayer->pendingAllAttributesList(), QgsRectangle(), useGeometry, false ); @@ -267,14 +275,8 @@ void QgsFieldCalculator::populateOutputFieldTypes() void QgsFieldCalculator::on_mUpdateExistingFieldCheckBox_stateChanged( int state ) { - if ( state == Qt::Checked ) - { - mNewFieldGroupBox->setEnabled( false ); - } - else - { - mNewFieldGroupBox->setEnabled( true ); - } + mExistingFieldComboBox->setEnabled( state == Qt::Checked ); + mNewFieldGroupBox->setDisabled( state == Qt::Checked ); setOkButtonState(); } diff --git a/src/core/qgsdistancearea.cpp b/src/core/qgsdistancearea.cpp index 1ef4623825f..8c20216a025 100644 --- a/src/core/qgsdistancearea.cpp +++ b/src/core/qgsdistancearea.cpp @@ -223,7 +223,7 @@ double QgsDistanceArea::measure( QgsGeometry* geometry ) case QGis::WKBPolygon25D: hasZptr = true; case QGis::WKBPolygon: - measurePolygon( wkb, &res, hasZptr ); + measurePolygon( wkb, &res, 0, hasZptr ); QgsDebugMsg( "returning " + QString::number( res ) ); return res; @@ -234,7 +234,60 @@ double QgsDistanceArea::measure( QgsGeometry* geometry ) ptr = wkb + 9; for ( i = 0; i < count; i++ ) { - ptr = measurePolygon( ptr, &res, hasZptr ); + ptr = measurePolygon( ptr, &res, 0, hasZptr ); + resTotal += res; + } + QgsDebugMsg( "returning " + QString::number( resTotal ) ); + return resTotal; + + default: + QgsDebugMsg( QString( "measure: unexpected geometry type: %1" ).arg( wkbType ) ); + return 0; + } +} + +double QgsDistanceArea::measurePerimeter( QgsGeometry* geometry ) +{ + if ( !geometry ) + return 0.0; + + unsigned char* wkb = geometry->asWkb(); + if ( !wkb ) + return 0.0; + + unsigned char* ptr; + unsigned int wkbType; + double res, resTotal = 0; + int count, i; + + memcpy( &wkbType, ( wkb + 1 ), sizeof( wkbType ) ); + + // measure distance or area based on what is the type of geometry + bool hasZptr = false; + + switch ( wkbType ) + { + case QGis::WKBLineString25D: + case QGis::WKBLineString: + case QGis::WKBMultiLineString25D: + case QGis::WKBMultiLineString: + return 0.0; + + case QGis::WKBPolygon25D: + hasZptr = true; + case QGis::WKBPolygon: + measurePolygon( wkb, 0, &res, hasZptr ); + QgsDebugMsg( "returning " + QString::number( res ) ); + return res; + + case QGis::WKBMultiPolygon25D: + hasZptr = true; + case QGis::WKBMultiPolygon: + count = *(( int* )( wkb + 5 ) ); + ptr = wkb + 9; + for ( i = 0; i < count; i++ ) + { + ptr = measurePolygon( ptr, 0, &res, hasZptr ); resTotal += res; } QgsDebugMsg( "returning " + QString::number( resTotal ) ); @@ -344,7 +397,7 @@ double QgsDistanceArea::measureLine( const QgsPoint& p1, const QgsPoint& p2 ) } -unsigned char* QgsDistanceArea::measurePolygon( unsigned char* feature, double* area, bool hasZptr ) +unsigned char* QgsDistanceArea::measurePolygon( unsigned char* feature, double* area, double* perimeter, bool hasZptr ) { // get number of rings in the polygon unsigned int numRings = *(( int* )( feature + 1 + sizeof( int ) ) ); @@ -357,8 +410,11 @@ unsigned char* QgsDistanceArea::measurePolygon( unsigned char* feature, double* QList points; QgsPoint pnt; - double x, y, areaTmp; - *area = 0; + double x, y; + if ( area ) + *area = 0; + if ( perimeter ) + *perimeter = 0; try { @@ -392,20 +448,38 @@ unsigned char* QgsDistanceArea::measurePolygon( unsigned char* feature, double* if ( points.size() > 2 ) { - areaTmp = computePolygonArea( points ); - if ( idx == 0 ) - *area += areaTmp; // exterior ring - else - *area -= areaTmp; // interior rings + if ( area ) + { + double areaTmp = computePolygonArea( points ); + if ( idx == 0 ) + { + // exterior ring + *area += areaTmp; + } + else + { + *area -= areaTmp; // interior rings + } + } + + if ( perimeter ) + { + *perimeter += measureLine( points ); + } } points.clear(); + + if ( !area ) + { + break; + } } } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); - QgsLogger::warning( QObject::tr( "Caught a coordinate system exception while trying to transform a point. Unable to calculate polygon area." ) ); + QgsLogger::warning( QObject::tr( "Caught a coordinate system exception while trying to transform a point. Unable to calculate polygon area or perimeter." ) ); } return ptr; @@ -679,7 +753,6 @@ QString QgsDistanceArea::textUnit( double value, int decimals, QGis::UnitType u, { QString unitLabel; - switch ( u ) { case QGis::Meters: @@ -785,5 +858,4 @@ QString QgsDistanceArea::textUnit( double value, int decimals, QGis::UnitType u, return QLocale::system().toString( value, 'f', decimals ) + unitLabel; - } diff --git a/src/core/qgsdistancearea.h b/src/core/qgsdistancearea.h index 3df7b2c2fd3..0cd5a422077 100644 --- a/src/core/qgsdistancearea.h +++ b/src/core/qgsdistancearea.h @@ -77,8 +77,12 @@ class CORE_EXPORT QgsDistanceArea //! general measurement (line distance or polygon area) double measure( QgsGeometry* geometry ); - //! measures line with more segments + //! measures perimeter of polygon + double measurePerimeter( QgsGeometry* geometry ); + + //! measures line double measureLine( const QList& points ); + //! measures line with one segment double measureLine( const QgsPoint& p1, const QgsPoint& p2 ); @@ -91,11 +95,10 @@ class CORE_EXPORT QgsDistanceArea static QString textUnit( double value, int decimals, QGis::UnitType u, bool isArea, bool keepBaseUnit = false ); protected: - //! measures line distance, line points are extracted from WKB unsigned char* measureLine( unsigned char* feature, double* area, bool hasZptr = false ); - //! measures polygon area, vertices are extracted from WKB - unsigned char* measurePolygon( unsigned char* feature, double* area, bool hasZptr = false ); + //! measures polygon area and perimeter, vertices are extracted from WKB + unsigned char* measurePolygon( unsigned char* feature, double* area, double* perimeter, bool hasZptr = false ); /** calculates distance from two points on ellipsoid diff --git a/src/core/qgssearchstringlexer.ll b/src/core/qgssearchstringlexer.ll index eb200d805c4..e9f5f135c12 100644 --- a/src/core/qgssearchstringlexer.ll +++ b/src/core/qgssearchstringlexer.ll @@ -121,6 +121,9 @@ string "'"{str_char}*"'" "$area" { return AREA; } "$length" { return LENGTH; } +"$perimeter" { return PERIMETER; } +"$x" { return X; } +"$y" { return Y; } "$id" { return ID; } {column_ref} { return COLUMN_REF; } diff --git a/src/core/qgssearchstringparser.yy b/src/core/qgssearchstringparser.yy index 227831f3fdc..cc0915b3036 100644 --- a/src/core/qgssearchstringparser.yy +++ b/src/core/qgssearchstringparser.yy @@ -74,7 +74,10 @@ void addToTmpNodes(QgsSearchTreeNode* node); %token IN %token ROWNUM %token AREA +%token PERIMETER %token LENGTH +%token X +%token Y %token ID %token NULLVALUE @@ -177,7 +180,10 @@ scalar_exp: | scalar_exp CONCAT scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opCONCAT, $1, $3); joinTmpNodes($$, $1, $3); } | ROWNUM { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opROWNUM, 0, 0); addToTmpNodes($$); } | AREA { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opAREA, 0, 0); addToTmpNodes($$); } + | PERIMETER { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPERIMETER, 0, 0); addToTmpNodes($$); } | LENGTH { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opLENGTH, 0, 0); addToTmpNodes($$); } + | X { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opX, 0, 0); addToTmpNodes($$); } + | Y { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opY, 0, 0); addToTmpNodes($$); } | ID { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opID, 0, 0); addToTmpNodes($$); } | NUMBER { $$ = new QgsSearchTreeNode($1); addToTmpNodes($$); } | STRING { $$ = new QgsSearchTreeNode(QString::fromUtf8(yytext), 0); addToTmpNodes($$); } diff --git a/src/core/qgssearchtreenode.cpp b/src/core/qgssearchtreenode.cpp index de1d1677057..a99d3028be4 100644 --- a/src/core/qgssearchtreenode.cpp +++ b/src/core/qgssearchtreenode.cpp @@ -139,7 +139,7 @@ void QgsSearchTreeNode::init() { mCalc = NULL; - if ( mType == tOperator && ( mOp == opLENGTH || mOp == opAREA ) ) + if ( mType == tOperator && ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER ) ) { //initialize QgsDistanceArea mCalc = new QgsDistanceArea; @@ -232,14 +232,17 @@ QString QgsSearchTreeNode::makeSearchString() // currently all functions take one parameter str += QString( "(%1)" ).arg( mLeft->makeSearchString() ); } - else if ( mOp == opLENGTH || mOp == opAREA || mOp == opROWNUM || mOp == opID ) + else if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opROWNUM || mOp == opID || mOp == opX || mOp == opY ) { // special nullary opeators switch ( mOp ) { case opLENGTH: str += "$length"; break; case opAREA: str += "$area"; break; + case opPERIMETER: str += "$perimeter"; break; case opROWNUM: str += "$rownum"; break; + case opX: str += "$x"; break; + case opY: str += "$y"; break; case opID: str += "$id"; break; default: str += "?"; } @@ -361,7 +364,7 @@ bool QgsSearchTreeNode::needsGeometry() { if ( mType == tOperator ) { - if ( mOp == opLENGTH || mOp == opAREA ) + if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY ) return true; if ( mLeft && mLeft->needsGeometry() ) @@ -656,7 +659,7 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q if ( !getValue( value2, mRight, fields, f ) ) return value2; } - if ( mOp == opLENGTH || mOp == opAREA ) + if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY ) { if ( !f.geometry() ) { @@ -664,15 +667,27 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q } //check that we don't use area for lines or length for polygons - if ( mOp == opLENGTH && f.geometry()->type() != QGis::Line ) + if ( mOp == opLENGTH && f.geometry()->type() == QGis::Line ) { - return QgsSearchTreeValue( 0 ); + return QgsSearchTreeValue( mCalc->measure( f.geometry() ) ); } - if ( mOp == opAREA && f.geometry()->type() != QGis::Polygon ) + if ( mOp == opAREA && f.geometry()->type() == QGis::Polygon ) { - return QgsSearchTreeValue( 0 ); + return QgsSearchTreeValue( mCalc->measure( f.geometry() ) ); } - return QgsSearchTreeValue( mCalc->measure( f.geometry() ) ); + if ( mOp == opPERIMETER && f.geometry()->type() == QGis::Polygon ) + { + return QgsSearchTreeValue( mCalc->measurePerimeter( f.geometry() ) ); + } + if ( mOp == opX && f.geometry()->type() == QGis::Point ) + { + return QgsSearchTreeValue( f.geometry()->asPoint().x() ); + } + if ( mOp == opY && f.geometry()->type() == QGis::Point ) + { + return QgsSearchTreeValue( f.geometry()->asPoint().y() ); + } + return QgsSearchTreeValue( 0 ); } if ( mOp == opID ) diff --git a/src/core/qgssearchtreenode.h b/src/core/qgssearchtreenode.h index c4fc47da9c5..7ca38405b37 100644 --- a/src/core/qgssearchtreenode.h +++ b/src/core/qgssearchtreenode.h @@ -82,9 +82,14 @@ class CORE_EXPORT QgsSearchTreeNode opTOREAL, opTOSTRING, + // coordinates + opX, + opY, + // measuring opLENGTH, opAREA, + opPERIMETER, // feature id opID,