Skip to content

Commit a9aafd0

Browse files
committed
apply #3645: synchronization of srs.db with GDAL/PROJ on installation
1 parent 86bcdc4 commit a9aafd0

8 files changed

+247
-6
lines changed

debian/qgis-providers.install

+1
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ usr/lib/qgis/plugins/libmemoryprovider.so
88
usr/lib/qgis/plugins/libspatialiteprovider.so
99
usr/lib/qgis/plugins/libosmprovider.so
1010
usr/lib/qgis/plugins/libgdalprovider.so
11+
usr/lib/qgis/crssync

ms-windows/osgeo4w/postinstall.bat

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ set O4W_ROOT=%OSGEO4W_ROOT%
88
set OSGEO4W_ROOT=%OSGEO4W_ROOT:\=\\%
99
textreplace -std -t "%O4W_ROOT%\apps\@package@\bin\qgis.reg"
1010
"%WINDIR%\regedit" /s "%O4W_ROOT%\apps\@package@\bin\qgis.reg"
11+
12+
start "CRS synchronization" /wait %OSGEO4W_ROOT%\apps\@package@\crssync

python/core/qgscoordinatereferencesystem.sip

+6-4
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,10 @@ class QgsCoordinateReferenceSystem
253253
* @return QGis::UnitType that gives the units for the coordinate system
254254
*/
255255
QGis::UnitType mapUnits() const;
256-
257-
256+
/*! Update proj.4 parameters in our database from proj.4
257+
* @returns number of updated CRS on success and
258+
* negative number of failed updates in case of errors.
259+
* @note added in 1.8
260+
*/
261+
static int syncDb();
258262
};
259-
260-

src/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SUBDIRS(astyle core analysis ui gui app providers plugins helpviewer)
1+
SUBDIRS(astyle core analysis ui gui app providers plugins helpviewer crssync)
22

33
IF (WITH_BINDINGS)
44
SUBDIRS(python)

src/core/qgscoordinatereferencesystem.cpp

+137
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "qgis.h" //const vals declared here
3333

3434
#include <sqlite3.h>
35+
#include <proj_api.h>
3536

