Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
apply #3645: synchronization of srs.db with GDAL/PROJ on installation
  • Loading branch information
jef-n committed May 5, 2011
1 parent 86bcdc4 commit a9aafd0
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 6 deletions.
1 change: 1 addition & 0 deletions debian/qgis-providers.install
Expand Up @@ -8,3 +8,4 @@ usr/lib/qgis/plugins/libmemoryprovider.so
usr/lib/qgis/plugins/libspatialiteprovider.so
usr/lib/qgis/plugins/libosmprovider.so
usr/lib/qgis/plugins/libgdalprovider.so
usr/lib/qgis/crssync
2 changes: 2 additions & 0 deletions ms-windows/osgeo4w/postinstall.bat
Expand Up @@ -8,3 +8,5 @@ set O4W_ROOT=%OSGEO4W_ROOT%
set OSGEO4W_ROOT=%OSGEO4W_ROOT:\=\\%
textreplace -std -t "%O4W_ROOT%\apps\@package@\bin\qgis.reg"
"%WINDIR%\regedit" /s "%O4W_ROOT%\apps\@package@\bin\qgis.reg"

start "CRS synchronization" /wait %OSGEO4W_ROOT%\apps\@package@\crssync
10 changes: 6 additions & 4 deletions python/core/qgscoordinatereferencesystem.sip
Expand Up @@ -253,8 +253,10 @@ class QgsCoordinateReferenceSystem
* @return QGis::UnitType that gives the units for the coordinate system
*/
QGis::UnitType mapUnits() const;


/*! Update proj.4 parameters in our database from proj.4
* @returns number of updated CRS on success and
* negative number of failed updates in case of errors.
* @note added in 1.8
*/
static int syncDb();
};


2 changes: 1 addition & 1 deletion src/CMakeLists.txt
@@ -1,4 +1,4 @@
SUBDIRS(astyle core analysis ui gui app providers plugins helpviewer)
SUBDIRS(astyle core analysis ui gui app providers plugins helpviewer crssync)

IF (WITH_BINDINGS)
SUBDIRS(python)
Expand Down
137 changes: 137 additions & 0 deletions src/core/qgscoordinatereferencesystem.cpp
Expand Up @@ -32,6 +32,7 @@
#include "qgis.h" //const vals declared here

#include <sqlite3.h>
#include <proj_api.h>

//gdal and ogr includes (needed for == operator)
#include <ogr_srs_api.h>
Expand Down Expand Up @@ -1353,3 +1354,139 @@ QString QgsCoordinateReferenceSystem::quotedValue( QString value )
value.replace( "'", "''" );
return value.prepend( "'" ).append( "'" );
}

