Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] Add scalebar sizing mode to fit a desired scalebar width (fix
  • Loading branch information
manisandro authored and nyalldawson committed May 7, 2015
1 parent 479ef79 commit 45bdae4
Show file tree
Hide file tree
Showing 6 changed files with 433 additions and 63 deletions.
67 changes: 67 additions & 0 deletions python/core/composer/qgscomposerscalebar.sip
Expand Up @@ -25,6 +25,14 @@ class QgsComposerScaleBar: QgsComposerItem
NauticalMiles
};

/** Modes for setting size for scale bar segments
*/
enum SegmentSizeMode
{
SegmentSizeFixed, /*!< Scale bar segment size is fixed to a map unit*/
SegmentSizeFitWidth /*!< Scale bar segment size is calculated to fit a size range*/
};

QgsComposerScaleBar( QgsComposition* composition /TransferThis/ );
~QgsComposerScaleBar();

Expand All @@ -44,6 +52,65 @@ class QgsComposerScaleBar: QgsComposerItem
double numUnitsPerSegment() const;
void setNumUnitsPerSegment( double units );

/** Returns the size mode for scale bar segments.
* @see setSegmentSizeMode
* @see minBarWidth
* @see maxBarWidth
* @note added in QGIS 2.9
*/
SegmentSizeMode segmentSizeMode() const;

/** Sets the size mode for scale bar segments.
* @param mode size mode
* @see segmentSizeMode
* @see setMinBarWidth
* @see setMaxBarWidth
* @note added in QGIS 2.9
*/
void setSegmentSizeMode( SegmentSizeMode mode );

/** Returns the minimum size (in millimeters) for scale bar segments. This
* property is only effective if the @link segmentSizeMode @endlink is set
* to @link SegmentSizeFitWidth @endlink.
* @see segmentSizeMode
* @see setMinBarWidth
* @see maxBarWidth
* @note added in QGIS 2.9
*/
double minBarWidth() const;

/** Sets the minimum size (in millimeters) for scale bar segments. This
* property is only effective if the @link segmentSizeMode @endlink is set
* to @link SegmentSizeFitWidth @endlink.
* @param minWidth minimum width in millimeters
* @see minBarWidth
* @see setMaxBarWidth
* @see setSegmentSizeMode
* @note added in QGIS 2.9
*/
void setMinBarWidth( double minWidth );

/** Returns the maximum size (in millimeters) for scale bar segments. This
* property is only effective if the @link segmentSizeMode @endlink is set
* to @link SegmentSizeFitWidth @endlink.
* @see segmentSizeMode
* @see setMaxBarWidth
* @see minBarWidth
* @note added in QGIS 2.9
*/
double maxBarWidth() const;

/** Sets the maximum size (in millimeters) for scale bar segments. This
* property is only effective if the @link segmentSizeMode @endlink is set
* to @link SegmentSizeFitWidth @endlink.
* @param minWidth maximum width in millimeters
* @see minBarWidth
* @see setMaxBarWidth
* @see setSegmentSizeMode
* @note added in QGIS 2.9
*/
void setMaxBarWidth( double maxWidth );

double numMapUnitsPerScaleBarUnit() const;
void setNumMapUnitsPerScaleBarUnit( double d );

Expand Down
80 changes: 80 additions & 0 deletions src/app/composer/qgscomposerscalebarwidget.cpp
Expand Up @@ -32,6 +32,10 @@ QgsComposerScaleBarWidget::QgsComposerScaleBarWidget( QgsComposerScaleBar* scale
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, scaleBar );
mainLayout->addWidget( itemPropertiesWidget );

mSegmentSizeRadioGroup.addButton( mFixedSizeRadio );
mSegmentSizeRadioGroup.addButton( mFitWidthRadio );
connect( &mSegmentSizeRadioGroup, SIGNAL( buttonClicked( QAbstractButton* ) ), this, SLOT( segmentSizeRadioChanged( QAbstractButton* ) ) );

blockMemberSignals( true );