3637
//gdal and ogr includes (needed for == operator)
3738
#include <ogr_srs_api.h>
@@ -1353,3 +1354,139 @@ QString QgsCoordinateReferenceSystem::quotedValue( QString value )
13531354
value.replace( "'", "''" );
13541355
return value.prepend( "'" ).append( "'" );
13551356
}
1357+
1358+
int QgsCoordinateReferenceSystem::syncDb()
1359+
{
1360+
int updated = 0, errors = 0;
1361+
sqlite3 *database;
1362+
if ( sqlite3_open( QgsApplication::srsDbFilePath().toUtf8().constData(), &database ) != SQLITE_OK )
1363+
{
1364+
qCritical( "Can't open database: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
1365+
return -1;
1366+
}
1367+
1368+
const char *tail;
1369+
sqlite3_stmt *select;
1370+
QString sql = "select auth_name,auth_id,parameters from tbl_srs WHERE auth_name IS NOT NULL AND auth_id IS NOT NULL";
1371+
if ( sqlite3_prepare( database, sql.toAscii(), sql.size(), &select, &tail ) != SQLITE_OK )
1372+
{
1373+
qCritical( "Could not prepare: %s [%s]\n", sql.toAscii().constData(), sqlite3_errmsg( database ) );
1374+
sqlite3_close( database );
1375+
return -1;
1376+
}
1377+
1378+
OGRSpatialReferenceH crs = OSRNewSpatialReference( NULL );
1379+
1380+
while ( sqlite3_step( select ) == SQLITE_ROW )
1381+
{
1382+
const char *auth_name = ( const char * ) sqlite3_column_text( select, 0 );
1383+
const char *auth_id = ( const char * ) sqlite3_column_text( select, 1 );
1384+
const char *params = ( const char * ) sqlite3_column_text( select, 2 );
1385+
1386+
QString proj4;
1387+
1388+
if ( QString( auth_name ).compare( "epsg", Qt::CaseInsensitive ) == 0 )
1389+
{
1390+
OGRErr ogrErr = OSRSetFromUserInput( crs, QString( "epsg:%1" ).arg( auth_id ).toAscii() );
1391+
1392+
if ( ogrErr == OGRERR_NONE )
1393+
{
1394+
char *output = 0;
1395+
1396+
if ( OSRExportToProj4( crs, &output ) == OGRERR_NONE )
1397+
{
1398+
proj4 = output;
1399+
proj4 = proj4.trimmed();
1400+
}
1401+
else
1402+
{
1403+
QgsDebugMsg( QString( "could not retrieve proj.4 string for epsg:%1 from OGR" ).arg( auth_id ) );
1404+
}
1405+
1406+
if ( output )
1407+
CPLFree( output );
1408+
}
1409+
}
1410+
#if !defined(PJ_VERSION) || PJ_VERSION!=470
1411+
// 4.7.0 has a bug that crashes after 16 consecutive pj_init_plus with different strings
1412+
else
1413+
{
1414+
input = QString( "+init=%1:%2" ).arg( QString( auth_name ).toLower() ).arg( auth_id );
1415+
projPJ pj = pj_init_plus( input.toAscii() );
1416+
if ( !pj )
1417+
{
1418+
input = QString( "+init=%1:%2" ).arg( QString( auth_name ).toUpper() ).arg( auth_id );
1419+
pj = pj_init_plus( input.toAscii() );
1420+
}
1421+
1422+
if ( pj )
1423+
{
1424+
char *def = pj_get_def( pj, 0 );
1425+
if ( def )
1426+
{
1427+
proj4 = def;
1428+
pj_dalloc( def );
1429+
1430+
input.prepend( ' ' ).append( ' ' );
1431+
if ( proj4.startsWith( input ) )
1432+
{
1433+
proj4 = proj4.mid( input.size() );
1434+
}
1435+
}
1436+
else
1437+
{
1438+
QgsDebugMsg( QString( "could not retrieve proj string for %1 from PROJ" ).arg( input ) );
1439+
}
1440+
}
1441+
else
1442+
{
1443+
QgsDebugMsgLevel( QString( "could not retrieve crs for %1 from PROJ" ).arg( input ), 3 );
1444+
}
1445+
1446+
pj_free( pj );
1447+
}
1448+
#endif
1449+
1450+
if ( proj4.isEmpty() )
1451+
{
1452+
continue;
1453+
}
1454+
1455+
if ( proj4 != params )
1456+
{
1457+
char *errMsg = NULL;
1458+
1459+
sql = QString( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name=%2 AND auth_id=%3" )
1460+
.arg( quotedValue( proj4 ) )
1461+
.arg( quotedValue( auth_name ) )
1462+
.arg( quotedValue( auth_id ) );
1463+
1464+
if ( sqlite3_exec( database, sql.toUtf8(), 0, 0, &errMsg ) != SQLITE_OK )
1465+
{
1466+
qCritical( "Could not execute: %s [%s/%s]\n",
1467+
sql.toLocal8Bit().constData(),
1468+
sqlite3_errmsg( database ),
1469+
errMsg ? errMsg : "(unknown error)" );
1470+
errors++;
1471+
}
1472+
else
1473+
{
1474+
updated++;
1475+
QgsDebugMsgLevel( QString( "SQL: %1\n OLD:%2\n NEW:%3" ).arg( sql ).arg( params ).arg( proj4 ), 3 );
1476+
}
1477+
1478+
if ( errMsg )
1479+
sqlite3_free( errMsg );
1480+
}
1481+
}
1482+
1483+
OSRDestroySpatialReference( crs );
1484+
1485+
sqlite3_finalize( select );
1486+
sqlite3_close( database );
1487+
1488+
if ( errors > 0 )
1489+
return -errors;
1490+
else
1491+
return updated;
1492+
}

src/core/qgscoordinatereferencesystem.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,13 @@ class CORE_EXPORT QgsCoordinateReferenceSystem
322322
/*! Get user hint for validation
323323
*/
324324
QString validationHint();
325+
/*! Update proj.4 parameters in our database from proj.4
326+
* @returns number of updated CRS on success and
327+
* negative number of failed updates in case of errors.
328+
* @note added in 1.8
329+
*/
330+
static int syncDb();
331+
325332
// Mutators -----------------------------------
326333
// We don't want to expose these to the public api since they wont create
327334
// a fully valid crs. Programmers should use the createFrom* methods rather
@@ -418,7 +425,7 @@ class CORE_EXPORT QgsCoordinateReferenceSystem
418425
long getRecordCount();
419426

420427
//! Helper for sql-safe value quoting
421-
QString quotedValue( QString value );
428+
static QString quotedValue( QString value );
422429

423430
void *mCRS;
424431

src/crssync/CMakeLists.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
ADD_EXECUTABLE(crssync main.cpp)
2+
INCLUDE_DIRECTORIES(
3+
../core
4+
${GDAL_INCLUDE_DIR}
5+
${PROJ_INCLUDE_DIR}
6+
)
7+
8+
TARGET_LINK_LIBRARIES(crssync
9+
qgis_core
10+
${PROJ_LIBRARY}
11+
${GDAL_LIBRARY}
12+
)
13+
14+
INSTALL(CODE "MESSAGE(\"Installing crssync ...\")")
15+
INSTALL(TARGETS crssync RUNTIME DESTINATION ${QGIS_LIBEXEC_DIR})

src/crssync/main.cpp

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/***************************************************************************
2+
crssync.cpp
3+
sync srs.db with proj
4+
-------------------
5+
begin : 2011
6+
copyright : (C) 2011 by Juergen E. Fischer, norBIT GmbH
7+
email : jef at norbit dot de
8+
***************************************************************************/
9+
10+
/***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************/
18+
#include "qgsapplication.h"
19+
#include "qgscoordinatereferencesystem.h"
20+
#include "qgsconfig.h"
21+
22+
#include <QRegExp>
23+
24+
#include <iostream>
25+
#include <limits>
26+
27+
#include <cpl_error.h>
28+
29+
void CPL_STDCALL showError( CPLErr errClass, int errNo, const char *msg )
30+
{
31+
QRegExp re( "EPSG PCS/GCS code \\d+ not found in EPSG support files. Is this a valid\nEPSG coordinate system?" );
32+
if ( errNo != 6 && !re.exactMatch( msg ) )
33+
{
34+
std::cerr << msg;
35+
}
36+
}
37+
38+
int main( int argc, char ** argv )
39+
{
40+
QgsApplication a( argc, argv, false );
41+
42+
#if defined(Q_WS_MACX)
43+
// If we're on Mac, we have the resource library way above us...
44+
a.setPkgDataPath( QgsApplication::prefixPath() + "/../../../../" + QString( QGIS_DATA_SUBDIR ) );
45+
#elif defined(Q_WS_WIN)
46+
a.setPkgDataPath( QgsApplication::prefixPath() + "/" QGIS_DATA_SUBDIR );
47+
#else
48+
a.setPkgDataPath( QgsApplication::prefixPath() + "/../" QGIS_DATA_SUBDIR );
49+
#endif
50+
51+
std::cout << "Synchronizing CRS database with PROJ definitions." << std::endl;
52+
53+
CPLPushErrorHandler( showError );
54+
55+
int res = QgsCoordinateReferenceSystem::syncDb();
56+
57+
CPLPopErrorHandler();
58+
59+
if ( res == 0 )
60+
{
61+
std::cout << "No CRS updates were necessary." << std::endl;
62+
}
63+
else if ( res > 0 )
64+
{
65+
std::cout << res << " CRSs updated." << std::endl;
66+
}
67+
else if ( res == std::numeric_limits<int>::min() )
68+
{
69+
std::cout << "CRSs synchronization not possible." << std::endl;
70+
}
71+
else if ( res < 0 )
72+
{
73+
std::cout << -res << " CRSs could not be updated." << std::endl;
74+
}
75+
76+
return 0;
77+
}

0 commit comments

Comments
 (0)