Skip to content

Commit

Permalink
[FEATURE][needs-docs] Custom SVG path and size for the north arrow de…
Browse files Browse the repository at this point in the history
…coration (#6715)
  • Loading branch information
nirvn committed Mar 30, 2018
1 parent 8ebd47b commit 13d0556
Show file tree
Hide file tree
Showing 5 changed files with 350 additions and 227 deletions.
229 changes: 121 additions & 108 deletions src/app/qgsdecorationnortharrow.cpp
Expand Up @@ -70,6 +70,8 @@ void QgsDecorationNorthArrow::projectRead()
QgsDecorationItem::projectRead();
mColor = QgsSymbolLayerUtils::decodeColor( QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Color" ), QStringLiteral( "#000000" ) ) );
mOutlineColor = QgsSymbolLayerUtils::decodeColor( QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/OutlineColor" ), QStringLiteral( "#FFFFFF" ) ) );
mSize = QgsProject::instance()->readDoubleEntry( mNameConfig, QStringLiteral( "/Size" ), 16.0 );
mSvgPath = QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/SvgPath" ), QString() );
mRotationInt = QgsProject::instance()->readNumEntry( mNameConfig, QStringLiteral( "/Rotation" ), 0 );
mAutomatic = QgsProject::instance()->readBoolEntry( mNameConfig, QStringLiteral( "/Automatic" ), true );
mMarginHorizontal = QgsProject::instance()->readNumEntry( mNameConfig, QStringLiteral( "/MarginH" ), 0 );
Expand All @@ -81,7 +83,8 @@ void QgsDecorationNorthArrow::saveToProject()
QgsDecorationItem::saveToProject();
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/OutlineColor" ), QgsSymbolLayerUtils::encodeColor( mOutlineColor ) );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Rotation" ), mRotationInt );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Size" ), mSize );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/SvgPath" ), mSvgPath );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Automatic" ), mAutomatic );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/MarginH" ), mMarginHorizontal );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/MarginV" ), mMarginVertical );
Expand All @@ -94,128 +97,138 @@ void QgsDecorationNorthArrow::run()
dlg.exec();
}

QString QgsDecorationNorthArrow::svgPath()
{
if ( !mSvgPath.isEmpty() )
{
QString resolvedPath = QgsSymbolLayerUtils::svgSymbolNameToPath( mSvgPath, QgsProject::instance()->pathResolver() );
bool validSvg = QFileInfo::exists( resolvedPath );
if ( validSvg )
{
return resolvedPath;
}
}

return QStringLiteral( ":/images/north_arrows/default.svg" );
}

