Skip to content

Commit

Permalink
Added expression based labels, simple dialog for entering expressions.
Browse files Browse the repository at this point in the history
Code still needs some clean up and testing.
  • Loading branch information
NathanW2 committed Jun 4, 2011
1 parent 0e318b0 commit 2b5fb21
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 54 deletions.
60 changes: 49 additions & 11 deletions src/app/qgslabelinggui.cpp
Expand Up @@ -24,13 +24,15 @@

#include "qgspallabeling.h"
#include "qgslabelengineconfigdialog.h"
#include "qgssearchstring.h"
#include "qgsexpressionbuilder.h"

#include <QColorDialog>
#include <QFontDialog>

#include <QTextEdit>
#include <iostream>
#include <QApplication>

#include <QMessageBox>


QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, QWidget* parent )
Expand All @@ -44,6 +46,7 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
connect( btnBufferColor, SIGNAL( clicked() ), this, SLOT( changeBufferColor() ) );
connect( spinBufferSize, SIGNAL( valueChanged( double ) ), this, SLOT( updatePreview() ) );
connect( btnEngineSettings, SIGNAL( clicked() ), this, SLOT( showEngineConfigDialog() ) );
connect( btnExpression, SIGNAL(clicked()), this, SLOT( showExpressionDialog()));

// set placement methods page based on geometry type
switch ( layer->geometryType() )
Expand All @@ -65,12 +68,14 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
label_19->setEnabled( layer->geometryType() != QGis::Point );
mMinSizeSpinBox->setEnabled( layer->geometryType() != QGis::Point );

populateFieldNames();

// load labeling settings from layer
QgsPalLayerSettings lyr;
lyr.readFromLayer( layer );
populateFieldNames();

//Add the current expression to the bottom of the list.
if (lyr.isExpression)
cboFieldName->addItem(lyr.fieldName);
populateDataDefinedCombos( lyr );

// placement
Expand Down Expand Up @@ -184,18 +189,23 @@ QgsLabelingGui::~QgsLabelingGui()

void QgsLabelingGui::apply()
{
layerSettings().writeToLayer( mLayer );
// trigger refresh
if ( mMapCanvas )
{
mMapCanvas->refresh();
}
QgsPalLayerSettings settings = layerSettings();
// If we get here we are good to go.
settings.writeToLayer( mLayer );
// trigger refresh
if ( mMapCanvas )
{
mMapCanvas->refresh();
}
}

QgsPalLayerSettings QgsLabelingGui::layerSettings()
{
QgsPalLayerSettings lyr;
lyr.fieldName = cboFieldName->currentText();
// Check if we are an expression. Also treats expressions with just a column name as non expressions,
// this saves time later so we don't have to parse the expression tree.
lyr.isExpression = mLayer->fieldNameIndex( lyr.fieldName ) == -1;

lyr.dist = 0;
lyr.placementFlags = 0;
Expand Down Expand Up @@ -300,7 +310,6 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
return lyr;
}


void QgsLabelingGui::populateFieldNames()
{
const QgsFieldMap& fields = mLayer->pendingFields();
Expand Down Expand Up @@ -449,6 +458,35 @@ void QgsLabelingGui::showEngineConfigDialog()
dlg.exec();
}

void QgsLabelingGui::showExpressionDialog()
{
QDialog* dlg = new QDialog();
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
| QDialogButtonBox::Cancel);
QgsExpressionBuilder* builder = new QgsExpressionBuilder();
QGridLayout* layout = new QGridLayout();
dlg->setLayout(layout);
layout->addWidget(builder);
layout->addWidget(buttonBox);

if ( dlg->exec() )
{
QString expression = builder->getExpressionString();
//Do validation here first before applying
QgsSearchString searchString;
if ( !searchString.setString( expression ) )
{
//expression not valid
QMessageBox::critical( 0, "Syntax error",
"Invalid expression syntax. The error message of the parser is: '" + searchString.parserErrorMsg() + "'" );
return;
}

cboFieldName->addItem(expression);
cboFieldName->setCurrentIndex(cboFieldName->count() - 1);
}
}

