Skip to content
Permalink
Browse files
Merge pull request #1338 from manisandro/line_multi_labeling
Line multi labeling
  • Loading branch information
mhugent committed May 23, 2014
2 parents be706ed + a3b9d6c commit 24867c76159b58a027bc619d0f1352bab978a29a
Showing with 240 additions and 5 deletions.
  1. +15 −0 src/app/qgslabelinggui.cpp
  2. +72 −3 src/core/pal/layer.cpp
  3. +1 −1 src/core/pal/layer.h
  4. +4 −0 src/core/pal/util.h
  5. +51 −1 src/core/qgspallabeling.cpp
  6. +6 −0 src/core/qgspallabeling.h
  7. +91 −0 src/ui/qgslabelingguibase.ui
@@ -62,6 +62,7 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
mShadowRadiusUnitWidget->setUnits( QStringList() << tr( "mm" ) << tr( "map units" ), 1 );
mPointOffsetUnitWidget->setUnits( QStringList() << tr( "mm" ) << tr( "map units" ), 1 );
mLineDistanceUnitWidget->setUnits( QStringList() << tr( "mm" ) << tr( "map units" ), 1 );
mRepeatDistanceUnitWidget->setUnits( QStringList() << tr( "mm" ) << tr( "map units" ), 1 );

mCharDlg = new QgsCharacterSelectorDialog( this );

@@ -308,6 +309,11 @@ void QgsLabelingGui::init()
chkLineOrientationDependent->setChecked( true );
}

// Label repeat distance
mRepeatDistanceSpinBox->setValue( lyr.repeatDistance );
mRepeatDistanceUnitWidget->setUnit( lyr.repeatDistanceUnit - 1 );
mRepeatDistanceUnitWidget->setMapUnitScale( lyr.repeatDistanceMapUnitScale );

mPrioritySlider->setValue( lyr.priority );
chkNoObstacle->setChecked( lyr.obstacle );
chkLabelPerFeaturePart->setChecked( lyr.labelPerPart );
@@ -591,6 +597,9 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
qFatal( "Invalid settings" );
}

lyr.repeatDistance = mRepeatDistanceSpinBox->value();
lyr.repeatDistanceUnit = static_cast<QgsPalLayerSettings::SizeUnit>(1 + mRepeatDistanceUnitWidget->getUnit());
lyr.repeatDistanceMapUnitScale = mRepeatDistanceUnitWidget->getMapUnitScale();

lyr.textColor = btnTextColor->color();
lyr.textFont = mRefFont;
@@ -779,6 +788,8 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
// TODO: is this necessary? maybe just use the data defined-only rotation?
//setDataDefinedProperty( mPointAngleDDBtn, QgsPalLayerSettings::OffsetRotation, lyr );
setDataDefinedProperty( mMaxCharAngleDDBtn, QgsPalLayerSettings::CurvedCharAngleInOut, lyr );
setDataDefinedProperty( mRepeatDistanceDDBtn, QgsPalLayerSettings::RepeatDistance, lyr );
setDataDefinedProperty( mRepeatDistanceUnitDDBtn, QgsPalLayerSettings::RepeatDistanceUnit, lyr );

// data defined-only
setDataDefinedProperty( mCoordXDDBtn, QgsPalLayerSettings::PositionX, lyr );
@@ -1004,6 +1015,10 @@ void QgsLabelingGui::populateDataDefinedButtons( QgsPalLayerSettings& s )
// QgsDataDefinedButton::AnyType, QgsDataDefinedButton::double180RotDesc() );
mMaxCharAngleDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::CurvedCharAngleInOut ),
QgsDataDefinedButton::AnyType, tr( "double coord [<b>in,out</b> as 20.0-60.0,20.0-95.0]" ) );
mRepeatDistanceDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::RepeatDistance ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doublePosDesc() );
mRepeatDistanceUnitDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::DistanceUnits ),
QgsDataDefinedButton::String, QgsDataDefinedButton::unitsMmMuDesc() );

// data defined-only
QString ddPlaceInfo = tr( "In edit mode, layer's relevant labeling map tool is:<br>"
@@ -39,6 +39,7 @@
#include <iostream>
#include <cstring>
#include <cmath>
#include <vector>


#include <pal/pal.h>
@@ -228,7 +229,7 @@ namespace pal

bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText,
double labelPosX, double labelPosY, bool fixedPos, double angle, bool fixedAngle,
int xQuadOffset, int yQuadOffset, double xOffset, double yOffset, bool alwaysShow )
int xQuadOffset, int yQuadOffset, double xOffset, double yOffset, bool alwaysShow, double repeatDistance )
{
if ( !geom_id || label_x < 0 || label_y < 0 )
return false;
@@ -284,6 +285,74 @@ namespace pal
throw InternalException::UnknownGeometry();
}

