206 changes: 203 additions & 3 deletions src/core/qgscoordinatetransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* *
***************************************************************************/
#include "qgscoordinatetransform.h"
#include "qgsapplication.h"
#include "qgscrscache.h"
#include "qgsmessagelog.h"
#include "qgslogger.h"
Expand All @@ -24,12 +25,14 @@
#include <QDomElement>
#include <QApplication>
#include <QPolygonF>
#include <QStringList>
#include <QVector>

extern "C"
{
#include <proj_api.h>
}
#include <sqlite3.h>

// if defined shows all information about transform to stdout
// #define COORDINATE_TRANSFORM_VERBOSE
Expand All @@ -39,6 +42,8 @@ QgsCoordinateTransform::QgsCoordinateTransform()
, mInitialisedFlag( false )
, mSourceProjection( 0 )
, mDestinationProjection( 0 )
, mSourceDatumTransform( -1 )
, mDestinationDatumTransform( -1 )
{
setFinder();
}
Expand All @@ -48,6 +53,8 @@ QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSyst
, mInitialisedFlag( false )
, mSourceProjection( 0 )
, mDestinationProjection( 0 )
, mSourceDatumTransform( -1 )
, mDestinationDatumTransform( -1 )
{
setFinder();
mSourceCRS = source;
Expand All @@ -62,6 +69,8 @@ QgsCoordinateTransform::QgsCoordinateTransform( long theSourceSrsId, long theDes
, mDestCRS( theDestSrsId, QgsCoordinateReferenceSystem::InternalCrsId )
, mSourceProjection( 0 )
, mDestinationProjection( 0 )
, mSourceDatumTransform( -1 )
, mDestinationDatumTransform( -1 )
{
initialise();
}
Expand All @@ -71,6 +80,8 @@ QgsCoordinateTransform::QgsCoordinateTransform( QString theSourceCRS, QString th
, mInitialisedFlag( false )
, mSourceProjection( 0 )
, mDestinationProjection( 0 )
, mSourceDatumTransform( -1 )
, mDestinationDatumTransform( -1 )
{
setFinder();
mSourceCRS.createFromWkt( theSourceCRS );
Expand All @@ -89,6 +100,8 @@ QgsCoordinateTransform::QgsCoordinateTransform( long theSourceSrid,
, mInitialisedFlag( false )
, mSourceProjection( 0 )
, mDestinationProjection( 0 )
, mSourceDatumTransform( -1 )
, mDestinationDatumTransform( -1 )
{
setFinder();

Expand Down Expand Up @@ -154,11 +167,37 @@ void QgsCoordinateTransform::initialise()
mDestCRS = QgsCRSCache::instance()->crsByAuthId( mSourceCRS.authid() );
}

bool useDefaultDatumTransform = ( mSourceDatumTransform == - 1 && mDestinationDatumTransform == -1 );

// init the projections (destination and source)
pj_free( mDestinationProjection );
mDestinationProjection = pj_init_plus( mDestCRS.toProj4().toUtf8() );

pj_free( mSourceProjection );
mSourceProjection = pj_init_plus( mSourceCRS.toProj4().toUtf8() );
QString sourceProjString = mSourceCRS.toProj4();
if ( !useDefaultDatumTransform )
{
sourceProjString = stripDatumTransform( sourceProjString );
}
if ( mSourceDatumTransform != -1 )
{
sourceProjString += ( " " + datumTransformString( mSourceDatumTransform ) );
}
mSourceProjection = pj_init_plus( sourceProjString.toUtf8() );

pj_free( mDestinationProjection );
QString destProjString = mDestCRS.toProj4();
if ( !useDefaultDatumTransform )
{
destProjString = stripDatumTransform( destProjString );
}
if ( mDestinationDatumTransform != -1 )
{
destProjString += ( " " + datumTransformString( mDestinationDatumTransform ) );
}
else if ( sourceProjString.contains( "+nadgrids" ) ) //add null grid if source transformation is ntv2
{
destProjString += " +nadgrids=@null";
}
mDestinationProjection = pj_init_plus( destProjString.toUtf8() );

#ifdef COORDINATE_TRANSFORM_VERBOSE
QgsDebugMsg( "From proj : " + mSourceCRS.toProj4() );
Expand Down Expand Up @@ -666,6 +705,9 @@ bool QgsCoordinateTransform::readXML( QDomNode & theNode )
QDomNode myDestNode = theNode.namedItem( "destinationsrs" );
mDestCRS.readXML( myDestNode );

mSourceDatumTransform = theNode.toElement().attribute( "sourceDatumTransform", "-1" ).toInt();
mDestinationDatumTransform = theNode.toElement().attribute( "destinationDatumTransform", "-1" ).toInt();

initialise();

return true;
Expand All @@ -675,6 +717,8 @@ bool QgsCoordinateTransform::writeXML( QDomNode & theNode, QDomDocument & theDoc
{
QDomElement myNodeElement = theNode.toElement();
QDomElement myTransformElement = theDoc.createElement( "coordinatetransform" );
myTransformElement.setAttribute( "sourceDatumTransform", QString::number( mSourceDatumTransform ) );
myTransformElement.setAttribute( "destinationDatumTransform", QString::number( mDestinationDatumTransform ) );

QDomElement mySourceElement = theDoc.createElement( "sourcesrs" );
mSourceCRS.writeXML( mySourceElement, theDoc );
Expand Down Expand Up @@ -716,3 +760,159 @@ void QgsCoordinateTransform::setFinder()
pj_set_finder( finder );
#endif
}

QList< QList< int > > QgsCoordinateTransform::datumTransformations( const QgsCoordinateReferenceSystem& srcCRS, const QgsCoordinateReferenceSystem& destCRS )
{
QList< QList< int > > transformations;

QString srcGeoId = srcCRS.geographicCRSAuthId();
QString destGeoId = destCRS.geographicCRSAuthId();

if ( srcGeoId.isEmpty() || destGeoId.isEmpty() )
{
return transformations;
}

QStringList srcSplit = srcGeoId.split( ":" );
QStringList destSplit = destGeoId.split( ":" );

if ( srcSplit.size() < 2 || destSplit.size() < 2 )
{
return transformations;
}

int srcAuthCode = srcSplit.at( 1 ).toInt();
int destAuthCode = destSplit.at( 1 ).toInt();

if ( srcAuthCode == destAuthCode )
{
return transformations; //crs have the same datum
}

QList<int> directTransforms;
searchDatumTransform( QString( "SELECT coord_op_code FROM tbl_datum_transform WHERE source_crs_code = %1 AND target_crs_code = %2" ).arg( srcAuthCode ).arg( destAuthCode ),
directTransforms );
QList<int> srcToWgs84;
searchDatumTransform( QString( "SELECT coord_op_code FROM tbl_datum_transform WHERE ( source_crs_code = %1 AND target_crs_code = %2 ) OR ( source_crs_code = %2 AND target_crs_code = %1 )" ).arg( srcAuthCode ).arg( 4326 ),
srcToWgs84 );
QList<int> destToWgs84;
searchDatumTransform( QString( "SELECT coord_op_code FROM tbl_datum_transform WHERE ( source_crs_code = %1 AND target_crs_code = %2 ) OR ( source_crs_code = %2 AND target_crs_code = %1 )" ).arg( destAuthCode ).arg( 4326 ),
destToWgs84 );

//add direct datum transformations
QList<int>::const_iterator directIt = directTransforms.constBegin();
for ( ; directIt != directTransforms.constEnd(); ++directIt )
{
transformations.push_back( QList<int>() << *directIt << -1 );
}


QList<int>::const_iterator srcWgsIt = srcToWgs84.constBegin();
for ( ; srcWgsIt != srcToWgs84.constEnd(); ++srcWgsIt )
{
QList<int>::const_iterator dstWgsIt = destToWgs84.constBegin();
for ( ; dstWgsIt != destToWgs84.constEnd(); ++dstWgsIt )
{
transformations.push_back( QList<int>() << *srcWgsIt << *dstWgsIt );
}
}

return transformations;
}

QString QgsCoordinateTransform::stripDatumTransform( const QString& proj4 )
{
QStringList parameterSplit = proj4.split( "+", QString::SkipEmptyParts );
QString currentParameter;
QString newProjString;

for ( int i = 0; i < parameterSplit.size(); ++i )
{
currentParameter = parameterSplit.at( i );
if ( !currentParameter.startsWith( "towgs84", Qt::CaseInsensitive )
&& !currentParameter.startsWith( "nadgrids", Qt::CaseInsensitive ) )
{
newProjString.append( "+" );
newProjString.append( currentParameter );
newProjString.append( " " );
}
}
return newProjString;
}

void QgsCoordinateTransform::searchDatumTransform( const QString& sql, QList< int >& transforms )
{
sqlite3* db;
int openResult = sqlite3_open( QgsApplication::srsDbFilePath().toUtf8().constData(), &db );
if ( openResult != SQLITE_OK )
{
return;
}

sqlite3_stmt* stmt;
int prepareRes = sqlite3_prepare( db, sql.toAscii(), sql.size(), &stmt, NULL );
if ( prepareRes != SQLITE_OK )
{
return;
}

QString cOpCode;
while ( sqlite3_step( stmt ) == SQLITE_ROW )
{
cOpCode = ( const char * ) sqlite3_column_text( stmt, 0 );
transforms.push_back( cOpCode.toInt() );
}
sqlite3_finalize( stmt );
sqlite3_close( db );
}

QString QgsCoordinateTransform::datumTransformString( int datumTransform )
{
QString transformString;

sqlite3* db;
int openResult = sqlite3_open( QgsApplication::srsDbFilePath().toUtf8().constData(), &db );
if ( openResult != SQLITE_OK )
{
return transformString;
}

sqlite3_stmt* stmt;
QString sql = QString( "SELECT coord_op_method_code, p1, p2, p3, p4, p5, p6, p7 FROM tbl_datum_transform WHERE coord_op_code = %1" ).arg( datumTransform );
int prepareRes = sqlite3_prepare( db, sql.toAscii(), sql.size(), &stmt, NULL );
if ( prepareRes != SQLITE_OK )
{
return transformString;
}

if ( sqlite3_step( stmt ) == SQLITE_ROW )
{
//coord_op_methode_code
int methodCode = sqlite3_column_int( stmt, 0 );
if ( methodCode == 9615 ) //ntv2
{
transformString = "+nadgrids=" + QString(( const char * )sqlite3_column_text( stmt, 1 ) );
}
else if ( methodCode == 9603 || methodCode == 9606 || methodCode == 9607 )
{
transformString += "+towgs84=";
double p1 = sqlite3_column_double( stmt, 1 );
double p2 = sqlite3_column_double( stmt, 2 );
double p3 = sqlite3_column_double( stmt, 3 );
double p4 = sqlite3_column_double( stmt, 4 );
double p5 = sqlite3_column_double( stmt, 5 );
double p6 = sqlite3_column_double( stmt, 6 );
double p7 = sqlite3_column_double( stmt, 7 );
if ( methodCode == 9603 ) //3 parameter transformation
{
transformString += QString( "%1,%2,%3" ).arg( p1 ).arg( p2 ).arg( p3 );
}
else //7 parameter transformation
{
transformString += QString( "%1,%2,%3,%4,%5,%6,%7" ).arg( p1 ).arg( p2 ).arg( p3 ).arg( p4 ).arg( p5 ).arg( p6 ).arg( p7 );
}
}
}

return transformString;
}
16 changes: 16 additions & 0 deletions src/core/qgscoordinatetransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ class CORE_EXPORT QgsCoordinateTransform : public QObject
* @param theCRSID - A long representing the srsid of the srs to be used */
void setDestCRSID( long theCRSID );

/**Returns list of datum transformations for the given src and dest CRS*/
static QList< QList< int > > datumTransformations( const QgsCoordinateReferenceSystem& srcCRS, const QgsCoordinateReferenceSystem& destCRS );
static QString datumTransformString( int datumTransform );

int sourceDatumTransform() const { return mSourceDatumTransform; }
void setSourceDatumTransform( int dt ) { mSourceDatumTransform = dt; }
int destinationDatumTransform() const { return mDestinationDatumTransform; }
void setDestinationDatumTransform( int dt ) { mDestinationDatumTransform = dt; }

public slots:
//!initialise is used to actually create the Transformer instance
void initialise();
Expand Down Expand Up @@ -265,10 +274,17 @@ class CORE_EXPORT QgsCoordinateTransform : public QObject
*/
projPJ mDestinationProjection;

int mSourceDatumTransform;
int mDestinationDatumTransform;

/*!
* Finder for PROJ grid files.
*/
void setFinder();

/**Removes +nadgrids and +towgs84 from proj4 string*/
static QString stripDatumTransform( const QString& proj4 );
static void searchDatumTransform( const QString& sql, QList< int >& transforms );
};

//! Output stream operator
Expand Down
33 changes: 20 additions & 13 deletions src/core/qgscrscache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,29 @@ QgsCoordinateTransformCache::~QgsCoordinateTransformCache()
}
}

const QgsCoordinateTransform* QgsCoordinateTransformCache::transform( const QString& srcAuthId, const QString& destAuthId )
const QgsCoordinateTransform* QgsCoordinateTransformCache::transform( const QString& srcAuthId, const QString& destAuthId, int srcDatumTransform, int destDatumTransform )
{
QHash< QPair< QString, QString >, QgsCoordinateTransform* >::const_iterator ctIt =
mTransforms.find( qMakePair( srcAuthId, destAuthId ) );
if ( ctIt == mTransforms.constEnd() )
{
const QgsCoordinateReferenceSystem& srcCrs = QgsCRSCache::instance()->crsByAuthId( srcAuthId );
const QgsCoordinateReferenceSystem& destCrs = QgsCRSCache::instance()->crsByAuthId( destAuthId );
QgsCoordinateTransform* ct = new QgsCoordinateTransform( srcCrs, destCrs );
mTransforms.insert( qMakePair( srcAuthId, destAuthId ), ct );
return ct;
}
else
QList< QgsCoordinateTransform* > values =
mTransforms.values( qMakePair( srcAuthId, destAuthId ) );

QList< QgsCoordinateTransform* >::const_iterator valIt = values.constBegin();
for ( ; valIt != values.constEnd(); ++valIt )
{
return ctIt.value();
if ( *valIt && ( *valIt )->sourceDatumTransform() == srcDatumTransform && ( *valIt )->destinationDatumTransform() == destDatumTransform )
{
return *valIt;
}
}

//not found, insert new value
const QgsCoordinateReferenceSystem& srcCrs = QgsCRSCache::instance()->crsByAuthId( srcAuthId );
const QgsCoordinateReferenceSystem& destCrs = QgsCRSCache::instance()->crsByAuthId( destAuthId );
QgsCoordinateTransform* ct = new QgsCoordinateTransform( srcCrs, destCrs );
ct->setSourceDatumTransform( srcDatumTransform );
ct->setDestinationDatumTransform( destDatumTransform );
ct->initialise();
mTransforms.insertMulti( qMakePair( srcAuthId, destAuthId ), ct );
return ct;
}

void QgsCoordinateTransformCache::invalidateCrs( const QString& crsAuthId )
Expand Down
5 changes: 3 additions & 2 deletions src/core/qgscrscache.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ class CORE_EXPORT QgsCoordinateTransformCache
/**Returns coordinate transformation. Cache keeps ownership
@param srcAuthId auth id string of source crs
@param destAuthId auth id string of dest crs*/
const QgsCoordinateTransform* transform( const QString& srcAuthId, const QString& destAuthId );
const QgsCoordinateTransform* transform( const QString& srcAuthId, const QString& destAuthId, int srcDatumTransform = -1, int destDatumTransform = -1 );
/**Removes transformations where a changed crs is involved from the cache*/
void invalidateCrs( const QString& crsAuthId );

private:
QHash< QPair< QString, QString >, QgsCoordinateTransform* > mTransforms;
static QgsCoordinateTransformCache* mInstance;
QMultiHash< QPair< QString, QString >, QgsCoordinateTransform* > mTransforms; //same auth_id pairs might have different datum transformations
};

class CORE_EXPORT QgsCRSCache
Expand Down
145 changes: 122 additions & 23 deletions src/core/qgsmaprenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ void QgsMapRenderer::render( QPainter* painter, double* forceWidthScale )
{
r1 = mExtent;
split = splitLayersExtent( ml, r1, r2 );
ct = QgsCoordinateTransformCache::instance()->transform( ml->crs().authid(), mDestCRS->authid() );
ct = transformation( ml );
mRenderContext.setExtent( r1 );
QgsDebugMsg( " extent 1: " + r1.toString() );
QgsDebugMsg( " extent 2: " + r2.toString() );
Expand Down Expand Up @@ -655,7 +655,7 @@ void QgsMapRenderer::render( QPainter* painter, double* forceWidthScale )
{
QgsRectangle r1 = mExtent;
split = splitLayersExtent( ml, r1, r2 );
ct = new QgsCoordinateTransform( ml->crs(), *mDestCRS );
ct = transformation( ml );
mRenderContext.setExtent( r1 );
}
else
Expand Down Expand Up @@ -736,12 +736,16 @@ bool QgsMapRenderer::hasCrsTransformEnabled() const
return mProjectionsEnabled;
}

void QgsMapRenderer::setDestinationCrs( const QgsCoordinateReferenceSystem& crs )
void QgsMapRenderer::setDestinationCrs( const QgsCoordinateReferenceSystem& crs, bool refreshCoordinateTransformInfo )
{
QgsDebugMsg( "* Setting destCRS : = " + crs.toProj4() );
QgsDebugMsg( "* DestCRS.srsid() = " + QString::number( crs.srsid() ) );
if ( *mDestCRS != crs )
{
if ( refreshCoordinateTransformInfo )
{
mLayerCoordinateTransformInfo.clear();
}
QgsRectangle rect;
if ( !mExtent.isEmpty() )
{
Expand Down Expand Up @@ -790,17 +794,23 @@ bool QgsMapRenderer::splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent
// extent separately.
static const double splitCoord = 180.0;

const QgsCoordinateTransform* transform = transformation( layer );
if ( layer->crs().geographicFlag() )
{
// Note: ll = lower left point
// and ur = upper right point
QgsPoint ll = tr( layer )->transform( extent.xMinimum(), extent.yMinimum(),
QgsCoordinateTransform::ReverseTransform );

QgsPoint ur = tr( layer )->transform( extent.xMaximum(), extent.yMaximum(),
QgsCoordinateTransform::ReverseTransform );
QgsPoint ll( extent.xMinimum(), extent.yMinimum() );
QgsPoint ur( extent.xMaximum(), extent.yMaximum() );

extent = tr( layer )->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
if ( transform )
{
ll = transform->transform( ll.x(), ll.y(),
QgsCoordinateTransform::ReverseTransform );
ur = transform->transform( ur.x(), ur.y(),
QgsCoordinateTransform::ReverseTransform );
extent = transform->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
}

if ( ll.x() > ur.x() )
{
Expand All @@ -812,7 +822,10 @@ bool QgsMapRenderer::splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent
}
else // can't cross 180
{
extent = tr( layer )->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
if ( transform )
{
extent = transform->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
}
}
}
catch ( QgsCsException &cse )
Expand All @@ -828,14 +841,18 @@ bool QgsMapRenderer::splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent

QgsRectangle QgsMapRenderer::layerExtentToOutputExtent( QgsMapLayer* theLayer, QgsRectangle extent )
{
QgsDebugMsg( QString( "sourceCrs = " + tr( theLayer )->sourceCrs().authid() ) );
QgsDebugMsg( QString( "destCRS = " + tr( theLayer )->destCRS().authid() ) );
QgsDebugMsg( QString( "extent = " + extent.toString() ) );
//QgsDebugMsg( QString( "sourceCrs = " + tr( theLayer )->sourceCrs().authid() ) );
//QgsDebugMsg( QString( "destCRS = " + tr( theLayer )->destCRS().authid() ) );
//QgsDebugMsg( QString( "extent = " + extent.toString() ) );
if ( hasCrsTransformEnabled() )
{
try
{
extent = tr( theLayer )->transformBoundingBox( extent );
const QgsCoordinateTransform* transform = transformation( theLayer );
if ( transform )
{
extent = transform->transformBoundingBox( extent );
}
}
catch ( QgsCsException &cse )
{
Expand All @@ -850,14 +867,14 @@ QgsRectangle QgsMapRenderer::layerExtentToOutputExtent( QgsMapLayer* theLayer, Q

QgsRectangle QgsMapRenderer::outputExtentToLayerExtent( QgsMapLayer* theLayer, QgsRectangle extent )
{
QgsDebugMsg( QString( "layer sourceCrs = " + tr( theLayer )->sourceCrs().authid() ) );
QgsDebugMsg( QString( "layer destCRS = " + tr( theLayer )->destCRS().authid() ) );
QgsDebugMsg( QString( "layer sourceCrs = " + transformation( theLayer )->sourceCrs().authid() ) );
QgsDebugMsg( QString( "layer destCRS = " + transformation( theLayer )->destCRS().authid() ) );
QgsDebugMsg( QString( "extent = " + extent.toString() ) );
if ( hasCrsTransformEnabled() )
{
try
{
extent = tr( theLayer )->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
extent = transformation( theLayer )->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
}
catch ( QgsCsException &cse )
{
Expand All @@ -876,7 +893,7 @@ QgsPoint QgsMapRenderer::layerToMapCoordinates( QgsMapLayer* theLayer, QgsPoint
{
try
{
point = tr( theLayer )->transform( point, QgsCoordinateTransform::ForwardTransform );
point = transformation( theLayer )->transform( point, QgsCoordinateTransform::ForwardTransform );
}
catch ( QgsCsException &cse )
{
Expand All @@ -896,7 +913,7 @@ QgsRectangle QgsMapRenderer::layerToMapCoordinates( QgsMapLayer* theLayer, QgsRe
{
try
{
rect = tr( theLayer )->transform( rect, QgsCoordinateTransform::ForwardTransform );
rect = transformation( theLayer )->transform( rect, QgsCoordinateTransform::ForwardTransform );
}
catch ( QgsCsException &cse )
{
Expand All @@ -916,7 +933,7 @@ QgsPoint QgsMapRenderer::mapToLayerCoordinates( QgsMapLayer* theLayer, QgsPoint
{
try
{
point = tr( theLayer )->transform( point, QgsCoordinateTransform::ReverseTransform );
point = transformation( theLayer )->transform( point, QgsCoordinateTransform::ReverseTransform );
}
catch ( QgsCsException &cse )
{
Expand All @@ -936,7 +953,7 @@ QgsRectangle QgsMapRenderer::mapToLayerCoordinates( QgsMapLayer* theLayer, QgsRe
{
try
{
rect = tr( theLayer )->transform( rect, QgsCoordinateTransform::ReverseTransform );
rect = transformation( theLayer )->transform( rect, QgsCoordinateTransform::ReverseTransform );
}
catch ( QgsCsException &cse )
{
Expand Down Expand Up @@ -1073,11 +1090,36 @@ bool QgsMapRenderer::readXML( QDomNode & theNode )
element = projNode.toElement();
setProjectionsEnabled( element.text().toInt() );

//load coordinate transform into
mLayerCoordinateTransformInfo.clear();
QDomElement layerCoordTransformInfoElem = theNode.firstChildElement( "layer_coordinate_transform_info" );
if ( !layerCoordTransformInfoElem.isNull() )
{
QDomNodeList layerCoordinateTransformList = layerCoordTransformInfoElem.elementsByTagName( "layer_coordinate_transform" );
QDomElement layerCoordTransformElem;
for ( int i = 0; i < layerCoordinateTransformList.size(); ++i )
{
layerCoordTransformElem = layerCoordinateTransformList.at( i ).toElement();
QString layerId = layerCoordTransformElem.attribute( "layerid" );
if ( layerId.isEmpty() )
{
continue;
}

QgsLayerCoordinateTransform lct;
lct.srcAuthId = layerCoordTransformElem.attribute( "srcAuthId" );
lct.destAuthId = layerCoordTransformElem.attribute( "destAuthId" );
lct.srcDatumTransform = layerCoordTransformElem.attribute( "srcDatumTransform", "-1" ).toInt();
lct.destDatumTransform = layerCoordTransformElem.attribute( "destDatumTransform", "-1" ).toInt();
mLayerCoordinateTransformInfo.insert( layerId, lct );
}
}

// set destination CRS
QgsCoordinateReferenceSystem srs;
QDomNode srsNode = theNode.namedItem( "destinationsrs" );
srs.readXML( srsNode );
setDestinationCrs( srs );
setDestinationCrs( srs, false );

// set extent
QgsRectangle aoi;
Expand Down Expand Up @@ -1105,6 +1147,7 @@ bool QgsMapRenderer::readXML( QDomNode & theNode )
aoi.setYMaximum( ymax );

setExtent( aoi );

return true;
}

Expand Down Expand Up @@ -1177,6 +1220,20 @@ bool QgsMapRenderer::writeXML( QDomNode & theNode, QDomDocument & theDoc )
theNode.appendChild( srsNode );
destinationCrs().writeXML( srsNode, theDoc );

// layer coordinate transform infos
QDomElement layerCoordTransformInfo = theDoc.createElement( "layer_coordinate_transform_info" );
QHash< QString, QgsLayerCoordinateTransform >::const_iterator coordIt = mLayerCoordinateTransformInfo.constBegin();
for ( ; coordIt != mLayerCoordinateTransformInfo.constEnd(); ++coordIt )
{
QDomElement layerCoordTransformElem = theDoc.createElement( "layer_coordinate_transform" );
layerCoordTransformElem.setAttribute( "layerid", coordIt.key() );
layerCoordTransformElem.setAttribute( "srcAuthId", coordIt->srcAuthId );
layerCoordTransformElem.setAttribute( "destAuthId", coordIt->destAuthId );
layerCoordTransformElem.setAttribute( "srcDatumTransform", QString::number( coordIt->srcDatumTransform ) );
layerCoordTransformElem.setAttribute( "destDatumTransform", QString::number( coordIt->destDatumTransform ) );
layerCoordTransformInfo.appendChild( layerCoordTransformElem );
}
theNode.appendChild( layerCoordTransformInfo );
return true;
}

Expand All @@ -1188,13 +1245,40 @@ void QgsMapRenderer::setLabelingEngine( QgsLabelingEngineInterface* iface )
mLabelingEngine = iface;
}

const QgsCoordinateTransform* QgsMapRenderer::tr( QgsMapLayer *layer )
const QgsCoordinateTransform* QgsMapRenderer::transformation( const QgsMapLayer *layer ) const
{
if ( !layer || !mDestCRS )
{
return 0;
}
return QgsCoordinateTransformCache::instance()->transform( layer->crs().authid(), mDestCRS->authid() );

if ( layer->crs().authid() == mDestCRS->authid() )
{
return 0;
}

QHash< QString, QgsLayerCoordinateTransform >::const_iterator ctIt = mLayerCoordinateTransformInfo.find( layer->id() );
if ( ctIt != mLayerCoordinateTransformInfo.constEnd()
&& ctIt->srcAuthId == layer->crs().authid()
&& ctIt->destAuthId == mDestCRS->authid() )
{
return QgsCoordinateTransformCache::instance()->transform( ctIt->srcAuthId, ctIt->destAuthId, ctIt->srcDatumTransform, ctIt->destDatumTransform );
}
else
{
emit datumTransformInfoRequested( layer, layer->crs().authid(), mDestCRS->authid() );
}

//still not present? get coordinate transformation with -1/-1 datum transform as default
ctIt = mLayerCoordinateTransformInfo.find( layer->id() );
if ( ctIt == mLayerCoordinateTransformInfo.constEnd()
|| ctIt->srcAuthId == layer->crs().authid()
|| ctIt->destAuthId == mDestCRS->authid()
)
{
return QgsCoordinateTransformCache::instance()->transform( layer->crs().authid(), mDestCRS->authid(), -1, -1 );
}
return QgsCoordinateTransformCache::instance()->transform( ctIt->srcAuthId, ctIt->destAuthId, ctIt->srcDatumTransform, ctIt->destDatumTransform );
}

/** Returns a QPainter::CompositionMode corresponding to a QgsMapRenderer::BlendMode
Expand Down Expand Up @@ -1271,4 +1355,19 @@ QgsMapRenderer::BlendMode QgsMapRenderer::getBlendModeEnum( const QPainter::Comp
}
}

void QgsMapRenderer::addLayerCoordinateTransform( const QString& layerId, const QString& srcAuthId, const QString& destAuthId, int srcDatumTransform, int destDatumTransform )
{
QgsLayerCoordinateTransform lt;
lt.srcAuthId = srcAuthId;
lt.destAuthId = destAuthId;
lt.srcDatumTransform = srcDatumTransform;
lt.destDatumTransform = destDatumTransform;
mLayerCoordinateTransformInfo.insert( layerId, lt );
}

void QgsMapRenderer::clearLayerCoordinateTransforms()
{
mLayerCoordinateTransformInfo.clear();
}

bool QgsMapRenderer::mDrawing = false;
22 changes: 18 additions & 4 deletions src/core/qgsmaprenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,13 @@ class CORE_EXPORT QgsLabelingEngineInterface
virtual QgsLabelingEngineInterface* clone() = 0;
};


struct CORE_EXPORT QgsLayerCoordinateTransform
{
QString srcAuthId;
QString destAuthId;
int srcDatumTransform; //-1 if unknown or not specified
int destDatumTransform;
};

/** \ingroup core
* A non GUI class for rendering a map layer set onto a QPainter.
Expand Down Expand Up @@ -243,7 +249,7 @@ class CORE_EXPORT QgsMapRenderer : public QObject
bool hasCrsTransformEnabled() const;

//! sets destination coordinate reference system
void setDestinationCrs( const QgsCoordinateReferenceSystem& crs );
void setDestinationCrs( const QgsCoordinateReferenceSystem& crs, bool refreshCoordinateTransformInfo = true );

//! returns CRS of destination coordinate reference system
const QgsCoordinateReferenceSystem& destinationCrs() const;
Expand Down Expand Up @@ -289,6 +295,11 @@ class CORE_EXPORT QgsMapRenderer : public QObject
//! Added in 1.9
static QgsMapRenderer::BlendMode getBlendModeEnum( const QPainter::CompositionMode blendMode );

void addLayerCoordinateTransform( const QString& layerId, const QString& srcAuthId, const QString& destAuthId, int srcDatumTransform = -1, int destDatumTransform = -1 );
void clearLayerCoordinateTransforms();

const QgsCoordinateTransform* transformation( const QgsMapLayer *layer ) const;

signals:

void drawingProgress( int current, int total );
Expand All @@ -304,6 +315,9 @@ class CORE_EXPORT QgsMapRenderer : public QObject
//! emitted when layer's draw() returned false
void drawError( QgsMapLayer* );

//! Notifies higher level components to show the datum transform dialog and add a QgsLayerCoordinateTransformInfo for that layer
void datumTransformInfoRequested( const QgsMapLayer* ml, const QString& srcAuthId, const QString& destAuthId ) const;

public slots:

//! called by signal from layer current being drawn
Expand Down Expand Up @@ -376,8 +390,8 @@ class CORE_EXPORT QgsMapRenderer : public QObject
//! Locks rendering loop for concurrent draws
QMutex mRenderMutex;

private:
const QgsCoordinateTransform* tr( QgsMapLayer *layer );
QHash< QString, QgsLayerCoordinateTransform > mLayerCoordinateTransformInfo;

};

#endif
Expand Down
53 changes: 30 additions & 23 deletions src/core/qgsvectorfilewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -744,26 +744,50 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
SymbologyExport symbologyExport,
double symbologyScale )
{
QgsDebugMsg( "fileName = " + fileName );
const QgsCoordinateReferenceSystem* outputCRS;
QgsCoordinateTransform* ct = 0;
int shallTransform = false;
if ( destCRS && layer )
{
ct = new QgsCoordinateTransform( layer->crs(), *destCRS );
}

QgsVectorFileWriter::WriterError error = writeAsVectorFormat( layer, fileName, fileEncoding, ct, driverName, onlySelected,
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation, newFilename, symbologyExport, symbologyScale );
delete ct;
return error;
}

QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
const QString& fileEncoding,
const QgsCoordinateTransform* ct,
const QString& driverName,
bool onlySelected,
QString *errorMessage,
const QStringList &datasourceOptions, // added in 1.6
const QStringList &layerOptions, // added in 1.6
bool skipAttributeCreation, // added in 1.6
QString *newFilename, // added in 1.9
SymbologyExport symbologyExport, //added in 2.0
double symbologyScale // added in 2.0
)
{
if ( !layer )
{
return ErrInvalidLayer;
}

if ( destCRS && destCRS->isValid() )
bool shallTransform = false;
const QgsCoordinateReferenceSystem* outputCRS = 0;
if ( ct )
{
// This means we should transform
outputCRS = destCRS;
outputCRS = &( ct->destCRS() );
shallTransform = true;
}
else
{
// This means we shouldn't transform, use source CRS as output (if defined)
outputCRS = &layer->crs();
outputCRS = &( layer->crs() );
}
QgsVectorFileWriter* writer =
new QgsVectorFileWriter( fileName, fileEncoding, skipAttributeCreation ? QgsFields() : layer->pendingFields(), layer->wkbType(), outputCRS, driverName, datasourceOptions, layerOptions, newFilename, symbologyExport );
Expand Down Expand Up @@ -805,18 +829,6 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,

const QgsFeatureIds& ids = layer->selectedFeaturesIds();

// Create our transform
if ( destCRS )
{
ct = new QgsCoordinateTransform( layer->crs(), *destCRS );
}

// Check for failure
if ( ct == NULL )
{
shallTransform = false;
}

//create symbol table if needed
if ( writer->symbologyExport() != NoSymbology )
{
Expand Down Expand Up @@ -928,11 +940,6 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
writer->stopRender( layer );
delete writer;

if ( shallTransform )
{
delete ct;
}

if ( errors > 0 && errorMessage && n > 0 )
{
*errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( n - errors ).arg( n );
Expand Down
15 changes: 15 additions & 0 deletions src/core/qgsvectorfilewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ class CORE_EXPORT QgsVectorFileWriter
double symbologyScale = 1.0 // added in 2.0
);

static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
const QString& fileEncoding,
const QgsCoordinateTransform* ct,
const QString& driverName = "ESRI Shapefile",
bool onlySelected = false,
QString *errorMessage = 0,
const QStringList &datasourceOptions = QStringList(), // added in 1.6
const QStringList &layerOptions = QStringList(), // added in 1.6
bool skipAttributeCreation = false, // added in 1.6
QString *newFilename = 0, // added in 1.9
SymbologyExport symbologyExport = NoSymbology, //added in 2.0
double symbologyScale = 1.0 // added in 2.0
);

/** create shapefile and initialize it */
QgsVectorFileWriter( const QString& vectorFileName,
const QString& fileEncoding,
Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ qgscredentialdialog.cpp
qgscursors.cpp
qgsdatadefinedbutton.cpp
qgsdetaileditemdata.cpp
qgsdatumtransformdialog.cpp
qgsdetaileditemdelegate.cpp
qgsdetaileditemwidget.cpp
qgsdialog.cpp
Expand Down Expand Up @@ -216,6 +217,7 @@ qgscomposerruler.h
qgscomposerview.h
qgscredentialdialog.h
qgsdatadefinedbutton.h
qgsdatumtransformdialog.h
qgsdetaileditemdelegate.h
qgsdetaileditemwidget.h
qgsdialog.h
Expand Down
55 changes: 55 additions & 0 deletions src/gui/qgsdatumtransformdialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "qgsdatumtransformdialog.h"
#include "qgscoordinatetransform.h"

QgsDatumTransformDialog::QgsDatumTransformDialog( const QString& layerName, const QList< QList< int > >& dt, QWidget* parent, Qt::WindowFlags f ): QDialog( parent, f )
{
setupUi( this );
setWindowTitle( tr( "Select datum transformations for layer" ) + " " + layerName );
QList< QList< int > >::const_iterator it = dt.constBegin();
for ( ; it != dt.constEnd(); ++it )
{
QTreeWidgetItem* item = new QTreeWidgetItem();
for ( int i = 0; i < 2; ++i )
{
if ( i >= it->size() )
{
break;
}

int nr = it->at( i );
item->setData( i, Qt::UserRole, nr );
if ( nr != -1 )
{
item->setText( i, QgsCoordinateTransform::datumTransformString( nr ) );
}
}
mDatumTransformTreeWidget->addTopLevelItem( item );
}
}

QgsDatumTransformDialog::~QgsDatumTransformDialog()
{
}

QgsDatumTransformDialog::QgsDatumTransformDialog(): QDialog()
{
setupUi( this );
}

QList< int > QgsDatumTransformDialog::selectedDatumTransform()
{
QList<int> list;
QTreeWidgetItem * item = mDatumTransformTreeWidget->currentItem();
if ( item )
{
for ( int i = 0; i < 2; ++i )
{
int transformNr = item->data( i, Qt::UserRole ).toInt();
if ( transformNr != -1 )
{
list << transformNr;
}
}
}
return list;
}
17 changes: 17 additions & 0 deletions src/gui/qgsdatumtransformdialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef QGSDATUMTRANSFORMDIALOG_H
#define QGSDATUMTRANSFORMDIALOG_H

#include "ui_qgsdatumtransformdialogbase.h"

class GUI_EXPORT QgsDatumTransformDialog: public QDialog, private Ui::QgsDatumTransformDialogBase
{
public:
QgsDatumTransformDialog( const QString& layerName, const QList< QList< int > >& dt, QWidget * parent = 0, Qt::WindowFlags f = 0 );
~QgsDatumTransformDialog();

QList< int > selectedDatumTransform();
private:
QgsDatumTransformDialog();
};

#endif // QGSDATUMTRANSFORMDIALOG_H
7 changes: 5 additions & 2 deletions src/gui/qgshighlight.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ void QgsHighlight::init()
{
if ( mMapCanvas->mapRenderer()->hasCrsTransformEnabled() )
{
QgsCoordinateTransform transform( mLayer->crs(), mMapCanvas->mapRenderer()->destinationCrs() );
mGeometry->transform( transform );
const QgsCoordinateTransform* ct = mMapCanvas->mapRenderer()->transformation( mLayer );
if ( ct )
{
mGeometry->transform( *ct );
}
}
updateRect();
update();
Expand Down
45 changes: 45 additions & 0 deletions src/gui/qgsmapcanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ email : sherman at mrcc.com

#include "qgis.h"
#include "qgsapplication.h"
#include "qgscrscache.h"
#include "qgsdatumtransformdialog.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsmapcanvasmap.h"
Expand Down Expand Up @@ -110,6 +112,8 @@ QgsMapCanvas::QgsMapCanvas( QWidget * parent, const char *name )
setFocusPolicy( Qt::StrongFocus );

mMapRenderer = new QgsMapRenderer;
connect( mMapRenderer, SIGNAL( datumTransformInfoRequested( const QgsMapLayer*, const QString&, const QString& ) ),
this, SLOT( getDatumTransformInfo( const QgsMapLayer*, const QString& , const QString& ) ) );

// create map canvas item which will show the map
mMap = new QgsMapCanvasMap( this );
Expand Down Expand Up @@ -1543,6 +1547,47 @@ void QgsMapCanvas::writeProject( QDomDocument & doc )
mMapRenderer->writeXML( mapcanvasNode, doc );
}

/**Ask user which datum transform to use*/
void QgsMapCanvas::getDatumTransformInfo( const QgsMapLayer* ml, const QString& srcAuthId, const QString& destAuthId )
{
if ( !ml )
{
return;
}

const QgsCoordinateReferenceSystem& srcCRS = QgsCRSCache::instance()->crsByAuthId( srcAuthId );
const QgsCoordinateReferenceSystem& destCRS = QgsCRSCache::instance()->crsByAuthId( destAuthId );

//get list of datum transforms
QList< QList< int > > dt = QgsCoordinateTransform::datumTransformations( srcCRS, destCRS );
if ( dt.size() < 2 )
{
return;
}

//if several possibilities: present dialog
QgsDatumTransformDialog d( ml->name(), dt );
if ( mMapRenderer && ( d.exec() == QDialog::Accepted ) )
{
int srcTransform = -1;
int destTransform = -1;
QList<int> t = d.selectedDatumTransform();
if ( t.size() > 0 )
{
srcTransform = t.at( 0 );
}
if ( t.size() > 1 )
{
destTransform = t.at( 1 );
}
mMapRenderer->addLayerCoordinateTransform( ml->id(), srcAuthId, destAuthId, srcTransform, destTransform );
}
else
{
mMapRenderer->addLayerCoordinateTransform( ml->id(), srcAuthId, destAuthId, -1, -1 );
}
}

void QgsMapCanvas::zoomByFactor( double scaleFactor )
{
if ( mDrawing )
Expand Down
3 changes: 3 additions & 0 deletions src/gui/qgsmapcanvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
//! called to write map canvas settings to project
void writeProject( QDomDocument & );

//! ask user about datum transformation
void getDatumTransformInfo( const QgsMapLayer* ml, const QString& srcAuthId, const QString& destAuthId );

private slots:
//! called when current maptool is destroyed
void mapToolDestroyed();
Expand Down
5 changes: 5 additions & 0 deletions src/mapserver/qgsconfigparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,3 +599,8 @@ void QgsConfigParser::serviceCapabilities( QDomElement& parentElement, QDomDocum
}
}
}

QList< QPair< QString, QgsLayerCoordinateTransform > > QgsConfigParser::layerCoordinateTransforms() const
{
return QList< QPair< QString, QgsLayerCoordinateTransform > >();
}
2 changes: 2 additions & 0 deletions src/mapserver/qgsconfigparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ class QgsConfigParser
/**Applies configuration specific label settings*/
virtual void loadLabelSettings( QgsLabelingEngineInterface* lbl ) { Q_UNUSED( lbl ); }

virtual QList< QPair< QString, QgsLayerCoordinateTransform > > layerCoordinateTransforms() const;

protected:
/**Parser to forward not resolved requests (e.g. SLD parser based on user request might have a fallback parser with admin configuration)*/
QgsConfigParser* mFallbackParser;
Expand Down
27 changes: 27 additions & 0 deletions src/mapserver/qgsprojectparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3670,3 +3670,30 @@ void QgsProjectParser::loadLabelSettings( QgsLabelingEngineInterface* lbl )
}
}
}

QList< QPair< QString, QgsLayerCoordinateTransform > > QgsProjectParser::layerCoordinateTransforms() const
{
QList< QPair< QString, QgsLayerCoordinateTransform > > layerTransformList;

QDomElement coordTransformInfoElem = mXMLDoc->documentElement().firstChildElement( "mapcanvas" ).firstChildElement( "layer_coordinate_transform_info" );
if ( coordTransformInfoElem.isNull() )
{
return layerTransformList;
}

QDomNodeList layerTransformNodeList = coordTransformInfoElem.elementsByTagName( "layer_coordinate_transform" );
for ( int i = 0; i < layerTransformNodeList.size(); ++i )
{
QPair< QString, QgsLayerCoordinateTransform > layerEntry;
QDomElement layerTransformElem = layerTransformNodeList.at( i ).toElement();
layerEntry.first = layerTransformElem.attribute( "layerid" );
QgsLayerCoordinateTransform t;
t.srcAuthId = layerTransformElem.attribute( "srcAuthId" );
t.destAuthId = layerTransformElem.attribute( "destAuthId" );
t.srcDatumTransform = layerTransformElem.attribute( "srcDatumTransform", "-1" ).toInt();
t.destDatumTransform = layerTransformElem.attribute( "destDatumTransform", "-1" ).toInt();
layerEntry.second = t;
layerTransformList.push_back( layerEntry );
}
return layerTransformList;
}
2 changes: 2 additions & 0 deletions src/mapserver/qgsprojectparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ class QgsProjectParser: public QgsConfigParser

void loadLabelSettings( QgsLabelingEngineInterface* lbl );

QList< QPair< QString, QgsLayerCoordinateTransform > > layerCoordinateTransforms() const;

private:

//forbidden
Expand Down
13 changes: 13 additions & 0 deletions src/mapserver/qgswmsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,7 @@ int QgsWMSServer::configureMapRender( const QPaintDevice* paintDevice ) const
return 1; //paint device is needed for height, width, dpi
}

mMapRenderer->clearLayerCoordinateTransforms();
mMapRenderer->setOutputSize( QSize( paintDevice->width(), paintDevice->height() ), paintDevice->logicalDpiX() );

//map extent
Expand Down Expand Up @@ -1259,6 +1260,18 @@ int QgsWMSServer::configureMapRender( const QPaintDevice* paintDevice ) const
mMapRenderer->setDestinationCrs( outputCRS );
mMapRenderer->setProjectionsEnabled( true );
mapUnits = outputCRS.mapUnits();

//read layer coordinate transforms from project file (e.g. ct with special datum shift)
if ( mConfigParser )
{
QList< QPair< QString, QgsLayerCoordinateTransform > > lt = mConfigParser->layerCoordinateTransforms();
QList< QPair< QString, QgsLayerCoordinateTransform > >::const_iterator ltIt = lt.constBegin();
for ( ; ltIt != lt.constEnd(); ++ltIt )
{
QgsLayerCoordinateTransform t = ltIt->second;
mMapRenderer->addLayerCoordinateTransform( ltIt->first, t.srcAuthId, t.destAuthId, t.srcDatumTransform, t.destDatumTransform );
}
}
}
mMapRenderer->setMapUnits( mapUnits );

Expand Down
83 changes: 83 additions & 0 deletions src/ui/qgsdatumtransformdialogbase.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsDatumTransformDialogBase</class>
<widget class="QDialog" name="QgsDatumTransformDialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>569</width>
<height>237</height>
</rect>
</property>
<property name="windowTitle">
<string>Select datum transformations</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTreeWidget" name="mDatumTransformTreeWidget">
<column>
<property name="text">
<string>src transform</string>
</property>
</column>
<column>
<property name="text">
<string>dst transform</string>
</property>
</column>
<column>
<property name="text">
<string/>
</property>
</column>
</widget>
</item>
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QgsDatumTransformDialogBase</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QgsDatumTransformDialogBase</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>