Skip to content

Commit 24867c7

Browse files
committed
Merge pull request #1338 from manisandro/line_multi_labeling
Line multi labeling
2 parents be706ed + a3b9d6c commit 24867c7

File tree

7 files changed

+240
-5
lines changed

7 files changed

+240
-5
lines changed

src/app/qgslabelinggui.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
6262
mShadowRadiusUnitWidget->setUnits( QStringList() << tr( "mm" ) << tr( "map units" ), 1 );
6363
mPointOffsetUnitWidget->setUnits( QStringList() << tr( "mm" ) << tr( "map units" ), 1 );
6464
mLineDistanceUnitWidget->setUnits( QStringList() << tr( "mm" ) << tr( "map units" ), 1 );
65+
mRepeatDistanceUnitWidget->setUnits( QStringList() << tr( "mm" ) << tr( "map units" ), 1 );
6566

6667
mCharDlg = new QgsCharacterSelectorDialog( this );
6768

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

312+
// Label repeat distance
313+
mRepeatDistanceSpinBox->setValue( lyr.repeatDistance );
314+
mRepeatDistanceUnitWidget->setUnit( lyr.repeatDistanceUnit - 1 );
315+
mRepeatDistanceUnitWidget->setMapUnitScale( lyr.repeatDistanceMapUnitScale );
316+
311317
mPrioritySlider->setValue( lyr.priority );
312318
chkNoObstacle->setChecked( lyr.obstacle );
313319
chkLabelPerFeaturePart->setChecked( lyr.labelPerPart );
@@ -591,6 +597,9 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
591597
qFatal( "Invalid settings" );
592598
}
593599

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

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

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

10081023
// data defined-only
10091024
QString ddPlaceInfo = tr( "In edit mode, layer's relevant labeling map tool is:<br>"

src/core/pal/layer.cpp

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <iostream>
4040
#include <cstring>
4141
#include <cmath>
42+
#include <vector>
4243

4344

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

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

288+
// if multiple labels are requested for lines, split the line in pieces of desired distance
289+
if(repeatDistance > 0) {
290+
int nSimpleGeometries = simpleGeometries->size();
291+
for(int i = 0; i < nSimpleGeometries; ++i) {
292+
const GEOSGeometry* geom = simpleGeometries->pop_front();
293+
if(GEOSGeomTypeId(geom) == GEOS_LINESTRING) {
294+
295+
// get number of points
296+
int n = GEOSGeomGetNumPoints(geom);
297+
298+
// Read points
299+
std::vector<Point> points(n);
300+
for(int i = 0; i < n; ++i) {
301+
GEOSGeometry* p = GEOSGeomGetPointN(geom, i);
302+
GEOSGeomGetX(p, &points[i].x);
303+
GEOSGeomGetY(p, &points[i].y);
304+
GEOSGeom_destroy(p);
305+
}
306+
307+
// Cumulative length vector
308+
std::vector<double> len(n, 0);
309+
for(int i = 1; i < n; ++i) {
310+
double dx = points[i].x - points[i - 1].x;
311+
double dy = points[i].y - points[i - 1].y;
312+
len[i] = len[i - 1] + std::sqrt(dx * dx + dy * dy);
313+
}
314+
315+
// Walk along line
316+
int cur = 0;
317+
double lambda = 0;
318+
std::vector<Point> part;
319+
while(true) {
320+
lambda += repeatDistance;
321+
for(; cur < n && lambda > len[cur]; ++cur) {
322+
part.push_back(points[cur]);
323+
}
324+
if(cur >= n) {
325+
break;
326+
}
327+
double c = (lambda - len[cur - 1]) / (len[cur] - len[cur - 1]);
328+
Point p;
329+
p.x = points[cur - 1].x + c * (points[cur].x - points[cur - 1].x);
330+
p.y = points[cur - 1].y + c * (points[cur].y - points[cur - 1].y);
331+
part.push_back(p);
332+
GEOSCoordSequence* cooSeq = GEOSCoordSeq_create(part.size(), 2);
333+
for(std::size_t i = 0; i < part.size(); ++i) {
334+
GEOSCoordSeq_setX(cooSeq, i, part[i].x);
335+
GEOSCoordSeq_setY(cooSeq, i, part[i].y);
336+
}
337+
338+
simpleGeometries->push_back(GEOSGeom_createLineString(cooSeq));
339+
part.clear();
340+
part.push_back(p);
341+
}
342+
// Create final part
343+
part.push_back(points[n - 1]);
344+
GEOSCoordSequence* cooSeq = GEOSCoordSeq_create(part.size(), 2);
345+
for(std::size_t i = 0; i < part.size(); ++i) {
346+
GEOSCoordSeq_setX(cooSeq, i, part[i].x);
347+
GEOSCoordSeq_setY(cooSeq, i, part[i].y);
348+
}
349+
simpleGeometries->push_back(GEOSGeom_createLineString(cooSeq));
350+
}else{
351+
simpleGeometries->push_back( geom );
352+
}
353+
}
354+
}
355+
287356
while ( simpleGeometries->size() > 0 )
288357
{
289358
const GEOSGeometry* geom = simpleGeometries->pop_front();
@@ -320,7 +389,7 @@ namespace pal
320389
continue;
321390
}
322391

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

351420
// if using only biggest parts...
352-
if (( mode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL )
421+
if (( (mode == LabelPerFeature && repeatDistance == 0.0) || f->fixedPosition() ) && biggest_part != NULL )
353422
{
354423
addFeaturePart( biggest_part, labelText );
355424
first_feat = false;

src/core/pal/layer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ namespace pal
317317
const char* labelText = NULL, double labelPosX = 0.0, double labelPosY = 0.0,
318318
bool fixedPos = false, double angle = 0.0, bool fixedAngle = false,
319319
int xQuadOffset = 0, int yQuadOffset = 0, double xOffset = 0.0, double yOffset = 0.0,
320-
bool alwaysShow = false );
320+
bool alwaysShow = false, double repeatDistance = 0.0 );
321321

322322
/** return pointer to feature or NULL if doesn't exist */
323323
Feature* getFeature( const char* geom_id );

src/core/pal/util.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ namespace pal
7474
int new_label;
7575
} ElemTrans;
7676

