Skip to content

Commit a2d5acb

Browse files
committedOct 1, 2014
Add argument to QgsCoordinateTransform::transformBoundingBox to flag that
handling of a bounding box which crosses the 180 degree longitude line is required. Fix composer map reprojected grids which cross 180 degree line.
1 parent d66e4dc commit a2d5acb

File tree

6 files changed

+153
-7
lines changed

6 files changed

+153
-7
lines changed
 

‎python/core/qgscoordinatetransform.sip

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,11 @@ class QgsCoordinateTransform : QObject
114114
* returned rectangle.
115115
* @param theRect rect to transform
116116
* @param direction TransformDirection (defaults to ForwardTransform)
117+
* @param handle180Crossover set to true if destination crs is geographic and handling of extents crossing the 180 degree
118+
* longitude line is required
117119
* @return QgsRectangle in Destination Coordinate System
118120
*/
119-
QgsRectangle transformBoundingBox( const QgsRectangle theRect, TransformDirection direction = ForwardTransform ) const throw (QgsCsException);
121+
QgsRectangle transformBoundingBox( const QgsRectangle theRect, TransformDirection direction = ForwardTransform, const bool handle180Crossover = false ) const throw (QgsCsException);
120122

121123
// Same as for the other transform() functions, but alters the x
122124
// and y variables in place. The second one works with good old-fashioned

‎src/core/composer/qgscomposermapgrid.cpp

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "qgsrendercontext.h"
2626
#include "qgssymbollayerv2utils.h"
2727
#include "qgssymbolv2.h"
28+
#include "qgscoordinatereferencesystem.h"
2829