void QgsLabelingGui::updateUi()
{
// enable/disable scale-based, buffer
Expand Down
1 change: 1 addition & 0 deletions src/app/qgslabelinggui.h
Expand Up @@ -41,6 +41,7 @@ class QgsLabelingGui : public QDialog, private Ui::QgsLabelingGuiBase
void changeTextColor();
void changeTextFont();
void showEngineConfigDialog();
void showExpressionDialog();
void changeBufferColor();

void updateUi();
Expand Down
31 changes: 27 additions & 4 deletions src/core/qgslabel.cpp
Expand Up @@ -34,10 +34,14 @@
#include "qgsmaptopixel.h"
#include "qgscoordinatetransform.h"
#include "qgsrendercontext.h"
#include "qgssearchtreenode.h"
#include "qgssearchstring.h"

#include "qgslabelattributes.h"
#include "qgslabel.h"

#include <QMessageBox>

// use M_PI define PI 3.141592654
#ifdef WIN32
#undef M_PI
Expand Down Expand Up @@ -103,16 +107,37 @@ void QgsLabel::renderLabel( QgsRenderContext &renderContext,
double x2 = point.x();
double scale = ( x2 - x1 ) * 0.001;

QgsSearchString searchString;
if ( !searchString.setString( " to string (Diameter) + 'mm'" ) )
{
//expression not valid
QMessageBox::critical( 0, "Syntax error", "Invalid expression syntax. The error message of the parser is: '" + searchString.parserErrorMsg() + "'" );
return;
}

//get QgsSearchTreeNode
QgsSearchTreeNode* searchTree = searchString.tree();
if ( !searchTree )
{
return;
}

QgsSearchTreeValue outValue;
searchTree->getValue( outValue, searchTree, mField , feature );
if (outValue.isError())
QMessageBox::critical( 0, "Error in expression tree" , "Error" );
text = outValue.string();

/* Text */
value = fieldValue( Text, feature );
/*value = fieldValue( Text, feature );
if ( value.isEmpty() )
{
text = mLabelAttributes->text();
}
else
{
text = value;
}
} */

/* Font */
value = fieldValue( Family, feature );
Expand Down Expand Up @@ -1030,8 +1055,6 @@ void QgsLabel::readXML( const QDomNode& node )

} // QgsLabel::readXML()



void QgsLabel::writeXML( QDomNode & layer_node, QDomDocument & document ) const
{
QDomElement labelattributes = document.createElement( "labelattributes" );
Expand Down
60 changes: 48 additions & 12 deletions src/core/qgspallabeling.cpp
Expand Up @@ -41,13 +41,16 @@
#include "qgsdiagram.h"
#include "qgsdiagramrendererv2.h"
#include "qgslabelsearchtree.h"
#include "qgssearchtreenode.h"
#include "qgssearchstring.h"

#include <qgslogger.h>
#include <qgsvectorlayer.h>
#include <qgsmaplayerregistry.h>
#include <qgsvectordataprovider.h>
#include <qgsgeometry.h>
#include <qgsmaprenderer.h>

#include <QMessageBox>

using namespace pal;

Expand Down Expand Up @@ -155,6 +158,7 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
{
// copy only permanent stuff
fieldName = s.fieldName;
isExpression = s.isExpression;
placement = s.placement;
placementFlags = s.placementFlags;
textFont = s.textFont;
Expand Down Expand Up @@ -276,6 +280,7 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
return; // there's no information available

fieldName = layer->customProperty( "labeling/fieldName" ).toString();
isExpression = layer->customProperty( "labeling/isExpression").toBool();
placement = ( Placement ) layer->customProperty( "labeling/placement" ).toInt();
placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
QString fontFamily = layer->customProperty( "labeling/fontFamily" ).toString();
Expand Down Expand Up @@ -311,6 +316,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
layer->setCustomProperty( "labeling", "pal" );

layer->setCustomProperty( "labeling/fieldName", fieldName );
layer->setCustomProperty( "labeling/isExpression", isExpression );
layer->setCustomProperty( "labeling/placement", placement );
layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );

Expand Down Expand Up @@ -427,9 +433,36 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString t
labelY = qAbs( ptSize.y() - ptZero.y() );
}

void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext& context )
void QgsPalLayerSettings::registerFeature(QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context )
{
QString labelText = f.attributeMap()[fieldIndex].toString();
QString labelText;
if (isExpression)
{
QgsSearchString searchString;
// We don't do any validating here as we should only have a vaild expression at this point.
searchString.setString( fieldName );

QgsSearchTreeNode* searchTree = searchString.tree();
if ( !searchTree )
{
return;
}

QgsSearchTreeValue outValue;
searchTree->getValue( outValue, searchTree, layer->dataProvider()->fields() , f );

if (outValue.isError())
{
QgsDebugMsg("EXPRESSION ERROR = " + outValue.string());
return;
}
QgsDebugMsg("EXPRESSION OUT VALUE = " + outValue.string());
labelText = outValue.string();
}
else
{
labelText = f.attributeMap()[fieldIndex].toString();
}
double labelX, labelY; // will receive label size
QFont labelFont = textFont;

Expand Down Expand Up @@ -569,7 +602,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
// record the created geometry - it will be deleted at the end.
geometries.append( lbl );

// register feature to the layer
// feature to the layer
try
{
if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
Expand Down Expand Up @@ -689,11 +722,15 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices,
if ( !lyrTmp.enabled )
return 0;

// find out which field will be needed
int fldIndex = layer->fieldNameIndex( lyrTmp.fieldName );
if ( fldIndex == -1 )
return 0;
attrIndices.insert( fldIndex );
// If we aren't an expression, we check to see if we can find the column.
int fldIndex ;
if (!lyrTmp.isExpression)
{
fldIndex = layer->fieldNameIndex( lyrTmp.fieldName );
if ( fldIndex == -1)
return 0;
attrIndices.insert( fldIndex );
}

//add indices of data defined fields
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator dIt = lyrTmp.dataDefinedProperties.constBegin();
Expand Down Expand Up @@ -782,7 +819,7 @@ int QgsPalLabeling::addDiagramLayer( QgsVectorLayer* layer, QgsDiagramLayerSetti
void QgsPalLabeling::registerFeature( QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context )
{
QgsPalLayerSettings& lyr = mActiveLayers[layer];
lyr.registerFeature( f, context );
lyr.registerFeature(layer, f, context );
}

void QgsPalLabeling::registerDiagramFeature( QgsVectorLayer* layer, QgsFeature& feat, const QgsRenderContext& context )
Expand Down Expand Up @@ -836,7 +873,7 @@ void QgsPalLabeling::registerDiagramFeature( QgsVectorLayer* layer, QgsFeature&
}
}

// register feature to the layer
// feature to the layer
int ddColX = layerIt.value().xPosColumn;
int ddColY = layerIt.value().yPosColumn;
double ddPosX = 0.0;
Expand Down Expand Up @@ -1212,7 +1249,6 @@ void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QPainter* painter, co

// TODO: optimize access :)
const QgsPalLayerSettings& lyr = layer( label->getLayerName() );

QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );

Expand Down
3 changes: 2 additions & 1 deletion src/core/qgspallabeling.h
Expand Up @@ -98,6 +98,7 @@ class CORE_EXPORT QgsPalLayerSettings
};

QString fieldName;
bool isExpression;
Placement placement;
unsigned int placementFlags;
QFont textFont;
Expand Down Expand Up @@ -125,7 +126,7 @@ class CORE_EXPORT QgsPalLayerSettings
void calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY );

// implementation of register feature hook
void registerFeature( QgsFeature& f, const QgsRenderContext& context );
void registerFeature(QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context );

void readFromLayer( QgsVectorLayer* layer );
void writeToLayer( QgsVectorLayer* layer );
Expand Down

0 comments on commit 2b5fb21

Please sign in to comment.