//style combo box
Expand Down Expand Up @@ -221,6 +225,23 @@ void QgsComposerScaleBarWidget::setGuiElements()
//units
mUnitsComboBox->setCurrentIndex( mUnitsComboBox->findData(( int )mComposerScaleBar->units() ) );

if ( mComposerScaleBar->segmentSizeMode() == QgsComposerScaleBar::SegmentSizeFixed )
{
mFixedSizeRadio->setChecked( true );
mSegmentSizeSpinBox->setEnabled( true );
mMinWidthSpinBox->setEnabled( false );
mMaxWidthSpinBox->setEnabled( false );
}
else /*if(mComposerScaleBar->segmentSizeMode() == QgsComposerScaleBar::SegmentSizeFitWidth)*/
{
mFitWidthRadio->setChecked( true );
mSegmentSizeSpinBox->setEnabled( false );
mMinWidthSpinBox->setEnabled( true );
mMaxWidthSpinBox->setEnabled( true );
}
mMinWidthSpinBox->setValue( mComposerScaleBar->minBarWidth() );
mMaxWidthSpinBox->setValue( mComposerScaleBar->maxBarWidth() );

blockMemberSignals( false );
}

Expand Down Expand Up @@ -621,6 +642,7 @@ void QgsComposerScaleBarWidget::blockMemberSignals( bool block )
mFillColorButton->blockSignals( block );
mFillColor2Button->blockSignals( block );
mStrokeColorButton->blockSignals( block );
mSegmentSizeRadioGroup.blockSignals( block );
}

void QgsComposerScaleBarWidget::connectUpdateSignal()
Expand Down Expand Up @@ -664,3 +686,61 @@ void QgsComposerScaleBarWidget::on_mLineCapStyleCombo_currentIndexChanged( int i
mComposerScaleBar->setLineCapStyle( mLineCapStyleCombo->penCapStyle() );
mComposerScaleBar->endCommand();
}

void QgsComposerScaleBarWidget::segmentSizeRadioChanged( QAbstractButton* radio )
{
bool fixedSizeMode = radio == mFixedSizeRadio;
mMinWidthSpinBox->setEnabled( !fixedSizeMode );
mMaxWidthSpinBox->setEnabled( !fixedSizeMode );
mSegmentSizeSpinBox->setEnabled( fixedSizeMode );

if ( !mComposerScaleBar )
{
return;
}

mComposerScaleBar->beginCommand( tr( "Scalebar segment size mode" ), QgsComposerMergeCommand::ScaleBarSegmentSize );
disconnectUpdateSignal();
if ( mFixedSizeRadio->isChecked() )
{
mComposerScaleBar->setSegmentSizeMode( QgsComposerScaleBar::SegmentSizeFixed );
mComposerScaleBar->setNumUnitsPerSegment( mSegmentSizeSpinBox->value() );
}
else /*if(mFitWidthRadio->isChecked())*/
{
mComposerScaleBar->setSegmentSizeMode( QgsComposerScaleBar::SegmentSizeFitWidth );
}
mComposerScaleBar->update();
connectUpdateSignal();
mComposerScaleBar->endCommand();
}

void QgsComposerScaleBarWidget::on_mMinWidthSpinBox_valueChanged( int )
{
if ( !mComposerScaleBar )
{
return;
}

mComposerScaleBar->beginCommand( tr( "Scalebar segment size mode" ), QgsComposerMergeCommand::ScaleBarSegmentSize );
disconnectUpdateSignal();
mComposerScaleBar->setMinBarWidth( mMinWidthSpinBox->value() );
mComposerScaleBar->update();
connectUpdateSignal();
mComposerScaleBar->endCommand();
}