// if multiple labels are requested for lines, split the line in pieces of desired distance
if(repeatDistance > 0) {
int nSimpleGeometries = simpleGeometries->size();
for(int i = 0; i < nSimpleGeometries; ++i) {
const GEOSGeometry* geom = simpleGeometries->pop_front();
if(GEOSGeomTypeId(geom) == GEOS_LINESTRING) {

// get number of points
int n = GEOSGeomGetNumPoints(geom);

// Read points
std::vector<Point> points(n);
for(int i = 0; i < n; ++i) {
GEOSGeometry* p = GEOSGeomGetPointN(geom, i);
GEOSGeomGetX(p, &points[i].x);
GEOSGeomGetY(p, &points[i].y);
GEOSGeom_destroy(p);
}

// Cumulative length vector
std::vector<double> len(n, 0);
for(int i = 1; i < n; ++i) {
double dx = points[i].x - points[i - 1].x;
double dy = points[i].y - points[i - 1].y;
len[i] = len[i - 1] + std::sqrt(dx * dx + dy * dy);
}

// Walk along line
int cur = 0;
double lambda = 0;
std::vector<Point> part;
while(true) {
lambda += repeatDistance;
for(; cur < n && lambda > len[cur]; ++cur) {
part.push_back(points[cur]);
}
if(cur >= n) {
break;
}
double c = (lambda - len[cur - 1]) / (len[cur] - len[cur - 1]);
Point p;
p.x = points[cur - 1].x + c * (points[cur].x - points[cur - 1].x);
p.y = points[cur - 1].y + c * (points[cur].y - points[cur - 1].y);
part.push_back(p);
GEOSCoordSequence* cooSeq = GEOSCoordSeq_create(part.size(), 2);
for(std::size_t i = 0; i < part.size(); ++i) {
GEOSCoordSeq_setX(cooSeq, i, part[i].x);
GEOSCoordSeq_setY(cooSeq, i, part[i].y);
}

simpleGeometries->push_back(GEOSGeom_createLineString(cooSeq));
part.clear();
part.push_back(p);
}
// Create final part
part.push_back(points[n - 1]);
GEOSCoordSequence* cooSeq = GEOSCoordSeq_create(part.size(), 2);
for(std::size_t i = 0; i < part.size(); ++i) {
GEOSCoordSeq_setX(cooSeq, i, part[i].x);
GEOSCoordSeq_setY(cooSeq, i, part[i].y);
}
simpleGeometries->push_back(GEOSGeom_createLineString(cooSeq));
}else{
simpleGeometries->push_back( geom );
}
}
}

while ( simpleGeometries->size() > 0 )
{
const GEOSGeometry* geom = simpleGeometries->pop_front();
@@ -320,7 +389,7 @@ namespace pal
continue;
}

