Skip to content

Commit

Permalink
[composer] Use intersection rather than clip for transformed grids
Browse files Browse the repository at this point in the history
Previous behaviour was to clip transformed grid lines to the visible
map extent. This caused problems if a map item had no border (a
solid line would be shown along the map extent), and also meant
that reprojected lines which crossed out of the map extent and back
in again did not have annotations or grid frame markings shown.

Also implement an improved logic for detecting the map frame side
for annotations.
  • Loading branch information
nyalldawson committed Nov 6, 2014
1 parent abf0087 commit b242324
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 46 deletions.
99 changes: 54 additions & 45 deletions src/core/composer/qgscomposermapgrid.cpp
Expand Up @@ -1615,11 +1615,15 @@ int QgsComposerMapGrid::xGridLinesCRSTransform( const QgsRectangle& bbox, const
}
crossed180 = false;

gridLine = trimLineToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
if ( gridLine.size() > 0 )
QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
for ( ; lineIt != lineSegments.constEnd(); lineIt ++ )
{
lines.append( qMakePair( currentLevel, gridLine ) );
gridLineCount++;
if (( *lineIt ).size() > 0 )
{
lines.append( qMakePair( currentLevel, *lineIt ) );
gridLineCount++;
}
}
currentLevel -= mGridIntervalY;
}
Expand Down Expand Up @@ -1677,11 +1681,15 @@ int QgsComposerMapGrid::yGridLinesCRSTransform( const QgsRectangle& bbox, const
currentY += step;
}
//clip grid line to map polygon
gridLine = trimLineToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
if ( gridLine.size() > 0 )
QList<QPolygonF> lineSegments = trimLinesToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
QList<QPolygonF>::const_iterator lineIt = lineSegments.constBegin();
for ( ; lineIt != lineSegments.constEnd(); lineIt ++ )
{
lines.append( qMakePair( currentLevel, gridLine ) );
gridLineCount++;
if (( *lineIt ).size() > 0 )
{
lines.append( qMakePair( currentLevel, *lineIt ) );
gridLineCount++;
}
}
currentLevel += mGridIntervalX;
if ( crosses180 && currentLevel > 180.0 )
Expand Down Expand Up @@ -1775,26 +1783,31 @@ bool QgsComposerMapGrid::shouldShowDivisionForDisplayMode( const QgsComposerMapG
|| ( mode == QgsComposerMapGrid::LongitudeOnly && coordinate == QgsComposerMapGrid::Longitude );
}

bool sortByDistance( const QPair<double, QgsComposerMapGrid::BorderSide>& a, const QPair<double, QgsComposerMapGrid::BorderSide>& b )
{
return a.first < b.first;
}

