Index: src/headers/spatialite_private.h
==================================================================
--- src/headers/spatialite_private.h
+++ src/headers/spatialite_private.h
@@ -246,10 +246,12 @@
 					     struct epsg_defs **first,
 					     struct epsg_defs **last);
 
     SPATIALITE_PRIVATE void free_epsg (struct epsg_defs *first);
 
+    SPATIALITE_PRIVATE int exists_spatial_ref_sys (void *handle);
+    
     SPATIALITE_PRIVATE int checkSpatialMetaData (const void *sqlite);
 
     SPATIALITE_PRIVATE int delaunay_triangle_check (void *pg);
 
     SPATIALITE_PRIVATE void *voronoj_build (int pgs, void *first,

Index: src/spatialite/spatialite.c
==================================================================
--- src/spatialite/spatialite.c
+++ src/spatialite/spatialite.c
@@ -58,10 +58,11 @@
 #include <sys/types.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
+#include <errno.h>
 #include <float.h>
 #include <locale.h>
 
 #if defined(_WIN32) && !defined(__MINGW32__)
 #include <io.h>
@@ -7697,14 +7698,68 @@
 }
 
 /* END of Klaus Foerster AsSvg() implementation */
 
 SPATIALITE_PRIVATE void
-getProjParams (void *p_sqlite, int srid, char **proj_params)
+getProjParamsFromSpatialReferenceSystemTable(sqlite3 *sqlite, int srid, char **proj_params)
 {
-/* retrives the PROJ params from SPATIAL_SYS_REF table, if possible */
-    sqlite3 *sqlite = (sqlite3 *) p_sqlite;
+    char *sql;
+    char **results;
+    int rows;
+    int columns;
+    int i;
+    int ret;
+    int len;
+    const char *proj4text;
+    char *errMsg = NULL;
+    *proj_params = NULL;
+    sql = sqlite3_mprintf("SELECT proj4text FROM spatial_ref_sys WHERE srid = %d", srid);
+    ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
+    sqlite3_free (sql);
+    if (ret != SQLITE_OK)
+      {
+        spatialite_e ("unknown SRID: %d\t<%s>\n", srid, errMsg);
+        sqlite3_free (errMsg);
+        return;
+      }
+    for (i = 1; i <= rows; i++)
+      {
+        proj4text = results[(i * columns)];
+        if (proj4text != NULL)
+          {
+            len = strlen (proj4text);
+            *proj_params = malloc (len + 1);
+            strcpy (*proj_params, proj4text);
+          }
+      }
+    if (*proj_params == NULL)
+      {
+        spatialite_e ("unknown SRID: %d\n", srid);
+      }
+    sqlite3_free_table (results);
+}
+
+static int
+coordinates_system_matches(struct epsg_defs *iter, const char *organization, const int organization_coordsys_id)
+{
+    if (organization == NULL)
+      {
+        /* we didn't have a good lookup on the target database, so use best-efforts lookup */
+        return ((iter->srid == organization_coordsys_id)
+            && (iter->proj4text != NULL));
+      }
+    else
+      {
+        return ((strcasecmp(iter->auth_name, organization) == 0)
+            && (iter->auth_srid ==  organization_coordsys_id)
+            && (iter->proj4text != NULL));
+      }
+}
+
+SPATIALITE_PRIVATE void
+getProjParamsFromGeopackageTable(sqlite3 *sqlite, int srid, char **proj_params)
+{
     char *sql;
     char **results;
     int rows;
     int columns;
     int i;
@@ -7711,33 +7766,95 @@
     int ret;
     int len;
     const char *proj4text;
     char *errMsg = NULL;
     *proj_params = NULL;
-    sql = sqlite3_mprintf
-	("SELECT proj4text FROM spatial_ref_sys WHERE srid = %d", srid);
+    struct epsg_defs *first = NULL;
+    struct epsg_defs *last = NULL;
+    struct epsg_defs *iter = NULL;
+    const char *organization = NULL;
+    int organization_coordsys_id = -1;
+
+    sql = sqlite3_mprintf("SELECT organization, organization_coordsys_id FROM gpkg_spatial_ref_sys WHERE srs_id = %d", srid);
     ret = sqlite3_get_table (sqlite, sql, &results, &rows, &columns, &errMsg);
     sqlite3_free (sql);
     if (ret != SQLITE_OK)
       {
-	  spatialite_e ("unknown SRID: %d\t<%s>\n", srid, errMsg);
-	  sqlite3_free (errMsg);
-	  return;
-      }
-    for (i = 1; i <= rows; i++)
-      {
-	  proj4text = results[(i * columns)];
-	  if (proj4text != NULL)
-	    {
-		len = strlen (proj4text);
-		*proj_params = malloc (len + 1);
-		strcpy (*proj_params, proj4text);
-	    }
-      }
-    if (*proj_params == NULL)
-	spatialite_e ("unknown SRID: %d\n", srid);
+        spatialite_e ("unknown SRID: %d\t<%s>\n", srid, errMsg);
+        sqlite3_free (errMsg);
+        return;
+      }
+    if (rows == 0)
+      {
+        printf ("unknown SRID: %d\t(not in local database, ignoring authority and using best efforts...)\n", srid);
+        organization_coordsys_id = srid;
+      }
+    else if (rows == 1)
+      {
+        /* there are 'columns' entries in the header row (result indexes 0 to columns - 1), and our data is the next row */
+        organization = results[columns];
+        errno = 0;
+        organization_coordsys_id = strtol(results[columns + 1], NULL, 10);
+        if ((errno != 0) || (organization_coordsys_id == 0))
+          {
+              spatialite_e ("Invalid organization_coordsys_id format: %s\n", results[columns + 1]);
+              sqlite3_free_table(results);
+              return;
+          }
+      }
+    else if (rows > 1)
+      {
+        spatialite_e ("invalid or corrupt gpkg_spatial_ref_sys table - duplicate entries for : %d\n", srid);
+        sqlite3_free_table (results);
+        return;
+      }
+
+    if (organization == NULL)
+      {
+        /* best-effort mode */
+        initialize_epsg(srid, &first, &last);
+      }
+    else
+      {
+        initialize_epsg (GAIA_EPSG_ANY, &first, &last);
+      }
+    iter = first;
+    while (iter)
+      {
+        if (coordinates_system_matches(iter, organization, organization_coordsys_id))
+          {
+            len = strlen (iter->proj4text);
+            *proj_params = malloc (len + 1);
+            strcpy (*proj_params, iter->proj4text);
+            free_epsg(first);
+            sqlite3_free_table (results);
+            return;
+          }
+        iter = iter->next;
+      }
+    /* if we get here, we didn't find a match */
+    free_epsg(first);
     sqlite3_free_table (results);
