[FEATURE] add $x, $y and $perimeter to field calculator

git-svn-id: http://svn.osgeo.org/qgis/trunk@15381 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
jef 2011-03-07 20:57:51 +00:00
parent 6f45929d53
commit a285aaaf4b
8 changed files with 144 additions and 35 deletions

View File

@ -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<QgsPoint>& points);

View File

@ -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();
}

View File

@ -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<QgsPoint> 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;
}

View File

@ -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<QgsPoint>& 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

View File

@ -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; }

View File

@ -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($$); }

View File

@ -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 )

View File

@ -82,9 +82,14 @@ class CORE_EXPORT QgsSearchTreeNode
opTOREAL,
opTOSTRING,
// coordinates
opX,
opY,
// measuring
opLENGTH,
opAREA,
opPERIMETER,
// feature id
opID,