void QgsComposerScaleBarWidget::on_mMaxWidthSpinBox_valueChanged( int )
{
if ( !mComposerScaleBar )
{
return;
}

mComposerScaleBar->beginCommand( tr( "Scalebar segment size mode" ), QgsComposerMergeCommand::ScaleBarSegmentSize );
disconnectUpdateSignal();
mComposerScaleBar->setMaxBarWidth( mMaxWidthSpinBox->value() );
mComposerScaleBar->update();
connectUpdateSignal();
mComposerScaleBar->endCommand();
}
4 changes: 4 additions & 0 deletions src/app/composer/qgscomposerscalebarwidget.h
Expand Up @@ -54,15 +54,19 @@ class QgsComposerScaleBarWidget: public QgsComposerItemBaseWidget, private Ui::Q
void on_mUnitsComboBox_currentIndexChanged( int index );
void on_mLineJoinStyleCombo_currentIndexChanged( int index );
void on_mLineCapStyleCombo_currentIndexChanged( int index );
void on_mMinWidthSpinBox_valueChanged( int i );
void on_mMaxWidthSpinBox_valueChanged( int i );

private slots:
void setGuiElements();
void segmentSizeRadioChanged( QAbstractButton*radio );

protected:
void showEvent( QShowEvent * event ) override;

private:
QgsComposerScaleBar* mComposerScaleBar;
QButtonGroup mSegmentSizeRadioGroup;

void refreshMapComboBox();
/**Enables/disables the signals of the input gui elements*/
Expand Down
111 changes: 106 additions & 5 deletions src/core/composer/qgscomposerscalebar.cpp
Expand Up @@ -39,6 +39,9 @@ QgsComposerScaleBar::QgsComposerScaleBar( QgsComposition* composition )
: QgsComposerItem( composition )
, mComposerMap( 0 )
, mNumUnitsPerSegment( 0 )
, mSegmentSizeMode( SegmentSizeFixed )
, mMinBarWidth( 50 )
, mMaxBarWidth( 150 )
, mFontColor( QColor( 0, 0, 0 ) )
, mStyle( 0 )
, mSegmentMillimeters( 0.0 )
Expand Down Expand Up @@ -114,6 +117,51 @@ void QgsComposerScaleBar::setNumUnitsPerSegment( double units )
emit itemChanged();
}

void QgsComposerScaleBar::setSegmentSizeMode( SegmentSizeMode mode )
{
if ( !mStyle )
{
mSegmentSizeMode = mode;
return;
}
double width = mStyle->calculateBoxSize().width();
mSegmentSizeMode = mode;
refreshSegmentMillimeters();
double widthAfter = mStyle->calculateBoxSize().width();
correctXPositionAlignment( width, widthAfter );
emit itemChanged();
}

void QgsComposerScaleBar::setMinBarWidth( double minWidth )
{
if ( !mStyle )
{
mMinBarWidth = minWidth;
return;
}
double width = mStyle->calculateBoxSize().width();
mMinBarWidth = minWidth;
refreshSegmentMillimeters();
double widthAfter = mStyle->calculateBoxSize().width();
correctXPositionAlignment( width, widthAfter );
emit itemChanged();
}

void QgsComposerScaleBar::setMaxBarWidth( double maxWidth )
{
if ( !mStyle )
{
mMaxBarWidth = maxWidth;
return;
}
double width = mStyle->calculateBoxSize().width();
mMaxBarWidth = maxWidth;
refreshSegmentMillimeters();
double widthAfter = mStyle->calculateBoxSize().width();
correctXPositionAlignment( width, widthAfter );
emit itemChanged();
}

void QgsComposerScaleBar::setNumSegmentsLeft( int nSegmentsLeft )
{
if ( !mStyle )
Expand Down Expand Up @@ -175,18 +223,65 @@ void QgsComposerScaleBar::invalidateCurrentMap()
mComposerMap = 0;
}

// nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc
inline double nextNiceNumber( double a, double d = 1 )
{
double s = pow10( floor( log10( a ) ) ) / d;
return ceil( a / s ) * s;
}

// prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc
inline double prevNiceNumber( double a, double d = 1 )
{
double s = pow10( floor( log10( a ) ) ) / d;
return floor( a / s ) * s;
}