+    spatialite_e ("unknown SRID: %d\n", srid);
+}
+
+
+SPATIALITE_PRIVATE void
+getProjParams (void *p_sqlite, int srid, char **proj_params)
+{
+/* retrives the PROJ params from SPATIAL_SYS_REF table, if possible */
+    sqlite3 *sqlite = (sqlite3 *) p_sqlite;
+    if (exists_spatial_ref_sys(sqlite))
+      {
+        /* normal Spatialite case */
+        getProjParamsFromSpatialReferenceSystemTable(sqlite, srid, proj_params);
+      }
+    else
+      {
+        /* geopackage case */
+        getProjParamsFromGeopackageTable(sqlite, srid, proj_params);
+      }
 }
 
 #ifndef OMIT_PROJ		/* PROJ.4 is strictly required to support KML */
 static void
 fnct_AsKml1 (sqlite3_context * context, int argc, sqlite3_value ** argv)

Index: src/srsinit/srs_init.c
==================================================================
--- src/srsinit/srs_init.c
+++ src/srsinit/srs_init.c
@@ -604,21 +604,23 @@
     free_epsg (first);
 
     return 0;
 }
 
-static int
-exists_spatial_ref_sys (sqlite3 * handle)
+SPATIALITE_PRIVATE int
+exists_spatial_ref_sys (void * p_sqlite)
 {
 /* checking if the SPATIAL_REF_SYS table exists */
     int ret;
     int ok = 0;
     char sql[1024];
     char **results;
     int n_rows;
     int n_columns;
     char *err_msg = NULL;
+
+    sqlite3 *handle = (sqlite3 *) p_sqlite;
 
     strcpy (sql,
 	    "SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'spatial_ref_sys'");
     ret =
 	sqlite3_get_table (handle, sql, &results, &n_rows, &n_columns,

ADDED   test/gpkg_test_broken.gpkg
Index: test/gpkg_test_broken.gpkg
==================================================================
--- test/gpkg_test_broken.gpkg
+++ test/gpkg_test_broken.gpkg
cannot compute difference between binary files

ADDED   test/gpkg_test_extrasrid.gpkg
Index: test/gpkg_test_extrasrid.gpkg
==================================================================
--- test/gpkg_test_extrasrid.gpkg
+++ test/gpkg_test_extrasrid.gpkg
cannot compute difference between binary files

ADDED   test/sql_stmt_proj_tests/transform_geopackage1.testcase
Index: test/sql_stmt_proj_tests/transform_geopackage1.testcase
==================================================================
--- test/sql_stmt_proj_tests/transform_geopackage1.testcase
+++ test/sql_stmt_proj_tests/transform_geopackage1.testcase
@@ -0,0 +1,7 @@
+transform - null args
+gpkg_test.gpkg
+SELECT Transform(NULL, NULL)
+1 # rows (not including the header row)
+1 # columns
+Transform(NULL, NULL)
+(NULL)

ADDED   test/sql_stmt_proj_tests/transform_geopackage2.testcase
Index: test/sql_stmt_proj_tests/transform_geopackage2.testcase
==================================================================
--- test/sql_stmt_proj_tests/transform_geopackage2.testcase
+++ test/sql_stmt_proj_tests/transform_geopackage2.testcase
@@ -0,0 +1,7 @@
+transform - Point
+gpkg_test_extrasrid.gpkg
+SELECT AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 27700))
+1 # rows (not including the header row)
+1 # columns
+AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 27700))
+POINT(669088.702263 -761460.136215)