77+
struct Point {
78+
double x, y;
79+
};
80+
7781

7882

7983
#define EPSILON 1e-9

src/core/qgspallabeling.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ QgsPalLayerSettings::QgsPalLayerSettings()
322322
maxCurvedCharAngleIn = 20.0;
323323
maxCurvedCharAngleOut = -20.0;
324324
priority = 5;
325+
repeatDistance = 0;
326+
repeatDistanceUnit = MM;
325327

326328
// rendering
327329
scaleVisibility = false;
@@ -513,6 +515,9 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
513515
maxCurvedCharAngleIn = s.maxCurvedCharAngleIn;
514516
maxCurvedCharAngleOut = s.maxCurvedCharAngleOut;
515517
priority = s.priority;
518+
repeatDistance = s.repeatDistance;
519+
repeatDistanceUnit = s.repeatDistanceUnit;
520+
repeatDistanceMapUnitScale = s.repeatDistanceMapUnitScale;
516521

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

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

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

2235+
// data defined repeat distance?
2236+
double repeatDist = repeatDistance;
2237+
if ( dataDefinedEvaluate( QgsPalLayerSettings::RepeatDistance, exprVal ) )
2238+
{
2239+
bool ok;
2240+
double distD = exprVal.toDouble( &ok );
2241+
if ( ok )
2242+
{
2243+
repeatDist = distD;
2244+
}
2245+
}
2246+
2247+
// data defined label-repeat distance units?
2248+
bool repeatdistinmapunit = repeatDistanceUnit == MapUnits;
2249+
if ( dataDefinedEvaluate( QgsPalLayerSettings::RepeatDistanceUnit, exprVal ) )
2250+
{
2251+
QString units = exprVal.toString().trimmed();
2252+
QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2253+
if ( !units.isEmpty() )
2254+
{
2255+
repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2256+
}
2257+
}
2258+
2259+
if ( repeatDist != 0 )
2260+
{
2261+
if ( !repeatdistinmapunit ) //convert distance from mm/map units to pixels
2262+
{
2263+
repeatDist *= repeatDistanceMapUnitScale.computeMapUnitsPerPixel(context) * context.scaleFactor();
2264+
}
2265+
else //mm
2266+
{
2267+
repeatDist *= vectorScaleFactor;
2268+
}
2269+
}
2270+
22222271
// feature to the layer
22232272
try
22242273
{
22252274
if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
22262275
xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation,
2227-
quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow ) )
2276+
quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow, repeatDist ) )
22282277
return;
22292278
}
22302279
catch ( std::exception &e )
@@ -2279,6 +2328,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
22792328
feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
22802329
}
22812330

2331+
22822332
//add parameters for data defined labeling to QgsPalGeometry
22832333
QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();
22842334
for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )

src/core/qgspallabeling.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ class CORE_EXPORT QgsPalLayerSettings
254254
Hali = 11, //horizontal alignment for data defined label position (Left, Center, Right)
255255
Vali = 12, //vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
256256
Rotation = 14, //data defined rotation
257+
RepeatDistance = 84,
258+
RepeatDistanceUnit = 85,
257259

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

382+
double repeatDistance;
383+
SizeUnit repeatDistanceUnit;
384+
QgsMapUnitScale repeatDistanceMapUnitScale;
385+
380386
// offset labels of point/centroid features default to center
381387
// move label to quadrant: left/down, don't move, right/up (-1, 0, 1)
382388
QuadrantPosition quadOffset;

0 commit comments

Comments
 (0)