void QgsComposerScaleBar::refreshSegmentMillimeters()
{
if ( mComposerMap )
{
//get extent of composer map
QgsRectangle composerMapRect = *( mComposerMap->currentMapExtent() );

//get mm dimension of composer map
QRectF composerItemRect = mComposerMap->rect();

//calculate size depending on mNumUnitsPerSegment
mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
if ( mSegmentSizeMode == SegmentSizeFixed )
{
//calculate size depending on mNumUnitsPerSegment
mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
}
else /*if(mSegmentSizeMode == SegmentSizeFitWidth)*/
{
if ( mMaxBarWidth < mMinBarWidth )
{
mSegmentMillimeters = 0;
}
else
{
double nSegments = ( mNumSegmentsLeft != 0 ) + mNumSegments;
// unitsPerSegments which fit minBarWidth resp. maxBarWidth
double minUnitsPerSeg = ( mMinBarWidth * mapWidth() ) / ( nSegments * composerItemRect.width() );
double maxUnitsPerSeg = ( mMaxBarWidth * mapWidth() ) / ( nSegments * composerItemRect.width() );

// Start with coarsest "nice" number closest to minUnitsPerSeg resp
// maxUnitsPerSeg, then proceed to finer numbers as long as neither
// lowerNiceUnitsPerSeg nor upperNiceUnitsPerSeg are are in
// [minUnitsPerSeg, maxUnitsPerSeg]
double lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg );
double upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg );

double d = 1;
while ( lowerNiceUnitsPerSeg > maxUnitsPerSeg && upperNiceUnitsPerSeg < minUnitsPerSeg )
{
d *= 10;
lowerNiceUnitsPerSeg = nextNiceNumber( minUnitsPerSeg, d );
upperNiceUnitsPerSeg = prevNiceNumber( maxUnitsPerSeg, d );
}

// Pick mNumUnitsPerSegment from {lowerNiceUnitsPerSeg, upperNiceUnitsPerSeg}, use the larger if possible
mNumUnitsPerSegment = upperNiceUnitsPerSeg < minUnitsPerSeg ? lowerNiceUnitsPerSeg : upperNiceUnitsPerSeg;
mSegmentMillimeters = composerItemRect.width() / mapWidth() * mNumUnitsPerSegment;
}
}
}
}

Expand Down Expand Up @@ -568,6 +663,9 @@ bool QgsComposerScaleBar::writeXML( QDomElement& elem, QDomDocument & doc ) cons
composerScaleBarElem.setAttribute( "numSegments", mNumSegments );
composerScaleBarElem.setAttribute( "numSegmentsLeft", mNumSegmentsLeft );
composerScaleBarElem.setAttribute( "numUnitsPerSegment", QString::number( mNumUnitsPerSegment ) );
composerScaleBarElem.setAttribute( "segmentSizeMode", mSegmentSizeMode );
composerScaleBarElem.setAttribute( "minBarWidth", mMinBarWidth );
composerScaleBarElem.setAttribute( "maxBarWidth", mMaxBarWidth );
composerScaleBarElem.setAttribute( "segmentMillimeters", QString::number( mSegmentMillimeters ) );
composerScaleBarElem.setAttribute( "numMapUnitsPerScaleBarUnit", QString::number( mNumMapUnitsPerScaleBarUnit ) );
composerScaleBarElem.setAttribute( "font", mFont.toString() );
Expand Down Expand Up @@ -646,6 +744,9 @@ bool QgsComposerScaleBar::readXML( const QDomElement& itemElem, const QDomDocume
mNumSegments = itemElem.attribute( "numSegments", "2" ).toInt();
mNumSegmentsLeft = itemElem.attribute( "numSegmentsLeft", "0" ).toInt();
mNumUnitsPerSegment = itemElem.attribute( "numUnitsPerSegment", "1.0" ).toDouble();
mSegmentSizeMode = static_cast<SegmentSizeMode>( itemElem.attribute( "segmentSizeMode", "0" ).toInt() );
mMinBarWidth = itemElem.attribute( "minBarWidth", "50" ).toInt();
mMaxBarWidth = itemElem.attribute( "maxBarWidth", "150" ).toInt();
mSegmentMillimeters = itemElem.attribute( "segmentMillimeters", "0.0" ).toDouble();
mNumMapUnitsPerScaleBarUnit = itemElem.attribute( "numMapUnitsPerScaleBarUnit", "1.0" ).toDouble();
mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
Expand Down

0 comments on commit 45bdae4

Please sign in to comment.