QgsComposerMapGrid::BorderSide QgsComposerMapGrid::borderForLineCoord( const QPointF& p, const AnnotationCoordinate coordinateType ) const
{
if ( !mComposerMap )
{
return QgsComposerMapGrid::Left;
}

double framePenWidth = mComposerMap->hasFrame() ? mComposerMap->pen().widthF() : 0.000000001;
double tolerance = qMax( mComposerMap->hasFrame() ? mComposerMap->pen().widthF() : 0.0, 1.0 );

//check for corner coordinates
if (( p.y() <= framePenWidth && p.x() <= framePenWidth ) // top left
|| ( p.y() <= framePenWidth && p.x() >= ( mComposerMap->rect().width() - framePenWidth ) ) //top right
|| ( p.y() >= ( mComposerMap->rect().height() - framePenWidth ) && p.x() <= framePenWidth ) //bottom left
|| ( p.y() >= ( mComposerMap->rect().height() - framePenWidth ) && p.x() >= ( mComposerMap->rect().width() - framePenWidth ) ) //bottom right
if (( p.y() <= tolerance && p.x() <= tolerance ) // top left
|| ( p.y() <= tolerance && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //top right
|| ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() <= tolerance ) //bottom left
|| ( p.y() >= ( mComposerMap->rect().height() - tolerance ) && p.x() >= ( mComposerMap->rect().width() - tolerance ) ) //bottom right
)
{
//coordinate is in corner - fall back to preferred side for coordinate type
if ( coordinateType == QgsComposerMapGrid::Latitude )
{
if ( p.x() <= framePenWidth )
if ( p.x() <= tolerance )
{
return QgsComposerMapGrid::Left;
}
Expand All @@ -1805,7 +1818,7 @@ QgsComposerMapGrid::BorderSide QgsComposerMapGrid::borderForLineCoord( const QPo
}
else
{
if ( p.y() <= framePenWidth )
if ( p.y() <= tolerance )
{
return QgsComposerMapGrid::Top;
}
Expand All @@ -1816,23 +1829,15 @@ QgsComposerMapGrid::BorderSide QgsComposerMapGrid::borderForLineCoord( const QPo
}
}

//otherwise, guess side based on point
if ( p.y() <= framePenWidth )
{
return QgsComposerMapGrid::Top;
}
else if ( p.x() <= framePenWidth )
{
return QgsComposerMapGrid::Left;
}
else if ( p.x() >= ( mComposerMap->rect().width() - framePenWidth ) )
{
return QgsComposerMapGrid::Right;
}
else
{
return QgsComposerMapGrid::Bottom;
}
//otherwise, guess side based on closest map side to point
QList< QPair<double, QgsComposerMapGrid::BorderSide > > distanceToSide;
distanceToSide << qMakePair( p.x(), QgsComposerMapGrid::Left );
distanceToSide << qMakePair( mComposerMap->rect().width() - p.x(), QgsComposerMapGrid::Right );
distanceToSide << qMakePair( p.y(), QgsComposerMapGrid::Top );
distanceToSide << qMakePair( mComposerMap->rect().height() - p.y(), QgsComposerMapGrid::Bottom );

qSort( distanceToSide.begin(), distanceToSide.end(), sortByDistance );
return distanceToSide.at( 0 ).second;
}

void QgsComposerMapGrid::setLineSymbol( QgsLineSymbolV2* symbol )
Expand Down Expand Up @@ -2268,21 +2273,25 @@ int QgsComposerMapGrid::crsGridParams( QgsRectangle& crsRect, QgsCoordinateTrans
return 0;
}

QPolygonF QgsComposerMapGrid::trimLineToMap( const QPolygonF& line, const QgsRectangle& rect )
QList<QPolygonF> QgsComposerMapGrid::trimLinesToMap( const QPolygonF& line, const QgsRectangle& rect )
{
QgsPolyline polyLine;
QPolygonF::const_iterator lineIt = line.constBegin();
for ( ; lineIt != line.constEnd(); ++lineIt )
QgsGeometry* lineGeom = QgsGeometry::fromQPolygonF( line );
QgsGeometry* rectGeom = QgsGeometry::fromRect( rect );

QgsGeometry* intersected = lineGeom->intersection( rectGeom );
QList<QgsGeometry*> intersectedParts = intersected->asGeometryCollection();

QList<QPolygonF> trimmedLines;
QList<QgsGeometry*>::const_iterator geomIt = intersectedParts.constBegin();
for ( ; geomIt != intersectedParts.constEnd(); geomIt++ )
{
polyLine.append( QgsPoint( lineIt->x(), lineIt->y() ) );
trimmedLines << ( *geomIt )->asQPolygonF();
}

QgsGeometry* geom = QgsGeometry::fromPolyline( polyLine );

QPolygonF clippedLine;
QgsClipper::clippedLineWKB( geom->asWkb(), rect, clippedLine );
delete geom;
return clippedLine;
qDeleteAll( intersectedParts );
intersectedParts.clear();
delete intersected;
delete lineGeom;
delete rectGeom;
return trimmedLines;
}


2 changes: 1 addition & 1 deletion src/core/composer/qgscomposermapgrid.h
Expand Up @@ -931,7 +931,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem
/**Get parameters for drawing grid in CRS different to map CRS*/
int crsGridParams( QgsRectangle& crsRect, QgsCoordinateTransform& inverseTransform ) const;

static QPolygonF trimLineToMap( const QPolygonF& line, const QgsRectangle& rect );
static QList<QPolygonF> trimLinesToMap( const QPolygonF &line, const QgsRectangle &rect );

QPolygonF scalePolygon( const QPolygonF &polygon, const double scale ) const;

Expand Down
27 changes: 27 additions & 0 deletions tests/src/core/testqgscomposermapgrid.cpp
Expand Up @@ -35,6 +35,7 @@ class TestQgsComposerMapGrid: public QObject
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.
void grid(); //test if grid and grid annotation works
void reprojected(); //test if reprojected grid works
void crossGrid(); //test if grid "cross" mode works
void markerGrid(); //test if grid "marker" mode works
void frameOnly(); //test if grid "frame/annotation" mode works
Expand Down Expand Up @@ -128,6 +129,31 @@ void TestQgsComposerMapGrid::grid()
QVERIFY( testResult );
}

void TestQgsComposerMapGrid::reprojected()
{
mComposerMap->setNewExtent( QgsRectangle( -243577.565, 2939084.773, 1215622.435, 3668684.773 ) );
QgsCoordinateReferenceSystem geographic = QgsCoordinateReferenceSystem( 4326 );
mComposerMap->grid()->setCrs( geographic );
mComposerMap->grid()->setEnabled( true );
mComposerMap->grid()->setIntervalX( 1 );
mComposerMap->grid()->setIntervalY( 1 );
mComposerMap->grid()->setAnnotationEnabled( false );
mComposerMap->grid()->setGridLineColor( QColor( 0, 0, 0 ) );
mComposerMap->grid()->setGridLineWidth( 0.5 );
mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver );
mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::ExteriorTicks );
mComposerMap->grid()->setFrameWidth( 10 );
mComposerMap->setFrameEnabled( false );
QgsCompositionChecker checker( "composermap_gridreprojected", mComposition );

bool testResult = checker.testComposition( mReport, 0, 0 );
mComposerMap->grid()->setEnabled( false );
mComposerMap->grid()->setCrs( mMapSettings.destinationCrs() );
mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::NoFrame );
mComposerMap->setFrameEnabled( true );
QVERIFY( testResult );
}

void TestQgsComposerMapGrid::crossGrid()
{
mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) );
Expand Down Expand Up @@ -177,6 +203,7 @@ void TestQgsComposerMapGrid::frameOnly()
mComposerMap->grid()->setAnnotationEnabled( false );
//set a frame for testing
mComposerMap->grid()->setFrameStyle( QgsComposerMapGrid::Zebra );
mComposerMap->grid()->setFrameWidth( 2.0 );
mComposerMap->grid()->setFramePenSize( 0.5 );
mComposerMap->grid()->setBlendMode( QPainter::CompositionMode_SourceOver );
QgsCompositionChecker checker( "composermap_gridframeonly", mComposition );
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b242324

Please sign in to comment.