ADDED   test/sql_stmt_proj_tests/transform_geopackage3.testcase
Index: test/sql_stmt_proj_tests/transform_geopackage3.testcase
==================================================================
--- test/sql_stmt_proj_tests/transform_geopackage3.testcase
+++ test/sql_stmt_proj_tests/transform_geopackage3.testcase
@@ -0,0 +1,7 @@
+transform - Unknown SRID
+gpkg_test_extrasrid.gpkg
+SELECT AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 3))
+1 # rows (not including the header row)
+1 # columns
+AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 3))
+(NULL)

ADDED   test/sql_stmt_proj_tests/transform_geopackage4.testcase
Index: test/sql_stmt_proj_tests/transform_geopackage4.testcase
==================================================================
--- test/sql_stmt_proj_tests/transform_geopackage4.testcase
+++ test/sql_stmt_proj_tests/transform_geopackage4.testcase
@@ -0,0 +1,7 @@
+transform - Linestring Z
+gpkg_test.gpkg
+SELECT AsText(Transform(GeomFromText('LINESTRINGZ(11 43 100, 12 43 101, 12 42 102)', 4326), 32632))
+1 # rows (not including the header row)
+1 # columns
+AsText(Transform(GeomFromText('LINESTRINGZ(11 43 100, 12 43 101, 12 42 102)', 4326), 32632))
+LINESTRING Z(663019.070082 4762755.64169 100, 744533.019451 4765182.932802 101, 748464.920716 4654130.891324 102)

ADDED   test/sql_stmt_proj_tests/transform_geopackage5.testcase
Index: test/sql_stmt_proj_tests/transform_geopackage5.testcase
==================================================================
--- test/sql_stmt_proj_tests/transform_geopackage5.testcase
+++ test/sql_stmt_proj_tests/transform_geopackage5.testcase
@@ -0,0 +1,7 @@
+transform - Bogus SRID
+gpkg_test_extrasrid.gpkg
+SELECT AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 888888))
+1 # rows (not including the header row)
+1 # columns
+AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 888888))
+(NULL)

ADDED   test/sql_stmt_proj_tests/transform_geopackage6.testcase
Index: test/sql_stmt_proj_tests/transform_geopackage6.testcase
==================================================================
--- test/sql_stmt_proj_tests/transform_geopackage6.testcase
+++ test/sql_stmt_proj_tests/transform_geopackage6.testcase
@@ -0,0 +1,7 @@
+transform - bad format organization_coordsys_id
+gpkg_test_extrasrid.gpkg
+SELECT AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 888889))
+1 # rows (not including the header row)
+1 # columns
+AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 888889))
+(NULL)

ADDED   test/sql_stmt_proj_tests/transform_geopackage7.testcase
Index: test/sql_stmt_proj_tests/transform_geopackage7.testcase
==================================================================
--- test/sql_stmt_proj_tests/transform_geopackage7.testcase
+++ test/sql_stmt_proj_tests/transform_geopackage7.testcase
@@ -0,0 +1,7 @@
+transform - bad format organization_coordsys_id
+gpkg_test_extrasrid.gpkg
+SELECT AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 888887))
+1 # rows (not including the header row)
+1 # columns
+AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 888887))
+(NULL)

ADDED   test/sql_stmt_proj_tests/transform_geopackage8.testcase
Index: test/sql_stmt_proj_tests/transform_geopackage8.testcase
==================================================================
--- test/sql_stmt_proj_tests/transform_geopackage8.testcase
+++ test/sql_stmt_proj_tests/transform_geopackage8.testcase
@@ -0,0 +1,7 @@
+transform - broken geopackage - no gpkg_spatial_ref_sys table
+gpkg_test_broken.gpkg
+SELECT AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 888887))
+1 # rows (not including the header row)
+1 # columns
+AsText(Transform(GeomFromText('POINT(1.3 43)', 4326), 888887))
+(NULL)

