Skip to content

Commit

Permalink
[FEATURE] add $x, $y and $perimeter to field calculator
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.osgeo.org/qgis/trunk@15381 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
jef committed Mar 7, 2011
1 parent 6f45929 commit a285aaa
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 35 deletions.
3 changes: 3 additions & 0 deletions python/core/qgsdistancearea.sip
Expand Up @@ -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);
Expand Down
20 changes: 11 additions & 9 deletions src/app/qgsfieldcalculator.cpp
Expand Up @@ -35,13 +35,16 @@ QgsFieldCalculator::QgsFieldCalculator( QgsVectorLayer* vl )
populateFields();
populateOutputFieldTypes();


//default values for field width and precision
mOuputFieldWidthSpinBox->setValue( 10 );
mOutputFieldPrecisionSpinBox->setValue( 3 );

//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 ) )
{
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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();
}

Expand Down
98 changes: 85 additions & 13 deletions src/core/qgsdistancearea.cpp
Expand Up @@ -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;

Expand All @@ -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 ) );
Expand Down Expand Up @@ -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 ) ) );
Expand All @@ -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
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -679,7 +753,6 @@ QString QgsDistanceArea::textUnit( double value, int decimals, QGis::UnitType u,
{
QString unitLabel;


switch ( u )
{
case QGis::Meters:
Expand Down Expand Up @@ -785,5 +858,4 @@ QString QgsDistanceArea::textUnit( double value, int decimals, QGis::UnitType u,


return QLocale::system().toString( value, 'f', decimals ) + unitLabel;

}
11 changes: 7 additions & 4 deletions src/core/qgsdistancearea.h
Expand Up @@ -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 );

Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/core/qgssearchstringlexer.ll
Expand Up @@ -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; }
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgssearchstringparser.yy
Expand Up @@ -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

Expand Down Expand Up @@ -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($$); }
Expand Down
33 changes: 24 additions & 9 deletions src/core/qgssearchtreenode.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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 += "?";
}
Expand Down Expand Up @@ -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() )
Expand Down Expand Up @@ -656,23 +659,35 @@ 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() )
{
return QgsSearchTreeValue( 2, "Geometry is 0" );
}

//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 )
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgssearchtreenode.h
Expand Up @@ -82,9 +82,14 @@ class CORE_EXPORT QgsSearchTreeNode
opTOREAL,
opTOSTRING,

// coordinates
opX,
opY,

// measuring
opLENGTH,
opAREA,
opPERIMETER,

// feature id
opID,
Expand Down

0 comments on commit a285aaa

Please sign in to comment.