int QgsCoordinateReferenceSystem::syncDb()
{
int updated = 0, errors = 0;
sqlite3 *database;
if ( sqlite3_open( QgsApplication::srsDbFilePath().toUtf8().constData(), &database ) != SQLITE_OK )
{
qCritical( "Can't open database: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
return -1;
}

const char *tail;
sqlite3_stmt *select;
QString sql = "select auth_name,auth_id,parameters from tbl_srs WHERE auth_name IS NOT NULL AND auth_id IS NOT NULL";
if ( sqlite3_prepare( database, sql.toAscii(), sql.size(), &select, &tail ) != SQLITE_OK )
{
qCritical( "Could not prepare: %s [%s]\n", sql.toAscii().constData(), sqlite3_errmsg( database ) );
sqlite3_close( database );
return -1;
}

OGRSpatialReferenceH crs = OSRNewSpatialReference( NULL );

while ( sqlite3_step( select ) == SQLITE_ROW )
{
const char *auth_name = ( const char * ) sqlite3_column_text( select, 0 );
const char *auth_id = ( const char * ) sqlite3_column_text( select, 1 );
const char *params = ( const char * ) sqlite3_column_text( select, 2 );

QString proj4;

if ( QString( auth_name ).compare( "epsg", Qt::CaseInsensitive ) == 0 )
{
OGRErr ogrErr = OSRSetFromUserInput( crs, QString( "epsg:%1" ).arg( auth_id ).toAscii() );

if ( ogrErr == OGRERR_NONE )
{
char *output = 0;

if ( OSRExportToProj4( crs, &output ) == OGRERR_NONE )
{
proj4 = output;
proj4 = proj4.trimmed();
}
else
{
QgsDebugMsg( QString( "could not retrieve proj.4 string for epsg:%1 from OGR" ).arg( auth_id ) );
}

if ( output )
CPLFree( output );
}
}
#if !defined(PJ_VERSION) || PJ_VERSION!=470
// 4.7.0 has a bug that crashes after 16 consecutive pj_init_plus with different strings
else
{
input = QString( "+init=%1:%2" ).arg( QString( auth_name ).toLower() ).arg( auth_id );
projPJ pj = pj_init_plus( input.toAscii() );
if ( !pj )
{
input = QString( "+init=%1:%2" ).arg( QString( auth_name ).toUpper() ).arg( auth_id );
pj = pj_init_plus( input.toAscii() );
}

if ( pj )
{
char *def = pj_get_def( pj, 0 );
if ( def )
{
proj4 = def;
pj_dalloc( def );

input.prepend( ' ' ).append( ' ' );
if ( proj4.startsWith( input ) )
{
proj4 = proj4.mid( input.size() );
}
}
else
{
QgsDebugMsg( QString( "could not retrieve proj string for %1 from PROJ" ).arg( input ) );
}
}
else
{
QgsDebugMsgLevel( QString( "could not retrieve crs for %1 from PROJ" ).arg( input ), 3 );
}

pj_free( pj );
}
#endif

if ( proj4.isEmpty() )
{
continue;
}

if ( proj4 != params )
{
char *errMsg = NULL;

sql = QString( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name=%2 AND auth_id=%3" )
.arg( quotedValue( proj4 ) )
.arg( quotedValue( auth_name ) )
.arg( quotedValue( auth_id ) );

if ( sqlite3_exec( database, sql.toUtf8(), 0, 0, &errMsg ) != SQLITE_OK )
{
qCritical( "Could not execute: %s [%s/%s]\n",
sql.toLocal8Bit().constData(),
sqlite3_errmsg( database ),
errMsg ? errMsg : "(unknown error)" );
errors++;
}
else
{
updated++;
QgsDebugMsgLevel( QString( "SQL: %1\n OLD:%2\n NEW:%3" ).arg( sql ).arg( params ).arg( proj4 ), 3 );
}

if ( errMsg )
sqlite3_free( errMsg );
}
}

OSRDestroySpatialReference( crs );

sqlite3_finalize( select );
sqlite3_close( database );

if ( errors > 0 )
return -errors;
else
return updated;
}
9 changes: 8 additions & 1 deletion src/core/qgscoordinatereferencesystem.h
Expand Up @@ -322,6 +322,13 @@ class CORE_EXPORT QgsCoordinateReferenceSystem
/*! Get user hint for validation
*/
QString validationHint();
/*! Update proj.4 parameters in our database from proj.4
* @returns number of updated CRS on success and
* negative number of failed updates in case of errors.
* @note added in 1.8
*/
static int syncDb();

// Mutators -----------------------------------
// We don't want to expose these to the public api since they wont create
// a fully valid crs. Programmers should use the createFrom* methods rather
Expand Down Expand Up @@ -418,7 +425,7 @@ class CORE_EXPORT QgsCoordinateReferenceSystem
long getRecordCount();

//! Helper for sql-safe value quoting
QString quotedValue( QString value );
static QString quotedValue( QString value );

void *mCRS;

Expand Down
15 changes: 15 additions & 0 deletions src/crssync/CMakeLists.txt
@@ -0,0 +1,15 @@
ADD_EXECUTABLE(crssync main.cpp)
INCLUDE_DIRECTORIES(
../core
${GDAL_INCLUDE_DIR}
${PROJ_INCLUDE_DIR}
)

TARGET_LINK_LIBRARIES(crssync
qgis_core
${PROJ_LIBRARY}
${GDAL_LIBRARY}
)

INSTALL(CODE "MESSAGE(\"Installing crssync ...\")")
INSTALL(TARGETS crssync RUNTIME DESTINATION ${QGIS_LIBEXEC_DIR})
77 changes: 77 additions & 0 deletions src/crssync/main.cpp
@@ -0,0 +1,77 @@
/***************************************************************************
crssync.cpp
sync srs.db with proj
-------------------
begin : 2011
copyright : (C) 2011 by Juergen E. Fischer, norBIT GmbH
email : jef at norbit dot de
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsapplication.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsconfig.h"

#include <QRegExp>

#include <iostream>
#include <limits>

#include <cpl_error.h>

void CPL_STDCALL showError( CPLErr errClass, int errNo, const char *msg )
{
QRegExp re( "EPSG PCS/GCS code \\d+ not found in EPSG support files. Is this a valid\nEPSG coordinate system?" );
if ( errNo != 6 && !re.exactMatch( msg ) )
{
std::cerr << msg;
}
}

int main( int argc, char ** argv )
{
QgsApplication a( argc, argv, false );

#if defined(Q_WS_MACX)
// If we're on Mac, we have the resource library way above us...
a.setPkgDataPath( QgsApplication::prefixPath() + "/../../../../" + QString( QGIS_DATA_SUBDIR ) );
#elif defined(Q_WS_WIN)
a.setPkgDataPath( QgsApplication::prefixPath() + "/" QGIS_DATA_SUBDIR );
#else
a.setPkgDataPath( QgsApplication::prefixPath() + "/../" QGIS_DATA_SUBDIR );
#endif

std::cout << "Synchronizing CRS database with PROJ definitions." << std::endl;

CPLPushErrorHandler( showError );

int res = QgsCoordinateReferenceSystem::syncDb();

CPLPopErrorHandler();

if ( res == 0 )
{
std::cout << "No CRS updates were necessary." << std::endl;
}
else if ( res > 0 )
{
std::cout << res << " CRSs updated." << std::endl;
}
else if ( res == std::numeric_limits<int>::min() )
{
std::cout << "CRSs synchronization not possible." << std::endl;
}
else if ( res < 0 )
{
std::cout << -res << " CRSs could not be updated." << std::endl;
}

return 0;
}

0 comments on commit a9aafd0

Please sign in to comment.