if ( mode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
if ( mode == LabelPerFeature && repeatDistance == 0.0 && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
{
if ( type == GEOS_LINESTRING )
GEOSLength( geom, &geom_size );
@@ -349,7 +418,7 @@ namespace pal
modMutex->unlock();

// if using only biggest parts...
if (( mode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL )
if (( (mode == LabelPerFeature && repeatDistance == 0.0) || f->fixedPosition() ) && biggest_part != NULL )
{
addFeaturePart( biggest_part, labelText );
first_feat = false;
@@ -317,7 +317,7 @@ namespace pal
const char* labelText = NULL, double labelPosX = 0.0, double labelPosY = 0.0,
bool fixedPos = false, double angle = 0.0, bool fixedAngle = false,
int xQuadOffset = 0, int yQuadOffset = 0, double xOffset = 0.0, double yOffset = 0.0,
bool alwaysShow = false );
bool alwaysShow = false, double repeatDistance = 0.0 );

/** return pointer to feature or NULL if doesn't exist */
Feature* getFeature( const char* geom_id );
@@ -74,6 +74,10 @@ namespace pal
int new_label;
} ElemTrans;

struct Point {
double x, y;
};



#define EPSILON 1e-9
@@ -322,6 +322,8 @@ QgsPalLayerSettings::QgsPalLayerSettings()
maxCurvedCharAngleIn = 20.0;
maxCurvedCharAngleOut = -20.0;
priority = 5;
repeatDistance = 0;
repeatDistanceUnit = MM;

// rendering
scaleVisibility = false;
@@ -513,6 +515,9 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
maxCurvedCharAngleIn = s.maxCurvedCharAngleIn;
maxCurvedCharAngleOut = s.maxCurvedCharAngleOut;
priority = s.priority;
repeatDistance = s.repeatDistance;
repeatDistanceUnit = s.repeatDistanceUnit;
repeatDistanceMapUnitScale = s.repeatDistanceMapUnitScale;

// rendering
scaleVisibility = s.scaleVisibility;
@@ -1007,6 +1012,10 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 20.0 ) ).toDouble();
maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -20.0 ) ).toDouble();
priority = layer->customProperty( "labeling/priority" ).toInt();
repeatDistance = layer->customProperty( "labeling/repeatDistance", 0.0 ).toDouble();
repeatDistanceUnit = (SizeUnit) layer->customProperty( "labeling/repeatDistanceUnit", QVariant( MM )).toUInt();
repeatDistanceMapUnitScale.minScale = layer->customProperty( "labeling/repeatDistanceMapUnitMinScale", 0.0).toDouble();
repeatDistanceMapUnitScale.maxScale = layer->customProperty( "labeling/repeatDistanceMapUnitMaxScale", 0.0).toDouble();

// rendering
int scalemn = layer->customProperty( "labeling/scaleMin", QVariant( 0 ) ).toInt();
@@ -1173,6 +1182,10 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
layer->setCustomProperty( "labeling/priority", priority );
layer->setCustomProperty( "labeling/repeatDistance", repeatDistance );
layer->setCustomProperty( "labeling/repeatDistanceUnit", repeatDistanceUnit );
layer->setCustomProperty( "labeling/repeatDistanceMapUnitMinScale", repeatDistanceMapUnitScale.minScale );
layer->setCustomProperty( "labeling/repeatDistanceMapUnitMaxScale", repeatDistanceMapUnitScale.maxScale );

// rendering
layer->setCustomProperty( "labeling/scaleVisibility", scaleVisibility );
@@ -2219,12 +2232,48 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
#endif
lbl->setDefinedFont( labelFont );

// data defined repeat distance?
double repeatDist = repeatDistance;
if ( dataDefinedEvaluate( QgsPalLayerSettings::RepeatDistance, exprVal ) )
{
bool ok;
double distD = exprVal.toDouble( &ok );
if ( ok )
{
repeatDist = distD;
}
}

// data defined label-repeat distance units?
bool repeatdistinmapunit = repeatDistanceUnit == MapUnits;
if ( dataDefinedEvaluate( QgsPalLayerSettings::RepeatDistanceUnit, exprVal ) )
{
QString units = exprVal.toString().trimmed();
QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
if ( !units.isEmpty() )
{
repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
}
}

if ( repeatDist != 0 )
{
if ( !repeatdistinmapunit ) //convert distance from mm/map units to pixels
{
repeatDist *= repeatDistanceMapUnitScale.computeMapUnitsPerPixel(context) * context.scaleFactor();
}
else //mm
{
repeatDist *= vectorScaleFactor;
}
}

// feature to the layer
try
{
if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation,
quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow ) )
quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow, repeatDist ) )
return;
}
catch ( std::exception &e )
@@ -2279,6 +2328,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
}


//add parameters for data defined labeling to QgsPalGeometry
QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();
for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )
@@ -254,6 +254,8 @@ class CORE_EXPORT QgsPalLayerSettings
Hali = 11, //horizontal alignment for data defined label position (Left, Center, Right)
Vali = 12, //vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
Rotation = 14, //data defined rotation
RepeatDistance = 84,
RepeatDistanceUnit = 85,

// rendering
ScaleVisibility = 23,
@@ -377,6 +379,10 @@ class CORE_EXPORT QgsPalLayerSettings
bool distInMapUnits; //true if distance is in map units (otherwise in mm)
QgsMapUnitScale distMapUnitScale;

double repeatDistance;
SizeUnit repeatDistanceUnit;
QgsMapUnitScale repeatDistanceMapUnitScale;

// offset labels of point/centroid features default to center
// move label to quadrant: left/down, don't move, right/up (-1, 0, 1)
QuadrantPosition quadOffset;

0 comments on commit 24867c7

Please sign in to comment.