mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Merge pull request #1338 from manisandro/line_multi_labeling
Line multi labeling
This commit is contained in:
commit
24867c7615
@ -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;
|
||||
|
@ -3829,6 +3829,70 @@ font-style: italic;</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QFrame" name="mRepeatDistanceFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_24">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Repeat</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="mRepeatDistanceSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QgsDataDefinedButton" name="mRepeatDistanceDDBtn">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QgsUnitSelectionWidget" name="mRepeatDistanceUnitWidget" native="true"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QgsDataDefinedButton" name="mRepeatDistanceUnitDDBtn">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pagePolygon">
|
||||
@ -4720,6 +4784,33 @@ font-style: italic;</string>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="s">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_11">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="minimumSize">
|
||||
|
Loading…
x
Reference in New Issue
Block a user