mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
[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:
parent
6f45929d53
commit
a285aaaf4b
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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($$); }
|
||||
|
@ -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 )
|
||||
|
@ -82,9 +82,14 @@ class CORE_EXPORT QgsSearchTreeNode
|
||||
opTOREAL,
|
||||
opTOSTRING,
|
||||
|
||||
// coordinates
|
||||
opX,
|
||||
opY,
|
||||
|
||||
// measuring
|
||||
opLENGTH,
|
||||
opAREA,
|
||||
opPERIMETER,
|
||||
|
||||
// feature id
|
||||
opID,
|
||||
|
Loading…
x
Reference in New Issue
Block a user