Skip to content

Commit 9f2cb21

Browse files
author
jef
committedMar 7, 2011
[FEATURE] add $x, $y and $perimeter to field calculator
git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@15381 c8812cc2-4d05-0410-92ff-de0c093fc19c

File tree

8 files changed

+144
-35
lines changed

8 files changed

+144
-35
lines changed
 

‎python/core/qgsdistancearea.sip

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ class QgsDistanceArea
4949

5050
//! general measurement (line distance or polygon area)
5151
double measure(QgsGeometry* geometry);
52+
53+
//! measurement perimater of polygon
54+
double measurePerimeter(QgsGeometry* geometry);
5255

5356
//! measures line with more segments
5457
double measureLine(const QList<QgsPoint>& points);

‎src/app/qgsfieldcalculator.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@ QgsFieldCalculator::QgsFieldCalculator( QgsVectorLayer* vl )
3535
populateFields();
3636
populateOutputFieldTypes();
3737

38+
3839
//default values for field width and precision
3940
mOuputFieldWidthSpinBox->setValue( 10 );
4041
mOutputFieldPrecisionSpinBox->setValue( 3 );
4142

4243
//disable ok button until there is text for output field and expression
4344
mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
4445

46+
mExistingFieldComboBox->setDisabled( true );
47+
4548
// disable creation of new fields if not supported by data provider
4649
if ( !( vl->dataProvider()->capabilities() & QgsVectorDataProvider::AddAttributes ) )
4750
{
@@ -151,7 +154,12 @@ void QgsFieldCalculator::accept()
151154
// block layerModified signals (that would trigger table update)
152155
mVectorLayer->blockSignals( true );
153156

154-
bool useGeometry = calcString.contains( "$area" ) || calcString.contains( "$length" );
157+
bool useGeometry =
158+
calcString.contains( "$area" ) ||
159+
calcString.contains( "$length" ) ||
160+
calcString.contains( "$perimeter" ) ||
161+
calcString.contains( "$x" ) ||
162+
calcString.contains( "$y" );
155163
int rownum = 1;
156164

157165
mVectorLayer->select( mVectorLayer->pendingAllAttributesList(), QgsRectangle(), useGeometry, false );
@@ -267,14 +275,8 @@ void QgsFieldCalculator::populateOutputFieldTypes()
267275

268276
void QgsFieldCalculator::on_mUpdateExistingFieldCheckBox_stateChanged( int state )
269277
{
270-
if ( state == Qt::Checked )
271-
{
272-
mNewFieldGroupBox->setEnabled( false );
273-
}
274-
else
275-
{
276-
mNewFieldGroupBox->setEnabled( true );
277-
}
278+
mExistingFieldComboBox->setEnabled( state == Qt::Checked );
279+
mNewFieldGroupBox->setDisabled( state == Qt::Checked );
278280
setOkButtonState();
279281
}
280282

‎src/core/qgsdistancearea.cpp

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ double QgsDistanceArea::measure( QgsGeometry* geometry )
223223
case QGis::WKBPolygon25D:
224224
hasZptr = true;
225225
case QGis::WKBPolygon:
226-
measurePolygon( wkb, &res, hasZptr );
226+
measurePolygon( wkb, &res, 0, hasZptr );
227227
QgsDebugMsg( "returning " + QString::number( res ) );
228228
return res;
229229

@@ -234,7 +234,60 @@ double QgsDistanceArea::measure( QgsGeometry* geometry )
234234
ptr = wkb + 9;
235235
for ( i = 0; i < count; i++ )
236236
{
237-
ptr = measurePolygon( ptr, &res, hasZptr );
237+
ptr = measurePolygon( ptr, &res, 0, hasZptr );
238+
resTotal += res;
239+
}
240+
QgsDebugMsg( "returning " + QString::number( resTotal ) );
241+
return resTotal;
242+
243+
default:
244+
QgsDebugMsg( QString( "measure: unexpected geometry type: %1" ).arg( wkbType ) );
245+
return 0;
246+
}
247+
}
248+
249+
double QgsDistanceArea::measurePerimeter( QgsGeometry* geometry )
250+
{
251+
if ( !geometry )
252+
return 0.0;
253+
254+
unsigned char* wkb = geometry->asWkb();
255+
if ( !wkb )
256+
return 0.0;
257+
258+
unsigned char* ptr;
259+
unsigned int wkbType;
260+
double res, resTotal = 0;
261+
int count, i;
262+
263+
memcpy( &wkbType, ( wkb + 1 ), sizeof( wkbType ) );
264+
265+
// measure distance or area based on what is the type of geometry
266+
bool hasZptr = false;
267+
268+
switch ( wkbType )
269+
{
270+
case QGis::WKBLineString25D:
271+
case QGis::WKBLineString:
272+
case QGis::WKBMultiLineString25D:
273+
case QGis::WKBMultiLineString:
274+
return 0.0;
275+
276+
case QGis::WKBPolygon25D:
277+
hasZptr = true;
278+
case QGis::WKBPolygon:
279+
measurePolygon( wkb, 0, &res, hasZptr );
280+
QgsDebugMsg( "returning " + QString::number( res ) );
281+
return res;
282+
283+
case QGis::WKBMultiPolygon25D:
284+
hasZptr = true;
285+
case QGis::WKBMultiPolygon:
286+
count = *(( int* )( wkb + 5 ) );
287+
ptr = wkb + 9;
288+
for ( i = 0; i < count; i++ )
289+
{
290+
ptr = measurePolygon( ptr, 0, &res, hasZptr );
238291
resTotal += res;
239292
}
240293
QgsDebugMsg( "returning " + QString::number( resTotal ) );
@@ -344,7 +397,7 @@ double QgsDistanceArea::measureLine( const QgsPoint& p1, const QgsPoint& p2 )
344397
}
345398

346399

347-
unsigned char* QgsDistanceArea::measurePolygon( unsigned char* feature, double* area, bool hasZptr )
400+
unsigned char* QgsDistanceArea::measurePolygon( unsigned char* feature, double* area, double* perimeter, bool hasZptr )
348401
{
349402
// get number of rings in the polygon
350403
unsigned int numRings = *(( int* )( feature + 1 + sizeof( int ) ) );
@@ -357,8 +410,11 @@ unsigned char* QgsDistanceArea::measurePolygon( unsigned char* feature, double*
357410

358411
QList<QgsPoint> points;
359412
QgsPoint pnt;
360-
double x, y, areaTmp;
361-
*area = 0;
413+
double x, y;
414+
if ( area )
415+
*area = 0;
416+
if ( perimeter )
417+
*perimeter = 0;
362418

363419
try
364420
{
@@ -392,20 +448,38 @@ unsigned char* QgsDistanceArea::measurePolygon( unsigned char* feature, double*
392448

393449
if ( points.size() > 2 )
394450
{
395-
areaTmp = computePolygonArea( points );
396-
if ( idx == 0 )
397-
*area += areaTmp; // exterior ring
398-
else
399-
*area -= areaTmp; // interior rings
451+
if ( area )
452+
{
453+
double areaTmp = computePolygonArea( points );
454+
if ( idx == 0 )
455+
{
456+
// exterior ring
457+
*area += areaTmp;
458+
}
459+
else
460+
{
461+
*area -= areaTmp; // interior rings
462+
}
463+
}
464+
465+
if ( perimeter )
466+
{
467+
*perimeter += measureLine( points );
468+
}
400469
}
401470

402471
points.clear();
472+
473+
if ( !area )
474+
{
475+
break;
476+
}
403477
}
404478
}
405479
catch ( QgsCsException &cse )
406480
{
407481
Q_UNUSED( cse );
408-
QgsLogger::warning( QObject::tr( "Caught a coordinate system exception while trying to transform a point. Unable to calculate polygon area." ) );
482+
QgsLogger::warning( QObject::tr( "Caught a coordinate system exception while trying to transform a point. Unable to calculate polygon area or perimeter." ) );
409483
}
410484

411485
return ptr;
@@ -679,7 +753,6 @@ QString QgsDistanceArea::textUnit( double value, int decimals, QGis::UnitType u,
679753
{
680754
QString unitLabel;
681755

682-
683756
switch ( u )
684757
{
685758
case QGis::Meters:
@@ -785,5 +858,4 @@ QString QgsDistanceArea::textUnit( double value, int decimals, QGis::UnitType u,
785858

786859

787860
return QLocale::system().toString( value, 'f', decimals ) + unitLabel;
788-
789861
}

‎src/core/qgsdistancearea.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,12 @@ class CORE_EXPORT QgsDistanceArea
7777
//! general measurement (line distance or polygon area)
7878
double measure( QgsGeometry* geometry );
7979

80-
//! measures line with more segments
80+
//! measures perimeter of polygon
81+
double measurePerimeter( QgsGeometry* geometry );
82+
83+
//! measures line
8184
double measureLine( const QList<QgsPoint>& points );
85+
8286
//! measures line with one segment
8387
double measureLine( const QgsPoint& p1, const QgsPoint& p2 );
8488

@@ -91,11 +95,10 @@ class CORE_EXPORT QgsDistanceArea
9195
static QString textUnit( double value, int decimals, QGis::UnitType u, bool isArea, bool keepBaseUnit = false );
9296

9397
protected:
94-
9598
//! measures line distance, line points are extracted from WKB
9699
unsigned char* measureLine( unsigned char* feature, double* area, bool hasZptr = false );
97-
//! measures polygon area, vertices are extracted from WKB
98-
unsigned char* measurePolygon( unsigned char* feature, double* area, bool hasZptr = false );
100+
//! measures polygon area and perimeter, vertices are extracted from WKB
101+
unsigned char* measurePolygon( unsigned char* feature, double* area, double* perimeter, bool hasZptr = false );
99102

100103
/**
101104
calculates distance from two points on ellipsoid

‎src/core/qgssearchstringlexer.ll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ string "'"{str_char}*"'"
121121
122122
"$area" { return AREA; }
123123
"$length" { return LENGTH; }
124+
"$perimeter" { return PERIMETER; }
125+
"$x" { return X; }
126+
"$y" { return Y; }
124127
"$id" { return ID; }
125128
126129
{column_ref} { return COLUMN_REF; }

‎src/core/qgssearchstringparser.yy

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ void addToTmpNodes(QgsSearchTreeNode* node);
7474
%token IN
7575
%token ROWNUM
7676
%token AREA
77+
%token PERIMETER
7778
%token LENGTH
79+
%token X
80+
%token Y
7881
%token ID
7982
%token NULLVALUE
8083

@@ -177,7 +180,10 @@ scalar_exp:
177180
| scalar_exp CONCAT scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opCONCAT, $1, $3); joinTmpNodes($$, $1, $3); }
178181
| ROWNUM { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opROWNUM, 0, 0); addToTmpNodes($$); }
179182
| AREA { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opAREA, 0, 0); addToTmpNodes($$); }
183+
| PERIMETER { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPERIMETER, 0, 0); addToTmpNodes($$); }
180184
| LENGTH { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opLENGTH, 0, 0); addToTmpNodes($$); }
185+
| X { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opX, 0, 0); addToTmpNodes($$); }
186+
| Y { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opY, 0, 0); addToTmpNodes($$); }
181187
| ID { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opID, 0, 0); addToTmpNodes($$); }
182188
| NUMBER { $$ = new QgsSearchTreeNode($1); addToTmpNodes($$); }
183189
| STRING { $$ = new QgsSearchTreeNode(QString::fromUtf8(yytext), 0); addToTmpNodes($$); }

‎src/core/qgssearchtreenode.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ void QgsSearchTreeNode::init()
139139
{
140140
mCalc = NULL;
141141

142-
if ( mType == tOperator && ( mOp == opLENGTH || mOp == opAREA ) )
142+
if ( mType == tOperator && ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER ) )
143143
{
144144
//initialize QgsDistanceArea
145145
mCalc = new QgsDistanceArea;
@@ -232,14 +232,17 @@ QString QgsSearchTreeNode::makeSearchString()
232232
// currently all functions take one parameter
233233
str += QString( "(%1)" ).arg( mLeft->makeSearchString() );
234234
}
235-
else if ( mOp == opLENGTH || mOp == opAREA || mOp == opROWNUM || mOp == opID )
235+
else if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opROWNUM || mOp == opID || mOp == opX || mOp == opY )
236236
{
237237
// special nullary opeators
238238
switch ( mOp )
239239
{
240240
case opLENGTH: str += "$length"; break;
241241
case opAREA: str += "$area"; break;
242+
case opPERIMETER: str += "$perimeter"; break;
242243
case opROWNUM: str += "$rownum"; break;
244+
case opX: str += "$x"; break;
245+
case opY: str += "$y"; break;
243246
case opID: str += "$id"; break;
244247
default: str += "?";
245248
}
@@ -361,7 +364,7 @@ bool QgsSearchTreeNode::needsGeometry()
361364
{
362365
if ( mType == tOperator )
363366
{
364-
if ( mOp == opLENGTH || mOp == opAREA )
367+
if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY )
365368
return true;
366369

367370
if ( mLeft && mLeft->needsGeometry() )
@@ -656,23 +659,35 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q
656659
if ( !getValue( value2, mRight, fields, f ) ) return value2;
657660
}
658661

659-
if ( mOp == opLENGTH || mOp == opAREA )
662+
if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY )
660663
{
661664
if ( !f.geometry() )
662665
{
663666
return QgsSearchTreeValue( 2, "Geometry is 0" );
664667
}
665668

666669
//check that we don't use area for lines or length for polygons
667-
if ( mOp == opLENGTH && f.geometry()->type() != QGis::Line )
670+
if ( mOp == opLENGTH && f.geometry()->type() == QGis::Line )
668671
{
669-
return QgsSearchTreeValue( 0 );
672+
return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
670673
}
671-
if ( mOp == opAREA && f.geometry()->type() != QGis::Polygon )
674+
if ( mOp == opAREA && f.geometry()->type() == QGis::Polygon )
672675
{
673-
return QgsSearchTreeValue( 0 );
676+
return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
674677
}
675-
return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
678+
if ( mOp == opPERIMETER && f.geometry()->type() == QGis::Polygon )
679+
{
680+
return QgsSearchTreeValue( mCalc->measurePerimeter( f.geometry() ) );
681+
}
682+
if ( mOp == opX && f.geometry()->type() == QGis::Point )
683+
{
684+
return QgsSearchTreeValue( f.geometry()->asPoint().x() );
685+
}
686+
if ( mOp == opY && f.geometry()->type() == QGis::Point )
687+
{
688+
return QgsSearchTreeValue( f.geometry()->asPoint().y() );
689+
}
690+
return QgsSearchTreeValue( 0 );
676691
}
677692

678693
if ( mOp == opID )

‎src/core/qgssearchtreenode.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,14 @@ class CORE_EXPORT QgsSearchTreeNode
8282
opTOREAL,
8383
opTOSTRING,
8484

85+
// coordinates
86+
opX,
87+
opY,
88+
8589
// measuring
8690
opLENGTH,
8791
opAREA,
92+
opPERIMETER,
8893

8994
// feature id
9095
opID,

0 commit comments

Comments
 (0)
Please sign in to comment.