void QgsDecorationNorthArrow::render( const QgsMapSettings &mapSettings, QgsRenderContext &context )
{
if ( !enabled() )
return;

//Large IF statement controlled by enable checkbox
if ( enabled() )
{
QSize size( 64, 64 );
QSvgRenderer svg;
double maxLength = mSize * mapSettings.outputDpi() / 25.4;
QSvgRenderer svg;

const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( QStringLiteral( ":/images/north_arrows/default.svg" ), size.width(), mColor, mOutlineColor, 1.0, 1.0 );
svg.load( svgContent );
const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( svgPath(), maxLength, mColor, mOutlineColor, 1.0, 1.0 );
svg.load( svgContent );

if ( svg.isValid() )
if ( svg.isValid() )
{
QSize size( maxLength, maxLength );
QRectF viewBox = svg.viewBoxF();
if ( viewBox.height() > viewBox.width() )
{
double centerXDouble = size.width() / 2.0;
double centerYDouble = size.width() / 2.0;

//save the current canvas rotation
context.painter()->save();
//
//work out how to shift the image so that it rotates
// properly about its center
//(x cos a + y sin a - x, -x sin a + y cos a - y)
//

// could move this call to somewhere else so that it is only
// called when the projection or map extent changes
if ( mAutomatic )
{
try
{
mRotationInt = QgsBearingUtils:: bearingTrueNorth( mapSettings.destinationCrs(), mapSettings.transformContext(), context.extent().center() );
}
catch ( QgsException & )
{
mRotationInt = 0.0;
//QgsDebugMsg( "Can not get direction to true north. Probably project CRS is not defined." );
}
mRotationInt += mapSettings.rotation();
}
size.setWidth( maxLength * viewBox.width() / viewBox.height() );
}
else
{
size.setHeight( maxLength * viewBox.height() / viewBox.width() );
}

double myRadiansDouble = mRotationInt * M_PI / 180.0;
int xShift = static_cast<int>( (
( centerXDouble * std::cos( myRadiansDouble ) ) +
( centerYDouble * std::sin( myRadiansDouble ) )
) - centerXDouble );
int yShift = static_cast<int>( (
( -centerXDouble * std::sin( myRadiansDouble ) ) +
( centerYDouble * std::cos( myRadiansDouble ) )
) - centerYDouble );

// need width/height of paint device
int myHeight = context.painter()->device()->height();
int myWidth = context.painter()->device()->width();

//QgsDebugMsg("Rendering north arrow at " + mPlacementLabels.at(mPlacementIndex));

// Set margin according to selected units
int myXOffset = 0;
int myYOffset = 0;
switch ( mMarginUnit )
double centerXDouble = size.width() / 2.0;
double centerYDouble = size.height() / 2.0;

//save the current canvas rotation
context.painter()->save();
//
//work out how to shift the image so that it rotates
// properly about its center
//(x cos a + y sin a - x, -x sin a + y cos a - y)
//
// could move this call to somewhere else so that it is only
// called when the projection or map extent changes
if ( mAutomatic )
{
try
{
case QgsUnitTypes::RenderMillimeters:
{
int myPixelsInchX = context.painter()->device()->logicalDpiX();
int myPixelsInchY = context.painter()->device()->logicalDpiY();
myXOffset = myPixelsInchX * INCHES_TO_MM * mMarginHorizontal;
myYOffset = myPixelsInchY * INCHES_TO_MM * mMarginVertical;
break;
}

case QgsUnitTypes::RenderPixels:
myXOffset = mMarginHorizontal - 5; // Minus 5 to shift tight into corner
myYOffset = mMarginVertical - 5;
break;

case QgsUnitTypes::RenderPercentage:
myXOffset = ( ( myWidth - size.width() ) / 100. ) * mMarginHorizontal;
myYOffset = ( ( myHeight - size.width() ) / 100. ) * mMarginVertical;
break;

default: // Use default of top left
break;
mRotationInt = QgsBearingUtils:: bearingTrueNorth( mapSettings.destinationCrs(), mapSettings.transformContext(), context.extent().center() );
}
//Determine placement of label from form combo box
switch ( mPlacement )
catch ( QgsException & )
{
case BottomLeft:
context.painter()->translate( myXOffset, myHeight - myYOffset - size.width() );
break;
case TopLeft:
context.painter()->translate( myXOffset, myYOffset );
break;
case TopRight:
context.painter()->translate( myWidth - myXOffset - size.width(), myYOffset );
break;
case BottomRight:
context.painter()->translate( myWidth - myXOffset - size.width(),
myHeight - myYOffset - size.width() );
break;
default:
{
//QgsDebugMsg("Unable to determine where to put north arrow so defaulting to top left");
}
mRotationInt = 0.0;
}
mRotationInt += mapSettings.rotation();
}

//rotate the canvas by the north arrow rotation amount
context.painter()->rotate( mRotationInt );
//Now we can actually do the drawing, and draw a smooth north arrow even when rotated

context.painter()->translate( xShift, yShift );
svg.render( context.painter(), QRectF( 0, 0, size.width(), size.height() ) );
double radiansDouble = mRotationInt * M_PI / 180.0;
int xShift = static_cast<int>( (
( centerXDouble * std::cos( radiansDouble ) ) +
( centerYDouble * std::sin( radiansDouble ) )
) - centerXDouble );
int yShift = static_cast<int>( (
( -centerXDouble * std::sin( radiansDouble ) ) +
( centerYDouble * std::cos( radiansDouble ) )
) - centerYDouble );
// need width/height of paint device
int deviceHeight = context.painter()->device()->height();
int deviceWidth = context.painter()->device()->width();

// Set margin according to selected units
int xOffset = 0;
int yOffset = 0;
switch ( mMarginUnit )
{
case QgsUnitTypes::RenderMillimeters:
{
int pixelsInchX = context.painter()->device()->logicalDpiX();
int pixelsInchY = context.painter()->device()->logicalDpiY();
xOffset = pixelsInchX * INCHES_TO_MM * mMarginHorizontal;
yOffset = pixelsInchY * INCHES_TO_MM * mMarginVertical;
break;
}

//unrotate the canvas again
context.painter()->restore();
case QgsUnitTypes::RenderPixels:
xOffset = mMarginHorizontal - 5; // Minus 5 to shift tight into corner
yOffset = mMarginVertical - 5;
break;

case QgsUnitTypes::RenderPercentage:
xOffset = ( ( deviceWidth - size.width() ) / 100. ) * mMarginHorizontal;
yOffset = ( ( deviceHeight - size.width() ) / 100. ) * mMarginVertical;
break;
case QgsUnitTypes::RenderMapUnits:
case QgsUnitTypes::RenderPoints:
case QgsUnitTypes::RenderInches:
case QgsUnitTypes::RenderUnknownUnit:
case QgsUnitTypes::RenderMetersInMapUnits:
break;
}
else
//Determine placement of label from form combo box
switch ( mPlacement )
{
QFont myQFont( QStringLiteral( "time" ), 12, QFont::Bold );
context.painter()->setFont( myQFont );
context.painter()->setPen( Qt::black );
context.painter()->drawText( 10, 20, tr( "North arrow pixmap not found" ) );
case BottomLeft:
context.painter()->translate( xOffset, deviceHeight - yOffset - maxLength + ( maxLength - size.height() ) / 2 );
break;
case TopLeft:
context.painter()->translate( xOffset, yOffset );
break;
case TopRight:
context.painter()->translate( deviceWidth - xOffset - maxLength + ( maxLength - size.width() ) / 2, yOffset );
break;
case BottomRight:
context.painter()->translate( deviceWidth - xOffset - maxLength + ( maxLength - size.width() ) / 2,
deviceHeight - yOffset - maxLength + ( maxLength - size.height() ) / 2 );
break;
}

//rotate the canvas by the north arrow rotation amount
context.painter()->rotate( mRotationInt );
//Now we can actually do the drawing, and draw a smooth north arrow even when rotated
context.painter()->translate( xShift, yShift );
svg.render( context.painter(), QRectF( 0, 0, size.width(), size.height() ) );

//unrotate the canvas again
context.painter()->restore();
}
}
13 changes: 10 additions & 3 deletions src/app/qgsdecorationnortharrow.h
Expand Up @@ -37,16 +37,19 @@ class APP_EXPORT QgsDecorationNorthArrow: public QgsDecorationItem
QgsDecorationNorthArrow( QObject *parent = nullptr );

public slots:
//! set values on the gui when a project is read or the gui first loaded
//! Set values on the gui when a project is read or the gui first loaded
void projectRead() override;
//! save values to the project
//! Save values to the project
void saveToProject() override;

//! Show the dialog box
void run() override;
//! draw some arbitrary text to the screen
//! Draw some arbitrary text to the screen
void render( const QgsMapSettings &mapSettings, QgsRenderContext &context ) override;

//! Return the north arrow SVG path
QString svgPath();

private:

// static const double DEG2RAD;
Expand All @@ -56,6 +59,10 @@ class APP_EXPORT QgsDecorationNorthArrow: public QgsDecorationItem
QColor mColor;
//! The north arrow outline color
QColor mOutlineColor;
//! The north arrow size in millimeter
double mSize = 16.0;
//! Custom north arrow svg path
QString mSvgPath;

// The amount of rotation for the north arrow
int mRotationInt = 0;
Expand Down

0 comments on commit 13d0556

Please sign in to comment.