2930
#include <QPainter>
3031
#include <QPen>
@@ -1356,6 +1357,15 @@ int QgsComposerMapGrid::xGridLinesCRSTransform( const QgsRectangle& bbox, const
13561357
double maxX = bbox.xMaximum();
13571358
double step = ( maxX - minX ) / 20;
13581359

1360+
bool crosses180 = false;
1361+
bool crossed180 = false;
1362+
if ( mCRS.geographicFlag() && ( minX > maxX ) )
1363+
{
1364+
//handle 180 degree longitude crossover
1365+
crosses180 = true;
1366+
step = ( maxX + 360.0 - minX ) / 20;
1367+
}
1368+
13591369
int gridLineCount = 0;
13601370
while ( currentLevel >= bbox.yMinimum() && gridLineCount < MAX_GRID_LINES )
13611371
{
@@ -1364,15 +1374,21 @@ int QgsComposerMapGrid::xGridLinesCRSTransform( const QgsRectangle& bbox, const
13641374
bool cont = true;
13651375
while ( cont )
13661376
{
1367-
if ( currentX > maxX )
1377+
if (( !crosses180 || crossed180 ) && ( currentX > maxX ) )
13681378
{
13691379
cont = false;
13701380
}
13711381

13721382
QgsPoint mapPoint = t.transform( currentX, currentLevel ); //transform back to map crs
13731383
gridLine.append( mComposerMap->mapToItemCoords( QPointF( mapPoint.x(), mapPoint.y() ) ) ); //transform back to composer coords
13741384
currentX += step;
1385+
if ( crosses180 && currentX > 180.0 )
1386+
{
1387+
currentX -= 360.0;
1388+
crossed180 = true;
1389+
}
13751390
}
1391+
crossed180 = false;
13761392

13771393
gridLine = trimLineToMap( gridLine, QgsRectangle( mComposerMap->rect() ) );
13781394
if ( gridLine.size() > 0 )
@@ -1401,8 +1417,16 @@ int QgsComposerMapGrid::yGridLinesCRSTransform( const QgsRectangle& bbox, const
14011417
double maxY = bbox.yMaximum();
14021418
double step = ( maxY - minY ) / 20;
14031419

1420+
bool crosses180 = false;
1421+
bool crossed180 = false;
1422+
if ( mCRS.geographicFlag() && ( bbox.xMinimum() > bbox.xMaximum() ) )
1423+
{
1424+
//handle 180 degree longitude crossover
1425+
crosses180 = true;
1426+
}
1427+
14041428
int gridLineCount = 0;
1405-
while ( currentLevel <= bbox.xMaximum() && gridLineCount < MAX_GRID_LINES )
1429+
while (( currentLevel <= bbox.xMaximum() || ( crosses180 && !crossed180 ) ) && gridLineCount < MAX_GRID_LINES )
14061430
{
14071431
QPolygonF gridLine;
14081432
double currentY = minY;
@@ -1427,6 +1451,11 @@ int QgsComposerMapGrid::yGridLinesCRSTransform( const QgsRectangle& bbox, const
14271451
gridLineCount++;
14281452
}
14291453
currentLevel += mGridIntervalX;
1454+
if ( crosses180 && currentLevel > 180.0 )
1455+
{
1456+
currentLevel -= 360.0;
1457+
crossed180 = true;
1458+
}
14301459
}
14311460

14321461
return 0;
@@ -1735,7 +1764,33 @@ int QgsComposerMapGrid::crsGridParams( QgsRectangle& crsRect, QgsCoordinateTrans
17351764
QPolygonF mapPolygon = mComposerMap->transformedMapPolygon();
17361765
QRectF mbr = mapPolygon.boundingRect();
17371766
QgsRectangle mapBoundingRect( mbr.left(), mbr.bottom(), mbr.right(), mbr.top() );
1738-
crsRect = tr.transformBoundingBox( mapBoundingRect );
1767+
1768+
1769+
if ( mCRS.geographicFlag() )
1770+
{
1771+
//handle crossing the 180 degree longitude line
1772+
QgsPoint lowerLeft( mapBoundingRect.xMinimum(), mapBoundingRect.yMinimum() );
1773+
QgsPoint upperRight( mapBoundingRect.xMaximum(), mapBoundingRect.yMaximum() );
1774+
1775+
lowerLeft = tr.transform( lowerLeft.x(), lowerLeft.y() );
1776+
upperRight = tr.transform( upperRight.x(), upperRight.y() );
1777+
1778+
if ( lowerLeft.x() > upperRight.x() )
1779+
{
1780+
//we've crossed the line
1781+
crsRect = tr.transformBoundingBox( mapBoundingRect, QgsCoordinateTransform::ForwardTransform, true );
1782+
}
1783+
else
1784+
{
1785+
//didn't cross the line
1786+
crsRect = tr.transformBoundingBox( mapBoundingRect );
1787+
}
1788+
}
1789+
else
1790+
{
1791+
crsRect = tr.transformBoundingBox( mapBoundingRect );
1792+
}
1793+
17391794
inverseTransform.setSourceCrs( mCRS );
17401795
inverseTransform.setDestCRS( mComposerMap->composition()->mapSettings().destinationCrs() );
17411796
return 0;

‎src/core/qgscoordinatetransform.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ void QgsCoordinateTransform::transformInPlace(
520520
#endif //ANDROID
521521

522522

523-
QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle rect, TransformDirection direction ) const
523+
QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle rect, TransformDirection direction, const bool handle180Crossover ) const
524524
{
525525
// Calculate the bounding box of a QgsRectangle in the source CRS
526526
// when projected to the destination CRS (or the inverse).
@@ -592,8 +592,29 @@ QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle re
592592

593593
for ( int i = 0; i < numP * numP; i++ )
594594
{
595-
if ( qIsFinite( x[i] ) && qIsFinite( y[i] ) )
595+
if ( !qIsFinite( x[i] ) || !qIsFinite( y[i] ) )
596+
{
597+
continue;
598+
}
599+
600+
if ( handle180Crossover )
601+
{
602+
//if crossing the date line, temporarily add 360 degrees to -ve longitudes
603+
bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] );
604+
}
605+
else
606+
{
596607
bb_rect.combineExtentWith( x[i], y[i] );
608+
}
609+
}
610+
611+
if ( handle180Crossover )
612+
{
613+
//subtract temporary addition of 360 degrees from longitudes
614+
if ( bb_rect.xMinimum() > 180.0 )
615+
bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
616+
if ( bb_rect.xMaximum() > 180.0 )
617+
bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
597618
}
598619

599620
QgsDebugMsg( "Projected extent: " + bb_rect.toString() );

‎src/core/qgscoordinatetransform.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,11 @@ class CORE_EXPORT QgsCoordinateTransform : public QObject
148148
* returned rectangle.
149149
* @param theRect rect to transform
150150
* @param direction TransformDirection (defaults to ForwardTransform)
151+
* @param handle180Crossover set to true if destination crs is geographic and handling of extents crossing the 180 degree
152+
* longitude line is required
151153
* @return QgsRectangle in Destination Coordinate System
152154
*/
153-
QgsRectangle transformBoundingBox( const QgsRectangle theRect, TransformDirection direction = ForwardTransform ) const;
155+
QgsRectangle transformBoundingBox( const QgsRectangle theRect, TransformDirection direction = ForwardTransform, const bool handle180Crossover = false ) const;
154156

155157
// Same as for the other transform() functions, but alters the x
156158
// and y variables in place. The second one works with good old-fashioned

‎tests/src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ ADD_QGIS_TEST(blendmodestest testqgsblendmodes.cpp)
9898
ADD_QGIS_TEST(geometrytest testqgsgeometry.cpp)
9999
ADD_QGIS_TEST(geometryimporttest testqgsgeometryimport.cpp)
100100
ADD_QGIS_TEST(coordinatereferencesystemtest testqgscoordinatereferencesystem.cpp)
101+
ADD_QGIS_TEST(coordinatetransformtest testqgscoordinatetransform.cpp)
101102
ADD_DEPENDENCIES(qgis_coordinatereferencesystemtest synccrsdb)
102103
ADD_QGIS_TEST(pointtest testqgspoint.cpp)
103104
ADD_QGIS_TEST(vectordataprovidertest testqgsvectordataprovider.cpp)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/***************************************************************************
2+
testqgscoordinatetransform.cpp
3+
-----------------------
4+
begin : October 2014
5+
copyright : (C) 2014 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
#include "qgscoordinatetransform.h"
18+
#include "qgsapplication.h"
19+
#include <QObject>
20+
#include <QtTest>
21+
22+
class TestQgsCoordinateTransform: public QObject
23+
{
24+
Q_OBJECT
25+
26+
private slots:
27+
void initTestCase();
28+
void transformBoundingBox();
29+
30+
private:
31+
32+
};
33+
34+
35+
void TestQgsCoordinateTransform::initTestCase()
36+
{
37+
QgsApplication::init();
38+
QgsApplication::initQgis();
39+
40+
}
41+
42+
void TestQgsCoordinateTransform::transformBoundingBox()
43+
{
44+
//test transforming a bounding box which crosses the 180 degree longitude line
45+
QgsCoordinateReferenceSystem sourceSrs;
46+
sourceSrs.createFromSrid( 3994 );
47+
QgsCoordinateReferenceSystem destSrs;
48+
destSrs.createFromSrid( 4326 );
49+
50+
QgsCoordinateTransform tr( sourceSrs, destSrs );
51+
QgsRectangle crossingRect( 6374985, -3626584, 7021195, -3272435 );
52+
QgsRectangle resultRect = tr.transformBoundingBox( crossingRect, QgsCoordinateTransform::ForwardTransform, true );
53+
QgsRectangle expectedRect;
54+
expectedRect.setXMinimum( 175.771 );
55+
expectedRect.setYMinimum( -39.7222 );
56+
expectedRect.setXMaximum( -176.549 );
57+
expectedRect.setYMaximum( -36.3951 );
58+
QVERIFY( qgsDoubleNear( resultRect.xMinimum(), expectedRect.xMinimum(), 0.001 ) );
59+
QVERIFY( qgsDoubleNear( resultRect.yMinimum(), expectedRect.yMinimum(), 0.001 ) );
60+
QVERIFY( qgsDoubleNear( resultRect.xMaximum(), expectedRect.xMaximum(), 0.001 ) );
61+
QVERIFY( qgsDoubleNear( resultRect.yMaximum(), expectedRect.yMaximum(), 0.001 ) );
62+
}
63+
64+
QTEST_MAIN( TestQgsCoordinateTransform )
65+
#include "moc_testqgscoordinatetransform.cxx"

0 commit comments

Comments
 (0)
Please sign in to comment.