]> granicus.if.org Git - postgis/commitdiff
Support new Proj6 API
authorPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 22 Feb 2019 21:36:27 +0000 (21:36 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 22 Feb 2019 21:36:27 +0000 (21:36 +0000)
Closes #4322

git-svn-id: http://svn.osgeo.org/postgis/trunk@17274 b70326c6-7e19-0410-871a-916f4a2858ee

30 files changed:
configure.ac
liblwgeom/cunit/cu_geodetic.c
liblwgeom/cunit/cu_geos_cluster.c
liblwgeom/g_box.c
liblwgeom/liblwgeom.h.in
liblwgeom/liblwgeom_internal.h
liblwgeom/lwgeom_transform.c
liblwgeom/lwin_encoded_polyline.c
liblwgeom/lwspheroid.c
liblwgeom/lwunionfind.c
liblwgeom/lwutil.c
liblwgeom/ptarray.c
libpgcommon/lwgeom_cache.c
libpgcommon/lwgeom_cache.h
libpgcommon/lwgeom_transform.c
libpgcommon/lwgeom_transform.h
macros/ac_proj4_version.m4
postgis/geography_inout.c
postgis/geography_measurement.c
postgis/gserialized_gist_2d.c
postgis/gserialized_gist_nd.c
postgis/gserialized_spgist_2d.c
postgis/gserialized_typmod.c
postgis/lwgeom_in_gml.c
postgis/lwgeom_spheroid.c
postgis/lwgeom_transform.c
regress/core/out_geography_expected
regress/core/out_geometry_expected
regress/core/regress_proj_expected
regress/core/tickets_expected

index 796bd0cc880d9e19e59a045a4ed2a37ca10c072f..9cfd44a3cc004e72ba37508560c4338db329ebec 100644 (file)
@@ -868,10 +868,13 @@ fi
 dnl Check that we can find the proj_api.h header file
 CPPFLAGS_SAVE="$CPPFLAGS"
 CPPFLAGS="$PROJ_CPPFLAGS"
-AC_CHECK_HEADER([proj_api.h], [], [AC_MSG_ERROR([could not find proj_api.h - you may need to specify the directory of a PROJ.4 installation using --with-projdir])],
-[
-#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H 1
-])
+AC_CHECK_HEADER([proj_api.h],
+       [],
+       [AC_CHECK_HEADER([proj.h],
+               [],
+               [AC_MSG_ERROR([could not find proj.h or proj_api.h - you may need to specify the directory of a PROJ installation using --with-projdir])]
+               )]
+       )
 
 dnl Return the PROJ.4 version number
 AC_PROJ_VERSION([POSTGIS_PROJ_VERSION])
@@ -879,11 +882,6 @@ AC_DEFINE_UNQUOTED([POSTGIS_PROJ_VERSION], [$POSTGIS_PROJ_VERSION], [PROJ librar
 AC_SUBST([POSTGIS_PROJ_VERSION])
 CPPFLAGS="$CPPFLAGS_SAVE"
 
-dnl For PROJ 6 add the ACCEPT_USE_OF_DEPRECATED_PROJ_API_H definition
-if test "$POSTGIS_PROJ_VERSION" -ge 60 && test "$POSTGIS_PROJ_VERSION" -lt 70; then
-    AC_DEFINE([ACCEPT_USE_OF_DEPRECATED_PROJ_API_H], [1], [Define to use the old PROJ API])
-fi
-
 dnl Ensure that we are using PROJ >= 4.6.0 (requires pj_set_searchpath)
 if test ! "$POSTGIS_PROJ_VERSION" -ge 46; then
        AC_MSG_ERROR([PostGIS requires PROJ >= 4.6.0])
@@ -897,7 +895,7 @@ LIBS_SAVE="$LIBS"
 LIBS="$PROJ_LDFLAGS"
 AC_CHECK_LIB([proj], [pj_get_release],
        [],
-       [AC_MSG_ERROR([could not find libproj - you may need to specify the directory of a PROJ.4 installation using --with-projdir])],
+       [AC_MSG_ERROR([could not find libproj - you may need to specify the directory of a PROJ installation using --with-projdir])],
        [])
 LIBS="$LIBS_SAVE"
 
index 737dadbd22ec6756d3768f92e59f5504a0a94f98..e200360c86b576ed5ed0fcf4bee72b3870c88e78 100644 (file)
@@ -1276,7 +1276,7 @@ static void test_spheroid_distance(void)
 {
        GEOGRAPHIC_POINT g1, g2;
        double d;
-#if ! PROJ_GEODESIC
+#ifndef PROJ_GEODESIC
        double epsilon; /* irregular */
 #else
        const double epsilon = 1e-8; /* at least 10 nm precision */
@@ -1291,7 +1291,7 @@ static void test_spheroid_distance(void)
        point_set(0.0, 0.0, &g1);
        point_set(0.0, 1.0, &g2);
        d = spheroid_distance(&g1, &g2, &s);
-#if ! PROJ_GEODESIC
+#ifndef PROJ_GEODESIC
        epsilon = 1e-6;
 #endif
        CU_ASSERT_DOUBLE_EQUAL(d, 110574.3885577987957342, epsilon);
@@ -1301,7 +1301,7 @@ static void test_spheroid_distance(void)
        point_set(-10.0, 0.0, &g1);
        point_set(0.0, 0.0, &g2);
        d = spheroid_distance(&g1, &g2, &s);
-#if ! PROJ_GEODESIC
+#ifndef PROJ_GEODESIC
        epsilon = 1e-3;
 #endif
        CU_ASSERT_DOUBLE_EQUAL(d, 1113194.9079327357264771, epsilon);
@@ -1311,7 +1311,7 @@ static void test_spheroid_distance(void)
        point_set(-1.0, 0.0, &g1);
        point_set(0.0, 0.0, &g2);
        d = spheroid_distance(&g1, &g2, &s);
-#if ! PROJ_GEODESIC
+#ifndef PROJ_GEODESIC
        epsilon = 1e-4;
 #endif
        CU_ASSERT_DOUBLE_EQUAL(d, 111319.4907932735726477, epsilon);
@@ -1321,7 +1321,7 @@ static void test_spheroid_distance(void)
        point_set(-180.0, 0.0, &g1);
        point_set(0.0, 1.0, &g2);
        d = spheroid_distance(&g1, &g2, &s);
-#if ! PROJ_GEODESIC
+#ifndef PROJ_GEODESIC
        epsilon = 1e-5;
 #endif
        CU_ASSERT_DOUBLE_EQUAL(d, 19893357.0700676468277450, epsilon);
@@ -1331,7 +1331,7 @@ static void test_spheroid_distance(void)
        point_set(-180.0, 0.0, &g1);
        point_set(0.0, 90.0, &g2);
        d = spheroid_distance(&g1, &g2, &s);
-#if ! PROJ_GEODESIC
+#ifndef PROJ_GEODESIC
        epsilon = 1e-6;
 #endif
        CU_ASSERT_DOUBLE_EQUAL(d, 10001965.7293127228117396, epsilon);
@@ -1372,7 +1372,7 @@ static void test_spheroid_area(void)
        a1 = lwgeom_area_sphere(lwg, &s);
        CU_ASSERT_DOUBLE_EQUAL(a1, 12341436880.106982993974659, 0.1);
        /* spheroid: Planimeter -E -p 20 -r --input-string "3 -2;4 -2;4 -1;3 -1" */
-#if PROJ_GEODESIC
+#ifdef PROJ_GEODESIC
        a2 = lwgeom_area_spheroid(lwg, &s);
        CU_ASSERT_DOUBLE_EQUAL(a2, 12286884908.946891319597874, 0.1);
 #endif
@@ -1385,7 +1385,7 @@ static void test_spheroid_area(void)
        a1 = lwgeom_area_sphere(lwg, &s);
        CU_ASSERT_DOUBLE_EQUAL(a1, 12360265021.368023059138681, 0.1);
        /* spheroid: Planimeter -E -p 20 --input-string "2 8.5;1 8.5;1 9.5;2 9.5" */
-#if PROJ_GEODESIC
+#ifdef PROJ_GEODESIC
        a2 = lwgeom_area_spheroid(lwg, &s);
        CU_ASSERT_DOUBLE_EQUAL(a2, 12305128751.042900673161556, 0.1);
 #endif
@@ -1398,7 +1398,7 @@ static void test_spheroid_area(void)
        a1 = lwgeom_area_sphere(lwg, &s);
        CU_ASSERT_DOUBLE_EQUAL(a1, 12360265021.368023059138681, 0.1);
        /* spheroid: Planimeter -E -p 20 -r --input-string "2 179.5;1 179.5;1 178.5;2 178.5" */
-#if PROJ_GEODESIC
+#ifdef PROJ_GEODESIC
        a2 = lwgeom_area_spheroid(lwg, &s);
        CU_ASSERT_DOUBLE_EQUAL(a2, 12305128751.042900673161556, 0.1);
 #endif
@@ -1411,7 +1411,7 @@ static void test_spheroid_area(void)
        a1 = lwgeom_area_sphere(lwg, &s);
        CU_ASSERT_DOUBLE_EQUAL(a1, 12360265021.368023059138681, 0.1);
        /* spheroid: Planimeter -E -p 20 --input-string "2 179.5;1 179.5;1 -179.5;2 -179.5" */
-#if PROJ_GEODESIC
+#ifdef PROJ_GEODESIC
        a2 = lwgeom_area_spheroid(lwg, &s);
        CU_ASSERT_DOUBLE_EQUAL(a2, 12305128751.042900673161556, 0.1);
 #endif
index 6b0f750a635f3bebf4178bcddcf8ae257392b54e..78d8c35c94ab17baa0c9fc6c347910cbd4852b21 100644 (file)
@@ -10,6 +10,8 @@
  *
  **********************************************************************/
 
+#include <stdlib.h>
+
 #include "CUnit/Basic.h"
 
 #include "../lwgeom_log.h"
index 502916de57daaeb7c8ce0f94d430d14cab98a0c0..35b67fce10cb4d435cde0db82f0d5efd80686f73 100644 (file)
@@ -390,11 +390,11 @@ GBOX* gbox_from_string(const char *str)
 
 char* gbox_to_string(const GBOX *gbox)
 {
-       static int sz = 138;
+       const size_t sz = 138;
        char *str = NULL;
 
        if ( ! gbox )
-               return strdup("NULL POINTER");
+               return lwstrdup("NULL POINTER");
 
        str = (char*)lwalloc(sz);
 
index 459d4b209c3a804400ccd4085381ed0de4a28305..ce5803aff894aca4da9bbfa46b12f82177d24494 100644 (file)
 #include <stdint.h>
 
 #include "../postgis_config.h"
-#include "proj_api.h"
 
-#if POSTGIS_PROJ_VERSION >= 49
-/* Enable new geodesic functions */
-#define PROJ_GEODESIC 1
+#if POSTGIS_PROJ_VERSION < 60
+#include "proj_api.h"
+typedef struct PJ
+{
+    projPJ pj_from;
+    projPJ pj_to;
+} PJ;
 #else
+#include "proj.h"
+#endif
+
+#if POSTGIS_PROJ_VERSION < 49
 /* Use the old (pre-2.2) geodesic functions */
-#define PROJ_GEODESIC 0
+#undef PROJ_GEODESIC
+#else
+/* Enable new geodesic functions API */
+#define PROJ_GEODESIC
 #endif
 
 /**
@@ -83,13 +93,13 @@ const char* lwgeom_version(void);
 /**
 * LWTYPE numbers, used internally by PostGIS
 */
-#define        POINTTYPE                1
-#define        LINETYPE                 2
-#define        POLYGONTYPE              3
-#define        MULTIPOINTTYPE           4
-#define        MULTILINETYPE            5
-#define        MULTIPOLYGONTYPE         6
-#define        COLLECTIONTYPE           7
+#define POINTTYPE                1
+#define LINETYPE                 2
+#define POLYGONTYPE              3
+#define MULTIPOINTTYPE           4
+#define MULTILINETYPE            5
+#define MULTIPOLYGONTYPE         6
+#define COLLECTIONTYPE           7
 #define CIRCSTRINGTYPE           8
 #define COMPOUNDTYPE             9
 #define CURVEPOLYTYPE           10
@@ -271,16 +281,16 @@ extern lwinterrupt_callback *lwgeom_register_interrupt_callback(lwinterrupt_call
 /******************************************************************/
 
 typedef struct {
-       double afac, bfac, cfac, dfac, efac, ffac, gfac, hfac, ifac, xoff, yoff, zoff;
+    double afac, bfac, cfac, dfac, efac, ffac, gfac, hfac, ifac, xoff, yoff, zoff;
 } AFFINE;
 
 /******************************************************************/
 
 typedef struct
 {
-       double xmin, ymin, zmin;
-       double xmax, ymax, zmax;
-       int32_t srid;
+    double xmin, ymin, zmin;
+    double xmax, ymax, zmax;
+    int32_t srid;
 }
 BOX3D;
 
@@ -292,15 +302,15 @@ BOX3D;
 */
 typedef struct
 {
-       uint8_t flags;
-       double xmin;
-       double xmax;
-       double ymin;
-       double ymax;
-       double zmin;
-       double zmax;
-       double mmin;
-       double mmax;
+    uint8_t flags;
+    double xmin;
+    double xmax;
+    double ymin;
+    double ymax;
+    double zmin;
+    double zmax;
+    double mmin;
+    double mmax;
 } GBOX;
 
 
@@ -314,13 +324,13 @@ typedef struct
 */
 typedef struct
 {
-       double  a;      /* semimajor axis */
-       double  b;      /* semiminor axis b = (a - fa) */
-       double  f;      /* flattening f = (a-b)/a */
-       double  e;      /* eccentricity (first) */
-       double  e_sq;   /* eccentricity squared (first) e_sq = (a*a-b*b)/(a*a) */
-       double  radius;  /* spherical average radius = (2*a+b)/3 */
-       char    name[20];  /* name of ellipse */
+    double  a;  /* semimajor axis */
+    double  b;  /* semiminor axis b = (a - fa) */
+    double  f;  /* flattening f = (a-b)/a */
+    double  e;  /* eccentricity (first) */
+    double  e_sq;   /* eccentricity squared (first) e_sq = (a*a-b*b)/(a*a) */
+    double  radius;  /* spherical average radius = (2*a+b)/3 */
+    char    name[20];  /* name of ellipse */
 }
 SPHEROID;
 
@@ -329,31 +339,31 @@ SPHEROID;
 */
 typedef struct
 {
-       double x, y;
+    double x, y;
 }
 POINT2D;
 
 typedef struct
 {
-       double x, y, z;
+    double x, y, z;
 }
 POINT3DZ;
 
 typedef struct
 {
-       double x, y, z;
+    double x, y, z;
 }
 POINT3D;
 
 typedef struct
 {
-       double x, y, m;
+    double x, y, m;
 }
 POINT3DM;
 
 typedef struct
 {
-       double x, y, z, m;
+    double x, y, z, m;
 }
 POINT4D;
 
@@ -366,14 +376,14 @@ POINT4D;
 */
 typedef struct
 {
-       /* Array of POINT 2D, 3D or 4D, possibly misaligned. */
-       uint8_t *serialized_pointlist;
+    /* Array of POINT 2D, 3D or 4D, possibly misaligned. */
+    uint8_t *serialized_pointlist;
 
-       /* Use FLAGS_* macros to handle */
-       uint8_t  flags;
+    /* Use FLAGS_* macros to handle */
+    uint8_t  flags;
 
-       uint32_t npoints;   /* how many points we are currently storing */
-       uint32_t maxpoints; /* how many points we have space for in serialized_pointlist */
+    uint32_t npoints;   /* how many points we are currently storing */
+    uint32_t maxpoints; /* how many points we have space for in serialized_pointlist */
 }
 POINTARRAY;
 
@@ -382,10 +392,10 @@ POINTARRAY;
 */
 typedef struct
 {
-       uint32_t size; /* For PgSQL use only, use VAR* macros to manipulate. */
-       uint8_t srid[3]; /* 24 bits of SRID */
-       uint8_t flags; /* HasZ, HasM, HasBBox, IsGeodetic, IsReadOnly */
-       uint8_t data[1]; /* See gserialized.txt */
+    uint32_t size; /* For PgSQL use only, use VAR* macros to manipulate. */
+    uint8_t srid[3]; /* 24 bits of SRID */
+    uint8_t flags; /* HasZ, HasM, HasBBox, IsGeodetic, IsReadOnly */
+    uint8_t data[1]; /* See gserialized.txt */
 } GSERIALIZED;
 
 
@@ -397,198 +407,198 @@ typedef struct
 */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       void *data;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    void *data;
 }
 LWGEOM;
 
 /* POINTYPE */
 typedef struct
 {
-       uint8_t type; /* POINTTYPE */
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       POINTARRAY *point;  /* hide 2d/3d (this will be an array of 1 point) */
+    uint8_t type; /* POINTTYPE */
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    POINTARRAY *point;  /* hide 2d/3d (this will be an array of 1 point) */
 }
 LWPOINT; /* "light-weight point" */
 
 /* LINETYPE */
 typedef struct
 {
-       uint8_t type; /* LINETYPE */
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       POINTARRAY *points; /* array of POINT3D */
+    uint8_t type; /* LINETYPE */
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    POINTARRAY *points; /* array of POINT3D */
 }
 LWLINE; /* "light-weight line" */
 
 /* TRIANGLE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       POINTARRAY *points;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    POINTARRAY *points;
 }
 LWTRIANGLE;
 
 /* CIRCSTRINGTYPE */
 typedef struct
 {
-       uint8_t type; /* CIRCSTRINGTYPE */
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       POINTARRAY *points; /* array of POINT(3D/3DM) */
+    uint8_t type; /* CIRCSTRINGTYPE */
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    POINTARRAY *points; /* array of POINT(3D/3DM) */
 }
 LWCIRCSTRING; /* "light-weight circularstring" */
 
 /* POLYGONTYPE */
 typedef struct
 {
-       uint8_t type; /* POLYGONTYPE */
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t nrings;   /* how many rings we are currently storing */
-       uint32_t maxrings; /* how many rings we have space for in **rings */
-       POINTARRAY **rings; /* list of rings (list of points) */
+    uint8_t type; /* POLYGONTYPE */
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t nrings;   /* how many rings we are currently storing */
+    uint32_t maxrings; /* how many rings we have space for in **rings */
+    POINTARRAY **rings; /* list of rings (list of points) */
 }
 LWPOLY; /* "light-weight polygon" */
 
 /* MULTIPOINTTYPE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWPOINT **geoms;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWPOINT **geoms;
 }
 LWMPOINT;
 
 /* MULTILINETYPE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWLINE **geoms;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWLINE **geoms;
 }
 LWMLINE;
 
 /* MULTIPOLYGONTYPE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWPOLY **geoms;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWPOLY **geoms;
 }
 LWMPOLY;
 
 /* COLLECTIONTYPE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWGEOM **geoms;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWGEOM **geoms;
 }
 LWCOLLECTION;
 
 /* COMPOUNDTYPE */
 typedef struct
 {
-       uint8_t type; /* COMPOUNDTYPE */
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWGEOM **geoms;
+    uint8_t type; /* COMPOUNDTYPE */
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWGEOM **geoms;
 }
 LWCOMPOUND; /* "light-weight compound line" */
 
 /* CURVEPOLYTYPE */
 typedef struct
 {
-       uint8_t type; /* CURVEPOLYTYPE */
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t nrings;    /* how many rings we are currently storing */
-       uint32_t maxrings;  /* how many rings we have space for in **rings */
-       LWGEOM **rings; /* list of rings (list of points) */
+    uint8_t type; /* CURVEPOLYTYPE */
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t nrings;    /* how many rings we are currently storing */
+    uint32_t maxrings;  /* how many rings we have space for in **rings */
+    LWGEOM **rings; /* list of rings (list of points) */
 }
 LWCURVEPOLY; /* "light-weight polygon" */
 
 /* MULTICURVE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWGEOM **geoms;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWGEOM **geoms;
 }
 LWMCURVE;
 
 /* MULTISURFACETYPE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWGEOM **geoms;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWGEOM **geoms;
 }
 LWMSURFACE;
 
 /* POLYHEDRALSURFACETYPE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWPOLY **geoms;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWPOLY **geoms;
 }
 LWPSURFACE;
 
 /* TINTYPE */
 typedef struct
 {
-       uint8_t type;
-       uint8_t flags;
-       GBOX *bbox;
-       int32_t srid;
-       uint32_t ngeoms;   /* how many geometries we are currently storing */
-       uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
-       LWTRIANGLE **geoms;
+    uint8_t type;
+    uint8_t flags;
+    GBOX *bbox;
+    int32_t srid;
+    uint32_t ngeoms;   /* how many geometries we are currently storing */
+    uint32_t maxgeoms; /* how many geometries we have space for in **geoms */
+    LWTRIANGLE **geoms;
 }
 LWTIN;
 
@@ -929,7 +939,7 @@ extern POINTARRAY *ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdi
 
 /**
  * @brief Remove a point from a pointarray.
- *     @param which -  is the offset (starting at 0)
+ *  @param which -  is the offset (starting at 0)
  * @return #POINTARRAY is newly allocated
  */
 extern POINTARRAY *ptarray_removePoint(POINTARRAY *pa, uint32_t where);
@@ -1615,8 +1625,8 @@ extern double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s);
 extern int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2);
 
 typedef struct {
-       POINT2D* center;
-       double radius;
+    POINT2D* center;
+    double radius;
 } LWBOUNDINGCIRCLE;
 
 extern void lwboundingcircle_destroy(LWBOUNDINGCIRCLE* c);
@@ -1971,7 +1981,7 @@ extern int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox);
 #define LW_PARSER_CHECK_ZCLOSURE   8
 
 #define LW_PARSER_CHECK_NONE   0
-#define LW_PARSER_CHECK_ALL    (LW_PARSER_CHECK_MINPOINTS | LW_PARSER_CHECK_ODD | LW_PARSER_CHECK_CLOSURE)
+#define LW_PARSER_CHECK_ALL (LW_PARSER_CHECK_MINPOINTS | LW_PARSER_CHECK_ODD | LW_PARSER_CHECK_CLOSURE)
 
 /**
  * Parser result structure: returns the result of attempting to convert
@@ -1979,14 +1989,14 @@ extern int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox);
  */
 typedef struct struct_lwgeom_parser_result
 {
-       const char *wkinput;        /* Copy of pointer to input WKT/WKB */
-       uint8_t *serialized_lwgeom; /* Pointer to serialized LWGEOM */
-       size_t size;                /* Size of serialized LWGEOM in bytes */
-       LWGEOM *geom;               /* Pointer to LWGEOM struct */
-       const char *message;        /* Error/warning message */
-       int errcode;                /* Error/warning number */
-       int errlocation;            /* Location of error */
-       int parser_check_flags;     /* Bitmask of validity checks run during this parse */
+    const char *wkinput;        /* Copy of pointer to input WKT/WKB */
+    uint8_t *serialized_lwgeom; /* Pointer to serialized LWGEOM */
+    size_t size;                /* Size of serialized LWGEOM in bytes */
+    LWGEOM *geom;               /* Pointer to LWGEOM struct */
+    const char *message;        /* Error/warning message */
+    int errcode;                /* Error/warning number */
+    int errlocation;            /* Location of error */
+    int parser_check_flags;     /* Bitmask of validity checks run during this parse */
 }
 LWGEOM_PARSER_RESULT;
 
@@ -2011,20 +2021,20 @@ LWGEOM_PARSER_RESULT;
  */
 typedef struct struct_lwgeom_unparser_result
 {
-       uint8_t *serialized_lwgeom;     /* Copy of pointer to input serialized LWGEOM */
-       char *wkoutput;                 /* Pointer to WKT or WKB output */
-       size_t size;                    /* Size of serialized LWGEOM in bytes */
-       const char *message;            /* Error/warning message */
-       int errlocation;                /* Location of error */
+    uint8_t *serialized_lwgeom; /* Copy of pointer to input serialized LWGEOM */
+    char *wkoutput;         /* Pointer to WKT or WKB output */
+    size_t size;            /* Size of serialized LWGEOM in bytes */
+    const char *message;        /* Error/warning message */
+    int errlocation;        /* Location of error */
 }
 LWGEOM_UNPARSER_RESULT;
 
 /*
  * Unparser error messages (these must match the message array in lwgunparse.c)
  */
-#define UNPARSER_ERROR_MOREPOINTS      1
-#define UNPARSER_ERROR_ODDPOINTS       2
-#define UNPARSER_ERROR_UNCLOSED                3
+#define UNPARSER_ERROR_MOREPOINTS   1
+#define UNPARSER_ERROR_ODDPOINTS    2
+#define UNPARSER_ERROR_UNCLOSED     3
 
 
 /*
@@ -2168,27 +2178,27 @@ LWGEOM *lwgeom_unstroke(const LWGEOM *geom);
  * lwcurve_linearize
  */
 typedef enum {
-       /**
-        * Tolerance expresses the number of segments to use
-        * for each quarter of circle (quadrant). Must be
-        * an integer.
-        */
-       LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD = 0,
-       /**
-        * Tolerance expresses the maximum distance between
-        * an arbitrary point on the curve and the closest
-        * point to it on the resulting approximation, in
-        * cartesian units.
-        */
-       LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION = 1,
-       /**
-        * Tolerance expresses the maximum angle between
-        * the radii generating approximation line vertices,
-        * given in radiuses. A value of 1 would result
-        * in an approximation of a semicircle composed by
-        * 180 segments
-        */
-       LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE = 2
+    /**
+     * Tolerance expresses the number of segments to use
+     * for each quarter of circle (quadrant). Must be
+     * an integer.
+     */
+    LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD = 0,
+    /**
+     * Tolerance expresses the maximum distance between
+     * an arbitrary point on the curve and the closest
+     * point to it on the resulting approximation, in
+     * cartesian units.
+     */
+    LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION = 1,
+    /**
+     * Tolerance expresses the maximum angle between
+     * the radii generating approximation line vertices,
+     * given in radiuses. A value of 1 would result
+     * in an approximation of a semicircle composed by
+     * 180 segments
+     */
+    LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE = 2
 } LW_LINEARIZE_TOLERANCE_TYPE;
 
 typedef enum {
@@ -2197,7 +2207,7 @@ typedef enum {
    * vertices would be the same no matter the order
    * of the points defining the input curve.
    */
-       LW_LINEARIZE_FLAG_SYMMETRIC = 1 << 0,
+    LW_LINEARIZE_FLAG_SYMMETRIC = 1 << 0,
 
   /**
    * Retain angle instructs the engine to try its best
@@ -2217,7 +2227,7 @@ typedef enum {
    * instead be smaller (shorter) than the others.
    *
    */
-       LW_LINEARIZE_FLAG_RETAIN_ANGLE = 1 << 1
+    LW_LINEARIZE_FLAG_RETAIN_ANGLE = 1 << 1
 } LW_LINEARIZE_FLAGS;
 
 /**
@@ -2296,6 +2306,9 @@ int lwgeom_is_simple(const LWGEOM *lwgeom);
  * PROJ4-dependent extra functions on LWGEOM
  ******************************************************************************/
 
+int lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr);
+
+#if POSTGIS_PROJ_VERSION < 60
 /**
  * Get a projection from a string representation
  *
@@ -2303,14 +2316,14 @@ int lwgeom_is_simple(const LWGEOM *lwgeom);
  */
 projPJ lwproj_from_string(const char* txt);
 
+#endif
 /**
  * Transform (reproject) a geometry in-place.
  * @param geom the geometry to transform
- * @param inpj the input (or current, or source) projection
- * @param outpj the output (or destination) projection
+ * @param PJ the input and output
  */
-int lwgeom_transform(LWGEOM *geom, projPJ inpj, projPJ outpj);
-int ptarray_transform(POINTARRAY *pa, projPJ inpj, projPJ outpj);
+int lwgeom_transform(LWGEOM *geom, PJ* pj);
+int ptarray_transform(POINTARRAY *pa, PJ* pj);
 
 
 /*******************************************************************************
index 57e8ca73d8a8f0e0f5cadb21aec6b1da0d96e3cb..3819fcddcef54f0224997e717a74825bbfd4ab53 100644 (file)
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
+#include <float.h>
+#include <math.h>
 
 #if HAVE_IEEEFP_H
 #include <ieeefp.h>
 #endif
 
-#include <float.h>
-
 #include "liblwgeom.h"
 
 /**
@@ -348,11 +349,6 @@ char lwtriangle_same(const LWTRIANGLE *p1, const LWTRIANGLE *p2);
 char lwcollection_same(const LWCOLLECTION *p1, const LWCOLLECTION *p2);
 char lwcircstring_same(const LWCIRCSTRING *p1, const LWCIRCSTRING *p2);
 
-/*
-* Transform
-*/
-int point4d_transform(POINT4D *pt, projPJ srcpj, projPJ dstpj);
-
 /*
 * Shift
 */
@@ -488,5 +484,6 @@ int ptarray_npoints_in_rect(const POINTARRAY *pa, const GBOX *gbox);
 int gbox_contains_point2d(const GBOX *g, const POINT2D *p);
 int lwpoly_contains_point(const LWPOLY *poly, const POINT2D *pt);
 POINT4D* lwmpoint_extract_points_4d(const LWMPOINT* g, uint32_t* npoints, int* input_empty);
+char* lwstrdup(const char* a);
 
 #endif /* _LIBLWGEOM_INTERNAL_H */
index 5638bc167ecd31c8c7e165a78047a3ca80e98527..8fbd35249500e10843c52e40d5ca084af392b86d 100644 (file)
@@ -44,12 +44,53 @@ to_dec(POINT4D *pt)
        pt->y *= 180.0/M_PI;
 }
 
+/***************************************************************************/
+
+#if POSTGIS_PROJ_VERSION < 60
+
+
+static int
+point4d_transform(POINT4D *pt, PJ* pj)
+{
+       POINT3D orig_pt = {pt->x, pt->y, pt->z}; /* Copy for error report*/
+
+       if (pj_is_latlong(pj->pj_from)) to_rad(pt) ;
+
+       LWDEBUGF(4, "transforming POINT(%f %f) from '%s' to '%s'",
+                orig_pt.x, orig_pt.y, pj_get_def(pj->pj_from,0), pj_get_def(pj->pj_to,0));
+
+       if (pj_transform(pj->pj_from, pj->pj_to, 1, 0, &(pt->x), &(pt->y), &(pt->z)) != 0)
+       {
+               int pj_errno_val = *pj_get_errno_ref();
+               if (pj_errno_val == -38)
+               {
+                       lwnotice("PostGIS was unable to transform the point because either no grid"
+                                " shift files were found, or the point does not lie within the "
+                                "range for which the grid shift is defined. Refer to the "
+                                "ST_Transform() section of the PostGIS manual for details on how "
+                                "to configure PostGIS to alter this behaviour.");
+                       lwerror("transform: couldn't project point (%g %g %g): %s (%d)",
+                               orig_pt.x, orig_pt.y, orig_pt.z,
+                               pj_strerrno(pj_errno_val), pj_errno_val);
+               }
+               else
+               {
+                       lwerror("transform: %s (%d)",
+                               pj_strerrno(pj_errno_val), pj_errno_val);
+               }
+               return LW_FAILURE;
+       }
+
+       if (pj_is_latlong(pj->pj_to)) to_dec(pt);
+       return LW_SUCCESS;
+}
+
 /**
  * Transform given POINTARRAY
  * from inpj projection to outpj projection
  */
 int
-ptarray_transform(POINTARRAY *pa, projPJ inpj, projPJ outpj)
+ptarray_transform(POINTARRAY *pa, PJ* pj)
 {
        uint32_t i;
        POINT4D p;
@@ -57,20 +98,51 @@ ptarray_transform(POINTARRAY *pa, projPJ inpj, projPJ outpj)
        for ( i = 0; i < pa->npoints; i++ )
        {
                getPoint4d_p(pa, i, &p);
-               if ( ! point4d_transform(&p, inpj, outpj) ) return LW_FAILURE;
+               if ( ! point4d_transform(&p, pj) ) return LW_FAILURE;
                ptarray_set_point4d(pa, i, &p);
        }
 
        return LW_SUCCESS;
 }
 
+int
+lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr)
+{
+       char *pj_errstr;
+       int rv;
+       PJ pj;
+
+       pj.pj_from = lwproj_from_string(instr);
+       if (!pj.pj_from)
+       {
+               pj_errstr = pj_strerrno(*pj_get_errno_ref());
+               if (!pj_errstr) pj_errstr = "";
+               lwerror("could not parse proj string '%s'", instr);
+               return LW_FAILURE;
+       }
+
+       pj.pj_to = lwproj_from_string(outstr);
+       if (!pj.pj_to)
+       {
+               pj_free(pj.pj_from);
+               pj_errstr = pj_strerrno(*pj_get_errno_ref());
+               if (!pj_errstr) pj_errstr = "";
+               lwerror("could not parse proj string '%s'", outstr);
+               return LW_FAILURE;
+       }
+
+       rv = lwgeom_transform(geom, &pj);
+       pj_free(pj.pj_from);
+       pj_free(pj.pj_to);
+       return rv;
+}
 
 /**
- * Transform given SERIALIZED geometry
+ * Transform given LWGEOM geometry
  * from inpj projection to outpj projection
  */
 int
-lwgeom_transform(LWGEOM *geom, projPJ inpj, projPJ outpj)
+lwgeom_transform(LWGEOM *geom, PJ* pj)
 {
        uint32_t i;
 
@@ -86,7 +158,7 @@ lwgeom_transform(LWGEOM *geom, projPJ inpj, projPJ outpj)
                case TRIANGLETYPE:
                {
                        LWLINE *g = (LWLINE*)geom;
-                       if ( ! ptarray_transform(g->points, inpj, outpj) ) return LW_FAILURE;
+                       if ( ! ptarray_transform(g->points, pj) ) return LW_FAILURE;
                        break;
                }
                case POLYGONTYPE:
@@ -94,7 +166,7 @@ lwgeom_transform(LWGEOM *geom, projPJ inpj, projPJ outpj)
                        LWPOLY *g = (LWPOLY*)geom;
                        for ( i = 0; i < g->nrings; i++ )
                        {
-                               if ( ! ptarray_transform(g->rings[i], inpj, outpj) ) return LW_FAILURE;
+                               if ( ! ptarray_transform(g->rings[i], pj) ) return LW_FAILURE;
                        }
                        break;
                }
@@ -112,7 +184,7 @@ lwgeom_transform(LWGEOM *geom, projPJ inpj, projPJ outpj)
                        LWCOLLECTION *g = (LWCOLLECTION*)geom;
                        for ( i = 0; i < g->ngeoms; i++ )
                        {
-                               if ( ! lwgeom_transform(g->geoms[i], inpj, outpj) ) return LW_FAILURE;
+                               if ( ! lwgeom_transform(g->geoms[i], pj) ) return LW_FAILURE;
                        }
                        break;
                }
@@ -126,49 +198,287 @@ lwgeom_transform(LWGEOM *geom, projPJ inpj, projPJ outpj)
        return LW_SUCCESS;
 }
 
+projPJ
+lwproj_from_string(const char *str1)
+{
+       if (!str1 || str1[0] == '\0')
+       {
+               return NULL;
+       }
+       return pj_init_plus(str1);
+}
+
+/***************************************************************************/
+
+#else /* POSTGIS_PROJ_VERION >= 60 */
+
 int
-point4d_transform(POINT4D *pt, projPJ srcpj, projPJ dstpj)
+lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr)
 {
-       POINT3D orig_pt = {pt->x, pt->y, pt->z}; /* Copy for error report*/
+       PJ *pj = proj_create_crs_to_crs(NULL, instr, outstr, NULL);
+       if (!pj)
+       {
+               PJ *pj_in = proj_create(NULL, instr);
+               PJ *pj_out = proj_create(NULL, outstr);
+               if (!pj_in)
+               {
+                       lwerror("could not parse proj string '%s'", instr);
+               }
+               if (!pj_out)
+               {
+                       proj_destroy(pj_in);
+                       lwerror("could not parse proj string '%s'", outstr);
+               }
+               return LW_FAILURE;
+       }
 
-       if (pj_is_latlong(srcpj)) to_rad(pt) ;
+       return lwgeom_transform(geom, pj);
+}
 
-       LWDEBUGF(4, "transforming POINT(%f %f) from '%s' to '%s'",
-                orig_pt.x, orig_pt.y, pj_get_def(srcpj,0), pj_get_def(dstpj,0));
+int
+lwgeom_transform(LWGEOM* geom, PJ* pj)
+{
+       uint32_t i;
+
+       /* No points to transform in an empty! */
+       if (lwgeom_is_empty(geom))
+               return LW_SUCCESS;
 
-       if (pj_transform(srcpj, dstpj, 1, 0, &(pt->x), &(pt->y), &(pt->z)) != 0)
+       switch(geom->type)
        {
-               int pj_errno_val = *pj_get_errno_ref();
-               if (pj_errno_val == -38)
+               case POINTTYPE:
+               case LINETYPE:
+               case CIRCSTRINGTYPE:
+               case TRIANGLETYPE:
                {
-                       lwnotice("PostGIS was unable to transform the point because either no grid"
-                                " shift files were found, or the point does not lie within the "
-                                "range for which the grid shift is defined. Refer to the "
-                                "ST_Transform() section of the PostGIS manual for details on how "
-                                "to configure PostGIS to alter this behaviour.");
-                       lwerror("transform: couldn't project point (%g %g %g): %s (%d)",
-                               orig_pt.x, orig_pt.y, orig_pt.z,
-                               pj_strerrno(pj_errno_val), pj_errno_val);
+                       LWLINE *g = (LWLINE*)geom;
+                       if (!ptarray_transform(g->points, pj))
+                               return LW_FAILURE;
+                       break;
                }
-               else
+               case POLYGONTYPE:
                {
-                       lwerror("transform: couldn't project point (%g %g %g): %s (%d)",
-                               orig_pt.x, orig_pt.y, orig_pt.z,
-                               pj_strerrno(pj_errno_val), pj_errno_val);
+                       LWPOLY *g = (LWPOLY*)geom;
+                       for (i = 0; i < g->nrings; i++)
+                       {
+                               if (!ptarray_transform(g->rings[i], pj))
+                                       return LW_FAILURE;
+                       }
+                       break;
+               }
+               case MULTIPOINTTYPE:
+               case MULTILINETYPE:
+               case MULTIPOLYGONTYPE:
+               case COLLECTIONTYPE:
+               case COMPOUNDTYPE:
+               case CURVEPOLYTYPE:
+               case MULTICURVETYPE:
+               case MULTISURFACETYPE:
+               case POLYHEDRALSURFACETYPE:
+               case TINTYPE:
+               {
+                       LWCOLLECTION *g = (LWCOLLECTION*)geom;
+                       for (i = 0; i < g->ngeoms; i++)
+                       {
+                               if (!lwgeom_transform(g->geoms[i], pj))
+                                       return LW_FAILURE;
+                       }
+                       break;
+               }
+               default:
+               {
+                       lwerror("lwgeom_transform: Cannot handle type '%s'",
+                                 lwtype_name(geom->type));
+                       return LW_FAILURE;
+               }
+       }
+       return LW_SUCCESS;
+}
+
+static int
+proj_crs_is_swapped(const PJ* pj_crs)
+{
+       PJ *pj_cs;
+       int rv = LW_FALSE;
+
+       if (proj_get_type(pj_crs) == PJ_TYPE_COMPOUND_CRS)
+       {
+               PJ *pj_horiz_crs = proj_crs_get_sub_crs(NULL, pj_crs, 0);
+               pj_cs = proj_crs_get_coordinate_system(NULL, pj_horiz_crs);
+               proj_destroy(pj_horiz_crs);
+       }
+       else if (proj_get_type(pj_crs) == PJ_TYPE_BOUND_CRS)
+       {
+               PJ *pj_bound_crs = proj_get_source_crs(NULL, pj_crs);
+               pj_cs = proj_crs_get_coordinate_system(NULL, pj_bound_crs);
+               proj_destroy(pj_bound_crs);
+       }
+       else
+       {
+               pj_cs = proj_crs_get_coordinate_system(NULL, pj_crs);
+       }
+       int axis_count = proj_cs_get_axis_count(NULL, pj_cs);
+       if (axis_count > 0)
+       {
+               const char *out_name, *out_abbrev, *out_direction;
+               double out_unit_conv_factor;
+               const char *out_unit_name, *out_unit_auth_name, *out_unit_code;
+               /* Read only first axis, see if it is degrees / north */
+               proj_cs_get_axis_info(NULL, pj_cs, 0,
+                       &out_name,
+                       &out_abbrev,
+                       &out_direction,
+                       &out_unit_conv_factor,
+                       &out_unit_name,
+                       &out_unit_auth_name,
+                       &out_unit_code
+                       );
+               rv = (strcasecmp(out_direction, "north") == 0);
+       }
+       proj_destroy(pj_cs);
+       return rv;
+}
+
+
+int
+ptarray_transform(POINTARRAY* pa, PJ* pj)
+{
+       uint32_t i;
+       POINT4D p;
+       size_t n_converted;
+       size_t n_points = pa->npoints;
+       size_t point_size = ptarray_point_size(pa);
+       int has_z = ptarray_has_z(pa);
+       double *pa_double = (double*)(pa->serialized_pointlist);
+       int input_swapped, output_swapped;
+
+       /* XXXX TODO check that the PJ has decimal degrees as units in input/output */
+
+       PJ* pj_source_crs = proj_get_source_crs(NULL, pj);
+       PJ* pj_target_crs = proj_get_target_crs(NULL, pj);
+
+       if (!(pj_source_crs && pj_source_crs))
+       {
+               lwerror("ptarray_transform: unable to access source and target crs");
+               return LW_FAILURE;
+       }
+
+       input_swapped = proj_crs_is_swapped(pj_source_crs);
+       output_swapped = proj_crs_is_swapped(pj_target_crs);
+       proj_destroy(pj_source_crs);
+       proj_destroy(pj_target_crs);
+
+       /* Convert to radians if necessary */
+       if (proj_angular_input(pj, PJ_FWD))
+       {
+               for (i = 0; i < pa->npoints; i++)
+               {
+                       getPoint4d_p(pa, i, &p);
+                       to_rad(&p);
                }
+       }
+
+       if (input_swapped)
+               ptarray_swap_ordinates(pa, LWORD_X, LWORD_Y);
+
+       /*
+       * size_t proj_trans_generic(PJ *P, PJ_DIRECTION direction,
+       * double *x, size_t sx, size_t nx,
+       * double *y, size_t sy, size_t ny,
+       * double *z, size_t sz, size_t nz,
+       * double *t, size_t st, size_t nt)
+       */
+
+       n_converted = proj_trans_generic(
+               pj, PJ_FWD,
+               pa_double,     point_size, n_points, /* X */
+               pa_double + 1, point_size, n_points, /* Y */
+               has_z ? pa_double + 2 : NULL,
+               has_z ? point_size : 0,
+               has_z ? n_points : 0, /* Z */
+               NULL, 0, 0 /* M */
+               );
+
+       if (n_converted != n_points)
+       {
+               lwerror("ptarray_transform: converted (%d) != input (%d)",
+                       n_converted, n_points);
+               return LW_FAILURE;
+       }
+
+       int pj_errno_val = proj_errno(pj);
+       if (pj_errno_val)
+       {
+               lwerror("transform: %s (%d)",
+                       proj_errno_string(pj_errno_val), pj_errno_val);
                return LW_FAILURE;
        }
 
-       if (pj_is_latlong(dstpj)) to_dec(pt);
+       if (output_swapped)
+               ptarray_swap_ordinates(pa, LWORD_X, LWORD_Y);
+
+       /* Convert radians to degrees if necessary */
+       if (proj_angular_output(pj, PJ_FWD))
+       {
+               for (i = 0; i < pa->npoints; i++)
+               {
+                       getPoint4d_p(pa, i, &p);
+                       to_dec(&p);
+               }
+       }
+
        return LW_SUCCESS;
 }
 
-projPJ
-lwproj_from_string(const char *str1)
+#if 0
+int
+point4d_transform(POINT4D *pt, PJ* pj)
 {
-       if (!str1 || str1[0] == '\0')
+       POINT4D pt_src = *pt; /* Copy for error report*/
+       PJ_COORD pj_coord_src, pj_coord_dst;
+       int input_swapped = proj_crs_is_swapped(proj_get_source_crs(NULL, pj));
+       int output_swapped = proj_crs_is_swapped(proj_get_target_crs(NULL, pj));
+
+       if (proj_angular_input(pj, PJ_FWD))
+               to_rad(pt);
+
+       LWDEBUGF(4, "transforming POINT(%f %f) using '%s'",
+               pt_src.x, pt_src.y, (proj_pj_info(pj)).definition);
+
+       if (input_swapped)
+               pj_coord_src = proj_coord(pt->y, pt->x, pt->z, pt->m);
+       else
+               pj_coord_src = proj_coord(pt->x, pt->y, pt->z, pt->m);
+
+       pj_coord_dst = proj_trans(pj, PJ_FWD, pj_coord_src);
+
+       int pj_errno_val = proj_errno(pj);
+       if (pj_errno_val)
        {
-               return NULL;
+               lwerror("point4d_transform: couldn't project point (%g %g %g): %s (%d)",
+                       pt_src.x, pt_src.y, pt_src.z,
+                       proj_errno_string(pj_errno_val), pj_errno_val);
+               return LW_FAILURE;
        }
-       return pj_init_plus(str1);
+
+       if (output_swapped)
+       {
+               pt->x = pj_coord_dst.xyzt.x;
+               pt->y = pj_coord_dst.xyzt.y;
+       }
+       else
+       {
+               pt->x = pj_coord_dst.xyzt.x;
+               pt->y = pj_coord_dst.xyzt.y;
+       }
+       pt->z = pj_coord_dst.xyzt.z;
+       pt->m = pt_src.m;
+
+       if (proj_angular_output(pj, PJ_FWD))
+               to_dec(pt);
+       return LW_SUCCESS;
 }
+#endif /* point4d_transform */
+
+
+#endif
index 9d486f576188447f34438661a494416ac84dfab3..7212314a6a2ebfa93a2bdd2b5fab11a9ead9358f 100644 (file)
@@ -25,6 +25,8 @@
 
 #include <assert.h>
 #include <string.h>
+#include <math.h>
+
 #include "liblwgeom.h"
 #include "../postgis_config.h"
 
index dcd44e7f995b91e793094151f51d339f86f6a025..e2b9506eb50802e0e812eb820d8070a719dc5c8f 100644 (file)
@@ -28,8 +28,8 @@
 #include "lwgeodetic.h"
 #include "lwgeom_log.h"
 
-/* GeographicLib */
-#if PROJ_GEODESIC
+/* In proj4.9, GeographicLib is in special header */
+#ifdef PROJ_GEODESIC
 #include <geodesic.h>
 #endif
 
@@ -45,7 +45,7 @@ void spheroid_init(SPHEROID *s, double a, double b)
        s->radius = (2.0 * a + b ) / 3.0;
 }
 
-#if ! PROJ_GEODESIC
+#ifndef PROJ_GEODESIC
 static double spheroid_mu2(double alpha, const SPHEROID *s)
 {
        double b2 = POW2(s->b);
@@ -64,7 +64,7 @@ static double spheroid_big_b(double u2)
 #endif /* ! PROJ_GEODESIC */
 
 
-#if PROJ_GEODESIC
+#ifdef PROJ_GEODESIC
 
 /**
 * Computes the shortest distance along the surface of the spheroid
@@ -165,7 +165,7 @@ static double ptarray_area_spheroid(const POINTARRAY *pa, const SPHEROID *sphero
        return fabs(area);
 }
 
-/* Above use GeographicLib */
+/* Above use Proj GeographicLib */
 #else /* ! PROJ_GEODESIC */
 /* Below use pre-version 2.2 geodesic functions */
 
index e30b625daca3cd3c05d052d59f00b282f513217c..854a9e8079639ba3e1c308990029c028e5ae802d 100644 (file)
@@ -26,6 +26,7 @@
 #include "liblwgeom.h"
 #include "lwunionfind.h"
 #include <string.h>
+#include <stdlib.h>
 
 static int cmp_int(const void *a, const void *b);
 static int cmp_int_ptr(const void *a, const void *b);
index 52f1720075e083ad000468025e972a3cc1b4d8db..8d2d43f269787ab145b6aea5003e051066457361 100644 (file)
@@ -244,6 +244,15 @@ lwfree(void *mem)
        lwfree_var(mem);
 }
 
+char *
+lwstrdup(const char* a)
+{
+       size_t l = strlen(a)+1;
+       char *b = lwalloc(l);
+       strncpy(b, a, l);
+       return b;
+}
+
 /*
  * Returns a new string which contains a maximum of maxlength characters starting
  * from startpos and finishing at endpos (0-based indexing). If the string is
index 919060fcb450fd421ba478848e09770f72edb326..594d2d2ebfc913ff39594eefc85a5d2d89471256 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
 
 #include "../postgis_config.h"
 /*#define POSTGIS_DEBUG_LEVEL 4*/
@@ -378,8 +379,8 @@ ptarray_swap_ordinates(POINTARRAY *pa, LWORD o1, LWORD o2)
        double d, *dp1, *dp2;
        POINT4D p;
 
-  dp1 = ((double*)&p)+(unsigned)o1;
-  dp2 = ((double*)&p)+(unsigned)o2;
+       dp1 = ((double*)&p)+(unsigned)o1;
+       dp2 = ((double*)&p)+(unsigned)o2;
        for (i=0 ; i < pa->npoints ; i++)
        {
                getPoint4d_p(pa, i, &p);
index b03a4f1ac70c663d25cdf434a0901ec5bf17af9d..6e2213ed04fa7b847183d636198158db456748fa 100644 (file)
@@ -77,35 +77,36 @@ GetGenericCacheCollection(FunctionCallInfo fcinfo)
 
 
 /**
-* Get the Proj4 entry from the generic cache if one exists.
+* Get the Proj entry from the generic cache if one exists.
 * If it doesn't exist, make a new empty one and return it.
 */
-PROJ4PortalCache *
-GetPROJ4SRSCache(FunctionCallInfo fcinfo)
+PROJPortalCache *
+GetPROJSRSCache(FunctionCallInfo fcinfo)
 {
        GenericCacheCollection* generic_cache = GetGenericCacheCollection(fcinfo);
-       PROJ4PortalCache* cache = (PROJ4PortalCache*)(generic_cache->entry[PROJ_CACHE_ENTRY]);
+       PROJPortalCache* cache = (PROJPortalCache*)(generic_cache->entry[PROJ_CACHE_ENTRY]);
 
        if ( ! cache )
        {
                /* Allocate in the upper context */
-               cache = MemoryContextAlloc(FIContext(fcinfo), sizeof(PROJ4PortalCache));
+               cache = MemoryContextAlloc(FIContext(fcinfo), sizeof(PROJPortalCache));
 
                if (cache)
                {
                        int i;
 
-                       POSTGIS_DEBUGF(3, "Allocating PROJ4Cache for portal with transform() MemoryContext %p", FIContext(fcinfo));
+                       POSTGIS_DEBUGF(3, "Allocating PROJCache for portal with transform() MemoryContext %p", FIContext(fcinfo));
                        /* Put in any required defaults */
-                       for (i = 0; i < PROJ4_CACHE_ITEMS; i++)
+                       for (i = 0; i < PROJ_CACHE_ITEMS; i++)
                        {
-                               cache->PROJ4SRSCache[i].srid = SRID_UNKNOWN;
-                               cache->PROJ4SRSCache[i].projection = NULL;
-                               cache->PROJ4SRSCache[i].projection_mcxt = NULL;
+                               cache->PROJSRSCache[i].srid_from = SRID_UNKNOWN;
+                               cache->PROJSRSCache[i].srid_to = SRID_UNKNOWN;
+                               cache->PROJSRSCache[i].projection = NULL;
+                               cache->PROJSRSCache[i].projection_mcxt = NULL;
                        }
                        cache->type = PROJ_CACHE_ENTRY;
-                       cache->PROJ4SRSCacheCount = 0;
-                       cache->PROJ4SRSCacheContext = FIContext(fcinfo);
+                       cache->PROJSRSCacheCount = 0;
+                       cache->PROJSRSCacheContext = FIContext(fcinfo);
 
                        /* Store the pointer in GenericCache */
                        generic_cache->entry[PROJ_CACHE_ENTRY] = (GenericCache*)cache;
index 8de47948002df973dcda60f2ca9edb8302502e00..abb0d5df1e4f912f52361a675b23fa7a4f5decc7 100644 (file)
@@ -62,31 +62,32 @@ typedef struct {
 * fcinfo->flinfo->fn_extra to avoid collisions.
 */
 
-/* An entry in the PROJ4 SRS cache */
-typedef struct struct_PROJ4SRSCacheItem
+/* An entry in the PROJ SRS cache */
+typedef struct struct_PROJSRSCacheItem
 {
-       int srid;
-       projPJ projection;
+       int srid_from;
+       int srid_to;
+       PJ* projection;
        MemoryContext projection_mcxt;
 }
-PROJ4SRSCacheItem;
+PROJSRSCacheItem;
 
 /* PROJ 4 lookup transaction cache methods */
-#define PROJ4_CACHE_ITEMS      8
+#define PROJ_CACHE_ITEMS       8
 
 /*
 * The proj4 cache holds a fixed number of reprojection
 * entries. In normal usage we don't expect it to have
 * many entries, so we always linearly scan the list.
 */
-typedef struct struct_PROJ4PortalCache
+typedef struct struct_PROJPortalCache
 {
        int type;
-       PROJ4SRSCacheItem PROJ4SRSCache[PROJ4_CACHE_ITEMS];
-       int PROJ4SRSCacheCount;
-       MemoryContext PROJ4SRSCacheContext;
+       PROJSRSCacheItem PROJSRSCache[PROJ_CACHE_ITEMS];
+       int PROJSRSCacheCount;
+       MemoryContext PROJSRSCacheContext;
 }
-PROJ4PortalCache;
+PROJPortalCache;
 
 /**
 * Generic signature for functions to manage a geometry
@@ -103,7 +104,7 @@ typedef struct
 /*
 * Cache retrieval functions
 */
-PROJ4PortalCache *GetPROJ4SRSCache(FunctionCallInfo fcinfo);
+PROJPortalCache *GetPROJSRSCache(FunctionCallInfo fcinfo);
 GeomCache *GetGeomCache(FunctionCallInfo fcinfo,
                        const GeomCacheMethods *cache_methods,
                        const GSERIALIZED *g1,
index a0f46178e7e89b9156d91638d5e3b84574574dba..3962c72cdd88db6dec9c4a42bb6837ad3c422ee5 100644 (file)
 /**
 * Global variable to hold cached information about what
 * schema functions are installed in. Currently used by
-* SetSpatialRefSysSchema and GetProj4StringSPI
+* SetSpatialRefSysSchema and GetProjStringsSPI
 */
 static char *spatialRefSysSchema = NULL;
 
 
-
-/* Expose an internal Proj function */
-int pj_transform_nodatum(projPJ srcdefn, projPJ dstdefn, long point_count, int point_offset, double *x, double *y, double *z );
-
-
 /*
  * PROJ 4 backend hash table initial hash size
  * (since 16 is the default portal hash table size, and we would
  * typically have 2 entries per portal
  * then we shall use a default size of 32)
  */
-#define PROJ4_BACKEND_HASH_SIZE        32
+#define PROJ_BACKEND_HASH_SIZE 32
 
 
 /**
- * Backend projPJ hash table
+ * Backend PROJ hash table
  *
- * This hash table stores a key/value pair of MemoryContext/projPJ objects.
- * Whenever we create a projPJ object using pj_init(), we create a separate
+ * This hash table stores a key/value pair of MemoryContext/PJ objects.
+ * Whenever we create a PJ object using pj_init(), we create a separate
  * MemoryContext as a child context of the current executor context.
- * The MemoryContext/projPJ object is stored in this hash table so
- * that when PROJ4SRSCacheDelete() is called during query cleanup, we can
- * lookup the projPJ object based upon the MemoryContext parameter and hence
+ * The MemoryContext/PJ object is stored in this hash table so
+ * that when PROJSRSCacheDelete() is called during query cleanup, we can
+ * lookup the PJ object based upon the MemoryContext parameter and hence
  * pj_free() it.
  */
 static HTAB *PJHash = NULL;
 
+/**
+ * Utility structure to get many potential string representations
+ * from spatial_ref_sys query.
+ */
+typedef struct {
+       char* epsgtext;
+       char* srtext;
+       char* proj4text;
+} PjStrs;
+
+
 typedef struct struct_PJHashEntry
 {
        MemoryContext ProjectionContext;
-       projPJ projection;
+       PJ* projection;
 }
 PJHashEntry;
 
@@ -80,42 +86,92 @@ PJHashEntry;
 uint32 mcxt_ptr_hash(const void *key, Size keysize);
 
 static HTAB *CreatePJHash(void);
-static void AddPJHashEntry(MemoryContext mcxt, projPJ projection);
-static projPJ GetPJHashEntry(MemoryContext mcxt);
 static void DeletePJHashEntry(MemoryContext mcxt);
+static PJ* GetPJHashEntry(MemoryContext mcxt);
+static void AddPJHashEntry(MemoryContext mcxt, PJ* projection);
 
 /* Internal Cache API */
-/* static PROJ4PortalCache *GetPROJ4SRSCache(FunctionCallInfo fcinfo) ; */
-static bool IsInPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid);
-static projPJ GetProjectionFromPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid);
-static void AddToPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid, int other_srid);
-static void DeleteFromPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid);
+/* static PROJPortalCache *GetPROJSRSCache(FunctionCallInfo fcinfo) ; */
+static bool IsInPROJSRSCache(PROJPortalCache *PROJCache, int srid_from, int srid_to);
+static void AddToPROJSRSCache(PROJPortalCache *PROJCache, int srid_from, int srid_to);
+static void DeleteFromPROJSRSCache(PROJPortalCache *PROJCache, int srid_from, int srid_to);
+
 
 /* Search path for PROJ.4 library */
-static bool IsPROJ4LibPathSet = false;
-void SetPROJ4LibPath(void);
+static bool IsPROJLibPathSet = false;
+void SetPROJLibPath(void);
+
+
+/*
+* Given a function call context, figure out what namespace the
+* function is being called from, and copy that into a global
+* for use by GetProjStringsSPI
+*/
+static void
+SetSpatialRefSysSchema(FunctionCallInfo fcinfo)
+{
+       char *nsp_name;
+
+       /* Schema info is already cached, we're done here */
+       if (spatialRefSysSchema) return;
+
+       /* For some reason we have a hobbled fcinfo/flinfo */
+       if (!fcinfo || !fcinfo->flinfo) return;
+
+       nsp_name = get_namespace_name(get_func_namespace(fcinfo->flinfo->fn_oid));
+       /* early exit if we cannot lookup nsp_name, cf #4067 */
+       if (!nsp_name) return;
 
+       elog(DEBUG4, "%s located %s in namespace %s", __func__, get_func_name(fcinfo->flinfo->fn_oid), nsp_name);
+       spatialRefSysSchema = MemoryContextStrdup(CacheMemoryContext, nsp_name);
+       return;
+}
+
+static void
+PROJSRSDestroyPJ(PJ* pj)
+{
+#if POSTGIS_PROJ_VERSION < 60
+/* Ape the Proj 6+ API for versions < 6 */
+       if (pj->pj_from)
+               pj_free(pj->pj_from);
+       if (pj->pj_to)
+               pj_free(pj->pj_to);
+       free(pj);
+#else
+       proj_destroy(pj);
+#endif
+}
+
+#if 0
+static const char *
+PJErrStr()
+{
+       const char *pj_errstr = pj_strerrno(*pj_get_errno_ref());
+       if (!pj_errstr)
+               return "";
+       return pj_errstr;
+}
+#endif
 
 static void
 #if POSTGIS_PGSQL_VERSION < 96
-PROJ4SRSCacheDelete(MemoryContext context)
+PROJSRSCacheDelete(MemoryContext context)
 {
 #else
-PROJ4SRSCacheDelete(void *ptr)
+PROJSRSCacheDelete(void *ptr)
 {
        MemoryContext context = (MemoryContext)ptr;
 #endif
-       projPJ projection;
 
-       /* Lookup the projPJ pointer in the global hash table so we can free it */
-       projection = GetPJHashEntry(context);
+       /* Lookup the PJ pointer in the global hash table so we can free it */
+       PJ* projection = GetPJHashEntry(context);
 
        if (!projection)
-               elog(ERROR, "PROJ4SRSCacheDelete: Trying to delete non-existant projection object with MemoryContext key (%p)", (void *)context);
+               elog(ERROR, "PROJSRSCacheDelete: Trying to delete non-existant projection object with MemoryContext key (%p)", (void *)context);
 
        POSTGIS_DEBUGF(3, "deleting projection object (%p) with MemoryContext key (%p)", projection, context);
        /* Free it */
-       pj_free(projection);
+       PROJSRSDestroyPJ(projection);
 
        /* Remove the hash entry as it is no longer needed */
        DeletePJHashEntry(context);
@@ -124,7 +180,7 @@ PROJ4SRSCacheDelete(void *ptr)
 #if POSTGIS_PGSQL_VERSION < 96
 
 static void
-PROJ4SRSCacheInit(MemoryContext context)
+PROJSRSCacheInit(MemoryContext context)
 {
        /*
         * Do nothing as the cache is initialised when the transform()
@@ -133,7 +189,7 @@ PROJ4SRSCacheInit(MemoryContext context)
 }
 
 static void
-PROJ4SRSCacheReset(MemoryContext context)
+PROJSRSCacheReset(MemoryContext context)
 {
        /*
         * Do nothing, but we must supply a function since this call is mandatory according to tgl
@@ -142,7 +198,7 @@ PROJ4SRSCacheReset(MemoryContext context)
 }
 
 static bool
-PROJ4SRSCacheIsEmpty(MemoryContext context)
+PROJSRSCacheIsEmpty(MemoryContext context)
 {
        /*
         * Always return false since this call is mandatory according to tgl
@@ -152,19 +208,19 @@ PROJ4SRSCacheIsEmpty(MemoryContext context)
 }
 
 static void
-PROJ4SRSCacheStats(MemoryContext context, int level)
+PROJSRSCacheStats(MemoryContext context, int level)
 {
        /*
         * Simple stats display function - we must supply a function since this call is mandatory according to tgl
         * (see postgis-devel archives July 2007)
         */
 
-       fprintf(stderr, "%s: PROJ4 context\n", context->name);
+       fprintf(stderr, "%s: PROJ context\n", context->name);
 }
 
 #ifdef MEMORY_CONTEXT_CHECKING
 static void
-PROJ4SRSCacheCheck(MemoryContext context)
+PROJSRSCacheCheck(MemoryContext context)
 {
        /*
         * Do nothing - stub required for when PostgreSQL is compiled
@@ -174,26 +230,26 @@ PROJ4SRSCacheCheck(MemoryContext context)
 #endif
 
 /* Memory context definition must match the current version of PostgreSQL */
-static MemoryContextMethods PROJ4SRSCacheContextMethods =
+static MemoryContextMethods PROJSRSCacheContextMethods =
 {
        NULL,
        NULL,
        NULL,
-       PROJ4SRSCacheInit,
-       PROJ4SRSCacheReset,
-       PROJ4SRSCacheDelete,
+       PROJSRSCacheInit,
+       PROJSRSCacheReset,
+       PROJSRSCacheDelete,
        NULL,
-       PROJ4SRSCacheIsEmpty,
-       PROJ4SRSCacheStats
+       PROJSRSCacheIsEmpty,
+       PROJSRSCacheStats
 #ifdef MEMORY_CONTEXT_CHECKING
-       ,PROJ4SRSCacheCheck
+       ,PROJSRSCacheCheck
 #endif
 };
 
 #endif /* POSTGIS_PGSQL_VERSION < 96 */
 
 /*
- * PROJ4 projPJ Hash Table functions
+ * PROJ PJ Hash Table functions
  */
 
 
@@ -220,10 +276,10 @@ static HTAB *CreatePJHash(void)
        ctl.entrysize = sizeof(PJHashEntry);
        ctl.hash = mcxt_ptr_hash;
 
-       return hash_create("PostGIS PROJ4 Backend projPJ MemoryContext Hash", PROJ4_BACKEND_HASH_SIZE, &ctl, (HASH_ELEM | HASH_FUNCTION));
+       return hash_create("PostGIS PROJ Backend MemoryContext Hash", PROJ_BACKEND_HASH_SIZE, &ctl, (HASH_ELEM | HASH_FUNCTION));
 }
 
-static void AddPJHashEntry(MemoryContext mcxt, projPJ projection)
+static void AddPJHashEntry(MemoryContext mcxt, PJ* projection)
 {
        bool found;
        void **key;
@@ -232,7 +288,7 @@ static void AddPJHashEntry(MemoryContext mcxt, projPJ projection)
        /* The hash key is the MemoryContext pointer */
        key = (void *)&mcxt;
 
-       he = (PJHashEntry *) hash_search(PJHash, key, HASH_ENTER, &found);
+       he = (PJHashEntry*) hash_search(PJHash, key, HASH_ENTER, &found);
        if (!found)
        {
                /* Insert the entry into the new hash element */
@@ -241,12 +297,12 @@ static void AddPJHashEntry(MemoryContext mcxt, projPJ projection)
        }
        else
        {
-               elog(ERROR, "AddPJHashEntry: PROJ4 projection object already exists for this MemoryContext (%p)",
+               elog(ERROR, "AddPJHashEntry: PROJ projection object already exists for this MemoryContext (%p)",
                     (void *)mcxt);
        }
 }
 
-static projPJ GetPJHashEntry(MemoryContext mcxt)
+static PJ* GetPJHashEntry(MemoryContext mcxt)
 {
        void **key;
        PJHashEntry *he;
@@ -273,128 +329,141 @@ static void DeletePJHashEntry(MemoryContext mcxt)
        he = (PJHashEntry *) hash_search(PJHash, key, HASH_REMOVE, NULL);
 
        if (!he)
-               elog(ERROR, "DeletePJHashEntry: There was an error removing the PROJ4 projection object from this MemoryContext (%p)", (void *)mcxt);
+               elog(ERROR, "DeletePJHashEntry: There was an error removing the PROJ projection object from this MemoryContext (%p)", (void *)mcxt);
        else
                he->projection = NULL;
 }
 
-bool
-IsInPROJ4Cache(Proj4Cache PROJ4Cache, int srid) {
-       return IsInPROJ4SRSCache((PROJ4PortalCache *)PROJ4Cache, srid) ;
-}
 
-/*
+/*****************************************************************************
  * Per-cache management functions
  */
 
 static bool
-IsInPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid)
+IsInPROJSRSCache(PROJPortalCache *cache, int srid_from, int srid_to)
 {
        /*
         * Return true/false depending upon whether the item
         * is in the SRS cache.
         */
-
-       int i;
-
-       for (i = 0; i < PROJ4_CACHE_ITEMS; i++)
+       uint32_t i;
+       for (i = 0; i < PROJ_CACHE_ITEMS; i++)
        {
-               if (PROJ4Cache->PROJ4SRSCache[i].srid == srid)
-                       return 1;
+               if (cache->PROJSRSCache[i].srid_from == srid_from &&
+                   cache->PROJSRSCache[i].srid_to == srid_to)
+                       return true;
        }
-
        /* Otherwise not found */
-       return 0;
+       return false;
 }
 
-projPJ GetProjectionFromPROJ4Cache(Proj4Cache cache, int srid)
+static PJ*
+GetProjectionFromPROJCache(PROJPortalCache *cache, int srid_from, int srid_to)
 {
-       return GetProjectionFromPROJ4SRSCache((PROJ4PortalCache *)cache, srid) ;
+       uint32_t i;
+       for (i = 0; i < PROJ_CACHE_ITEMS; i++)
+       {
+               if (cache->PROJSRSCache[i].srid_from == srid_from &&
+                   cache->PROJSRSCache[i].srid_to == srid_to)
+                       return cache->PROJSRSCache[i].projection;
+       }
+
+       return NULL;
 }
 
-/**
- * Return the projection object from the cache (we should
- * already have checked it exists using IsInPROJ4SRSCache first)
- */
-static projPJ
-GetProjectionFromPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid)
+static char*
+SPI_pstrdup(const char* str)
 {
-       int i;
-
-       for (i = 0; i < PROJ4_CACHE_ITEMS; i++)
+       char* ostr = NULL;
+       if (str)
        {
-               if (PROJ4Cache->PROJ4SRSCache[i].srid == srid)
-                       return PROJ4Cache->PROJ4SRSCache[i].projection;
+               ostr = SPI_palloc(strlen(str)+1);
+               strcpy(ostr, str);
        }
-
-       return NULL;
+       return ostr;
 }
 
-char* GetProj4StringSPI(int srid)
+static PjStrs
+GetProjStringsSPI(int srid)
 {
-       static int maxproj4len = 512;
+       const int maxprojlen = 512;
+       const int spibufferlen = 512;
        int spi_result;
-       char *proj_str = palloc(maxproj4len);
-       char proj4_spi_buffer[256];
+       char proj_spi_buffer[spibufferlen];
+       PjStrs strs;
+       memset(&strs, 0, sizeof(strs));
 
        /* Connect */
        spi_result = SPI_connect();
        if (spi_result != SPI_OK_CONNECT)
        {
-               elog(ERROR, "GetProj4StringSPI: Could not connect to database using SPI");
+               elog(ERROR, "Could not connect to database using SPI");
        }
 
        /*
        * This global is allocated in CacheMemoryContext (lifespan of this backend)
        * and is set by SetSpatialRefSysSchema the first time
-       * that GetProjectionsUsingFCInfo is called.
+       * that GetPJUsingFCInfo is called.
        */
+       static char *proj_str_tmpl = "SELECT proj4text, auth_name, auth_srid, srtext "
+                                    "FROM %s%sspatial_ref_sys "
+                                    "WHERE srid = %d "
+                                    "LIMIT 1";
        if (spatialRefSysSchema)
        {
-               /* Format the lookup query */
-               static char *proj_str_tmpl = "SELECT proj4text FROM %s.spatial_ref_sys WHERE srid = %d LIMIT 1";
-               snprintf(proj4_spi_buffer, 255, proj_str_tmpl, spatialRefSysSchema, srid);
+               snprintf(proj_spi_buffer, spibufferlen, proj_str_tmpl, spatialRefSysSchema, ".", srid);
        }
        else
        {
-               /* Format the lookup query */
-               static char *proj_str_tmpl = "SELECT proj4text FROM spatial_ref_sys WHERE srid = %d LIMIT 1";
-               snprintf(proj4_spi_buffer, 255, proj_str_tmpl, srid);
+               snprintf(proj_spi_buffer, spibufferlen, proj_str_tmpl, "", "", srid);
        }
+
        /* Execute the query, noting the readonly status of this SQL */
-       spi_result = SPI_execute(proj4_spi_buffer, true, 1);
+       spi_result = SPI_execute(proj_spi_buffer, true, 1);
 
-       /* Read back the PROJ4 text */
+       /* Read back the PROJ text */
        if (spi_result == SPI_OK_SELECT && SPI_processed > 0)
        {
                /* Select the first (and only tuple) */
                TupleDesc tupdesc = SPI_tuptable->tupdesc;
                SPITupleTable *tuptable = SPI_tuptable;
                HeapTuple tuple = tuptable->vals[0];
-               char *proj4text = SPI_getvalue(tuple, tupdesc, 1);
-
-               if ( proj4text )
+               /* Always return the proj4text */
+               char* proj4text = SPI_getvalue(tuple, tupdesc, 1);
+               if (proj4text && strlen(proj4text))
+                       strs.proj4text = SPI_pstrdup(proj4text);
+
+               /* For Proj >= 6 prefer "EPSG:XXXX" to proj strings */
+               /* as proj_create_crs_to_crs() will give us more consistent */
+               /* results with EPSG numbers than with proj strings */
+               char* authname = SPI_getvalue(tuple, tupdesc, 2);
+               char* authsrid = SPI_getvalue(tuple, tupdesc, 3);
+               if (authname && authsrid &&
+                   strcmp(authname,"EPSG") == 0 &&
+                   strlen(authsrid))
                {
-                       /* Make a projection object out of it */
-                       strncpy(proj_str, proj4text, maxproj4len - 1);
-               }
-               else
-               {
-                       proj_str[0] = 0;
+                       char tmp[maxprojlen];
+                       snprintf(tmp, maxprojlen, "EPSG:%s", authsrid);
+                       strs.epsgtext = SPI_pstrdup(tmp);
                }
+
+               /* Proj6+ can parse srtext, so return that too */
+               char* srtext = SPI_getvalue(tuple, tupdesc, 4);
+               if (srtext && strlen(srtext))
+                       strs.srtext = SPI_pstrdup(srtext);
        }
        else
        {
-               elog(ERROR, "GetProj4StringSPI: Cannot find SRID (%d) in spatial_ref_sys", srid);
+               elog(ERROR, "Cannot find SRID (%d) in spatial_ref_sys", srid);
        }
 
        spi_result = SPI_finish();
        if (spi_result != SPI_OK_FINISH)
        {
-               elog(ERROR, "GetProj4StringSPI: Could not disconnect from database using SPI");
+               elog(ERROR, "Could not disconnect from database using SPI");
        }
 
-       return proj_str;
+       return strs;
 }
 
 
@@ -404,29 +473,32 @@ char* GetProj4StringSPI(int srid)
  *  (WGS84 UTM N/S, Polar Stereographic N/S - see SRID_* macros),
  *  return the proj4text for those.
  */
-static char* GetProj4String(int srid)
+static PjStrs
+GetProjStrings(int srid)
 {
-       static int maxproj4len = 512;
+       const int maxprojlen = 512;
+       PjStrs strs;
+       memset(&strs, 0, sizeof(strs));
 
        /* SRIDs in SPATIAL_REF_SYS */
        if ( srid < SRID_RESERVE_OFFSET )
        {
-               return GetProj4StringSPI(srid);
+               return GetProjStringsSPI(srid);
        }
        /* Automagic SRIDs */
        else
        {
-               char *proj_str = palloc(maxproj4len);
+               strs.proj4text = palloc(maxprojlen);
                int id = srid;
                /* UTM North */
                if ( id >= SRID_NORTH_UTM_START && id <= SRID_NORTH_UTM_END )
                {
-                       snprintf(proj_str, maxproj4len, "+proj=utm +zone=%d +ellps=WGS84 +datum=WGS84 +units=m +no_defs", id - SRID_NORTH_UTM_START + 1);
+                       snprintf(strs.proj4text, maxprojlen, "+proj=utm +zone=%d +ellps=WGS84 +datum=WGS84 +units=m +no_defs", id - SRID_NORTH_UTM_START + 1);
                }
                /* UTM South */
                else if ( id >= SRID_SOUTH_UTM_START && id <= SRID_SOUTH_UTM_END )
                {
-                       snprintf(proj_str, maxproj4len, "+proj=utm +zone=%d +south +ellps=WGS84 +datum=WGS84 +units=m +no_defs", id - SRID_SOUTH_UTM_START + 1);
+                       snprintf(strs.proj4text, maxprojlen, "+proj=utm +zone=%d +south +ellps=WGS84 +datum=WGS84 +units=m +no_defs", id - SRID_SOUTH_UTM_START + 1);
                }
                /* Lambert zones (about 30x30, larger in higher latitudes) */
                /* There are three latitude zones, divided at -90,-60,-30,0,30,60,90. */
@@ -451,102 +523,190 @@ static char* GetProj4String(int srid)
                        else
                                lwerror("Unknown yzone encountered!");
 
-                       snprintf(proj_str, maxproj4len, "+proj=laea +ellps=WGS84 +datum=WGS84 +lat_0=%g +lon_0=%g +units=m +no_defs", lat_0, lon_0);
+                       snprintf(strs.proj4text, maxprojlen, "+proj=laea +ellps=WGS84 +datum=WGS84 +lat_0=%g +lon_0=%g +units=m +no_defs", lat_0, lon_0);
                }
                /* Lambert Azimuthal Equal Area South Pole */
                else if ( id == SRID_SOUTH_LAMBERT )
                {
-                       strncpy(proj_str, "+proj=laea +lat_0=-90 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxproj4len );
+                       strncpy(strs.proj4text, "+proj=laea +lat_0=-90 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxprojlen );
                }
                /* Polar Sterographic South */
                else if ( id == SRID_SOUTH_STEREO )
                {
-                       strncpy(proj_str, "+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxproj4len);
+                       strncpy(strs.proj4text, "+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxprojlen);
                }
                /* Lambert Azimuthal Equal Area North Pole */
                else if ( id == SRID_NORTH_LAMBERT )
                {
-                       strncpy(proj_str, "+proj=laea +lat_0=90 +lon_0=-40 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxproj4len );
+                       strncpy(strs.proj4text, "+proj=laea +lat_0=90 +lon_0=-40 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxprojlen );
                }
                /* Polar Stereographic North */
                else if ( id == SRID_NORTH_STEREO )
                {
-                       strncpy(proj_str, "+proj=stere +lat_0=90 +lat_ts=71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxproj4len );
+                       strncpy(strs.proj4text, "+proj=stere +lat_0=90 +lat_ts=71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxprojlen );
                }
                /* World Mercator */
                else if ( id == SRID_WORLD_MERCATOR )
                {
-                       strncpy(proj_str, "+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxproj4len );
+                       strncpy(strs.proj4text, "+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs", maxprojlen );
                }
                else
                {
                        elog(ERROR, "Invalid reserved SRID (%d)", srid);
-                       return NULL;
+                       return strs;
                }
 
-               POSTGIS_DEBUGF(3, "returning on SRID=%d: %s", srid, proj_str);
-               return proj_str;
+               POSTGIS_DEBUGF(3, "returning on SRID=%d: %s", srid, strs.proj4text);
+               return strs;
        }
 }
 
-void AddToPROJ4Cache(Proj4Cache cache, int srid, int other_srid) {
-       AddToPROJ4SRSCache((PROJ4PortalCache *)cache, srid, other_srid) ;
+static int
+pjstrs_has_entry(const PjStrs *strs)
+{
+       if ((strs->proj4text && strlen(strs->proj4text)) ||
+               (strs->epsgtext && strlen(strs->epsgtext)) ||
+               (strs->srtext && strlen(strs->srtext)))
+               return 1;
+       else
+               return 0;
+}
+
+static void
+pjstrs_pfree(PjStrs *strs)
+{
+       if (strs->proj4text)
+               pfree(strs->proj4text);
+       if (strs->epsgtext)
+               pfree(strs->epsgtext);
+       if (strs->srtext)
+               pfree(strs->srtext);
+}
+
+#if POSTGIS_PROJ_VERSION >= 60
+static char*
+pgstrs_get_entry(const PjStrs *strs, int n)
+{
+       switch (n)
+       {
+               case 0:
+                       return strs->epsgtext;
+               case 1:
+                       return strs->srtext;
+               case 2:
+                       return strs->proj4text;
+               default:
+                       return NULL;
+       }
 }
+#endif
 
+/*
+* Utility function for GML reader that still
+* needs proj4text access
+*/
+char *
+GetProj4String(int srid)
+{
+       PjStrs strs;
+       char *proj4str;
+       memset(&strs, 0, sizeof(strs));
+       strs = GetProjStringsSPI(srid);
+       proj4str = pstrdup(strs.proj4text);
+       pjstrs_pfree(&strs);
+       return proj4str;
+}
 
 /**
- * Add an entry to the local PROJ4 SRS cache. If we need to wrap around then
+ * Add an entry to the local PROJ SRS cache. If we need to wrap around then
  * we must make sure the entry we choose to delete does not contain other_srid
  * which is the definition for the other half of the transformation.
  */
 static void
-AddToPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid, int other_srid)
+AddToPROJSRSCache(PROJPortalCache *PROJCache, int srid_from, int srid_to)
 {
        MemoryContext PJMemoryContext;
-       projPJ projection = NULL;
-       char *proj_str = NULL;
+
+       PjStrs from_strs, to_strs;
+       char *pj_from_str, *pj_to_str;
 
        /*
        ** Turn the SRID number into a proj4 string, by reading from spatial_ref_sys
        ** or instantiating a magical value from a negative srid.
        */
-       proj_str = GetProj4String(srid);
-       if ( ! proj_str )
+       from_strs = GetProjStrings(srid_from);
+       if (!pjstrs_has_entry(&from_strs))
+               elog(ERROR, "got NULL for SRID (%d)", srid_from);
+       to_strs = GetProjStrings(srid_to);
+       if (!pjstrs_has_entry(&to_strs))
+               elog(ERROR, "got NULL for SRID (%d)", srid_to);
+
+#if POSTGIS_PROJ_VERSION < 60
+       PJ* projection = malloc(sizeof(PJ));
+       pj_from_str = from_strs.proj4text;
+       pj_to_str = to_strs.proj4text;
+       projection->pj_from = lwproj_from_string(pj_from_str);
+       projection->pj_to = lwproj_from_string(pj_to_str);
+
+       if (!projection->pj_from)
+               elog(ERROR,
+                   "could not form projection from 'srid=%d' to 'srid=%d'",
+                   srid_from, srid_to);
+
+       if (!projection->pj_to)
+               elog(ERROR,
+                   "could not form projection from 'srid=%d' to 'srid=%d'",
+                   srid_from, srid_to);
+#else
+       PJ* projection = NULL;
+       /* Try combinations of ESPG/SRTEXT/PROJ4TEXT until we find */
+       /* one that gives us a usable transform. Note that we prefer */
+       /* EPSG numbers over SRTEXT and SRTEXT over PROJ4TEXT */
+       /* (3 entries * 3 entries = 9 combos) */
+       uint32_t i;
+       for (i = 0; i < 9; i++)
        {
-               elog(ERROR, "GetProj4String returned NULL for SRID (%d)", srid);
+               pj_from_str = pgstrs_get_entry(&from_strs, i / 3);
+               pj_to_str   = pgstrs_get_entry(&to_strs,   i % 3);
+               if (!(pj_from_str && pj_to_str))
+                       continue;
+               projection = proj_create_crs_to_crs(NULL, pj_from_str, pj_to_str, NULL);
+               if (projection && !proj_errno(projection))
+                       break;
        }
-
-       projection = lwproj_from_string(proj_str);
-       if ( projection == NULL )
+       if (!projection)
        {
-               char *pj_errstr = pj_strerrno(*pj_get_errno_ref());
-               if ( ! pj_errstr )
-                       pj_errstr = "";
-
                elog(ERROR,
-                   "AddToPROJ4SRSCache: could not parse proj4 string '%s' %s",
-                   proj_str, pj_errstr);
+                   "could not form projection from 'srid=%d' to 'srid=%d'",
+                   srid_from, srid_to);
        }
+#endif
 
        /*
         * If the cache is already full then find the first entry
-        * that doesn't contain other_srid and use this as the
-        * subsequent value of PROJ4SRSCacheCount
+        * that doesn't contain our srids and use this as the
+        * subsequent value of PROJSRSCacheCount
         */
-       if (PROJ4Cache->PROJ4SRSCacheCount == PROJ4_CACHE_ITEMS)
+       if (PROJCache->PROJSRSCacheCount == PROJ_CACHE_ITEMS)
        {
                bool found = false;
-               int i;
-
-               for (i = 0; i < PROJ4_CACHE_ITEMS; i++)
+               uint32_t i;
+               for (i = 0; i < PROJ_CACHE_ITEMS; i++)
                {
-                       if (PROJ4Cache->PROJ4SRSCache[i].srid != other_srid && found == false)
+                       if (found == false &&
+                               (PROJCache->PROJSRSCache[i].srid_from != srid_from ||
+                                PROJCache->PROJSRSCache[i].srid_to != srid_to))
                        {
-                               POSTGIS_DEBUGF(3, "choosing to remove item from query cache with SRID %d and index %d", PROJ4Cache->PROJ4SRSCache[i].srid, i);
+                               POSTGIS_DEBUGF(3, "choosing to remove item from query cache with SRIDs %d => %d and index %d",
+                                       PROJCache->PROJSRSCache[i].srid_from,
+                                       PROJCache->PROJSRSCache[i].srid_to,
+                                       i);
 
-                               DeleteFromPROJ4SRSCache(PROJ4Cache, PROJ4Cache->PROJ4SRSCache[i].srid);
-                               PROJ4Cache->PROJ4SRSCacheCount = i;
+                               DeleteFromPROJSRSCache(PROJCache,
+                                   PROJCache->PROJSRSCache[i].srid_from,
+                                   PROJCache->PROJSRSCache[i].srid_to);
 
+                               PROJCache->PROJSRSCacheCount = i;
                                found = true;
                        }
                }
@@ -556,16 +716,21 @@ AddToPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid, int other_srid)
         * Now create a memory context for this projection and
         * store it in the backend hash
         */
-       POSTGIS_DEBUGF(3, "adding SRID %d with proj4text \"%s\" to query cache at index %d", srid, proj_str, PROJ4Cache->PROJ4SRSCacheCount);
+       POSTGIS_DEBUGF(3, "adding transform %d => %d aka \"%s\" => \"%s\" to query cache at index %d",
+               srid_from, srid_to, pj_from_str, pj_to_str, PROJCache->PROJSRSCacheCount);
+
+       /* Free the projection strings */
+       pjstrs_pfree(&from_strs);
+       pjstrs_pfree(&to_strs);
 
 #if POSTGIS_PGSQL_VERSION < 96
        PJMemoryContext = MemoryContextCreate(T_AllocSetContext, 8192,
-                                             &PROJ4SRSCacheContextMethods,
-                                             PROJ4Cache->PROJ4SRSCacheContext,
-                                             "PostGIS PROJ4 PJ Memory Context");
+                                             &PROJSRSCacheContextMethods,
+                                             PROJCache->PROJSRSCacheContext,
+                                             "PostGIS PROJ PJ Memory Context");
 #else
-       PJMemoryContext = AllocSetContextCreate(PROJ4Cache->PROJ4SRSCacheContext,
-                                               "PostGIS PROJ4 PJ Memory Context",
+       PJMemoryContext = AllocSetContextCreate(PROJCache->PROJSRSCacheContext,
+                                               "PostGIS PROJ PJ Memory Context",
                                                ALLOCSET_SMALL_SIZES);
 
        /* PgSQL comments suggest allocating callback in the context */
@@ -574,7 +739,7 @@ AddToPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid, int other_srid)
        {
                MemoryContextCallback *callback = MemoryContextAlloc(PJMemoryContext, sizeof(MemoryContextCallback));
                callback->arg = (void*)PJMemoryContext;
-               callback->func = PROJ4SRSCacheDelete;
+               callback->func = PROJSRSCacheDelete;
                MemoryContextRegisterResetCallback(PJMemoryContext, callback);
        }
 #endif
@@ -591,43 +756,37 @@ AddToPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid, int other_srid)
 
        AddPJHashEntry(PJMemoryContext, projection);
 
-       PROJ4Cache->PROJ4SRSCache[PROJ4Cache->PROJ4SRSCacheCount].srid = srid;
-       PROJ4Cache->PROJ4SRSCache[PROJ4Cache->PROJ4SRSCacheCount].projection = projection;
-       PROJ4Cache->PROJ4SRSCache[PROJ4Cache->PROJ4SRSCacheCount].projection_mcxt = PJMemoryContext;
-       PROJ4Cache->PROJ4SRSCacheCount++;
-
-       /* Free the projection string */
-       pfree(proj_str);
-
-}
-
-void DeleteFromPROJ4Cache(Proj4Cache cache, int srid) {
-       DeleteFromPROJ4SRSCache((PROJ4PortalCache *)cache, srid) ;
+       PROJCache->PROJSRSCache[PROJCache->PROJSRSCacheCount].srid_from = srid_from;
+       PROJCache->PROJSRSCache[PROJCache->PROJSRSCacheCount].srid_to = srid_to;
+       PROJCache->PROJSRSCache[PROJCache->PROJSRSCacheCount].projection = projection;
+       PROJCache->PROJSRSCache[PROJCache->PROJSRSCacheCount].projection_mcxt = PJMemoryContext;
+       PROJCache->PROJSRSCacheCount++;
 }
 
 
-static void DeleteFromPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid)
+static void
+DeleteFromPROJSRSCache(PROJPortalCache *PROJCache, int srid_from, int srid_to)
 {
        /*
         * Delete the SRID entry from the cache
         */
-
-       int i;
-
-       for (i = 0; i < PROJ4_CACHE_ITEMS; i++)
+       uint32_t i;
+       for (i = 0; i < PROJ_CACHE_ITEMS; i++)
        {
-               if (PROJ4Cache->PROJ4SRSCache[i].srid == srid)
+               if (PROJCache->PROJSRSCache[i].srid_from == srid_from &&
+                       PROJCache->PROJSRSCache[i].srid_to == srid_to)
                {
-                       POSTGIS_DEBUGF(3, "removing query cache entry with SRID %d at index %d", srid, i);
+                       POSTGIS_DEBUGF(3, "removing query cache entry with SRIDs %d => %d at index %d", srid_from, srid_to, i);
 
                        /*
-                        * Zero out the entries and free the PROJ4 handle
+                        * Zero out the entries and free the PROJ handle
                         * by deleting the memory context
                         */
-                       MemoryContextDelete(PROJ4Cache->PROJ4SRSCache[i].projection_mcxt);
-                       PROJ4Cache->PROJ4SRSCache[i].projection = NULL;
-                       PROJ4Cache->PROJ4SRSCache[i].projection_mcxt = NULL;
-                       PROJ4Cache->PROJ4SRSCache[i].srid = SRID_UNKNOWN;
+                       MemoryContextDelete(PROJCache->PROJSRSCache[i].projection_mcxt);
+                       PROJCache->PROJSRSCache[i].projection = NULL;
+                       PROJCache->PROJSRSCache[i].projection_mcxt = NULL;
+                       PROJCache->PROJSRSCache[i].srid_from = SRID_UNKNOWN;
+                       PROJCache->PROJSRSCache[i].srid_to = SRID_UNKNOWN;
                }
        }
 }
@@ -645,13 +804,13 @@ static void DeleteFromPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid)
  * since the method of determining the current installation
  * path are different on older PostgreSQL versions.
  */
-void SetPROJ4LibPath(void)
+void SetPROJLibPath(void)
 {
        char *path;
        char *share_path;
        const char **proj_lib_path;
 
-       if (!IsPROJ4LibPathSet) {
+       if (!IsPROJLibPathSet) {
 
                /*
                 * Get the sharepath and append /contrib/postgis/proj to form a suitable
@@ -666,117 +825,74 @@ void SetPROJ4LibPath(void)
                *proj_lib_path = path;
 
                snprintf(path, MAXPGPATH - 1, "%s/contrib/postgis-%s.%s/proj", share_path, POSTGIS_MAJOR_VERSION, POSTGIS_MINOR_VERSION);
-
+#if POSTGIS_PROJ_VERSION < 60
                /* Set the search path for PROJ.4 */
                pj_set_searchpath(1, proj_lib_path);
-
+#else
+               /* Set the search path for PROJ */
+               proj_context_set_search_paths(NULL, 1, proj_lib_path);
+#endif
                /* Ensure we only do this once... */
-               IsPROJ4LibPathSet = true;
+               IsPROJLibPathSet = true;
        }
 }
 
-Proj4Cache GetPROJ4Cache(FunctionCallInfo fcinfo) {
-       return (Proj4Cache)GetPROJ4SRSCache(fcinfo);
-}
-
-
-/*
-* Given a function call context, figure out what namespace the
-* function is being called from, and copy that into a global
-* for use by GetProj4StringSPI
-*/
-static void
-SetSpatialRefSysSchema(FunctionCallInfo fcinfo)
-{
-       char *nsp_name;
-
-       /* Schema info is already cached, we're done here */
-       if (spatialRefSysSchema) return;
-
-       /* For some reason we have a hobbled fcinfo/flinfo */
-       if (!fcinfo || !fcinfo->flinfo) return;
-
-       nsp_name = get_namespace_name(get_func_namespace(fcinfo->flinfo->fn_oid));
-       /* early exit if we cannot lookup nsp_name, cf #4067 */
-       if (!nsp_name) return;
-
-       elog(DEBUG4, "%s located %s in namespace %s", __func__, get_func_name(fcinfo->flinfo->fn_oid), nsp_name);
-       spatialRefSysSchema = MemoryContextStrdup(CacheMemoryContext, nsp_name);;
-       return;
-}
 
 int
-GetProjectionsUsingFCInfo(FunctionCallInfo fcinfo, int srid1, int srid2, projPJ *pj1, projPJ *pj2)
+GetPJUsingFCInfo(FunctionCallInfo fcinfo, int srid_from, int srid_to, PJ** pj)
 {
-       Proj4Cache *proj_cache = NULL;
+       PROJPortalCache *proj_cache = NULL;
 
        /* Set the search path if we haven't already */
-       SetPROJ4LibPath();
+       SetPROJLibPath();
 
        /* Look up the spatial_ref_sys schema if we haven't already */
        SetSpatialRefSysSchema(fcinfo);
 
        /* get or initialize the cache for this round */
-       proj_cache = GetPROJ4Cache(fcinfo);
-       if ( !proj_cache )
+       proj_cache = GetPROJSRSCache(fcinfo);
+       if (!proj_cache)
                return LW_FAILURE;
 
        /* Add the output srid to the cache if it's not already there */
-       if (!IsInPROJ4Cache(proj_cache, srid1))
-               AddToPROJ4Cache(proj_cache, srid1, srid2);
-
-       /* Add the input srid to the cache if it's not already there */
-       if (!IsInPROJ4Cache(proj_cache, srid2))
-               AddToPROJ4Cache(proj_cache, srid2, srid1);
+       if (!IsInPROJSRSCache(proj_cache, srid_from, srid_to))
+               AddToPROJSRSCache(proj_cache, srid_from, srid_to);
 
        /* Get the projections */
-       *pj1 = GetProjectionFromPROJ4Cache(proj_cache, srid1);
-       *pj2 = GetProjectionFromPROJ4Cache(proj_cache, srid2);
+       *pj = GetProjectionFromPROJCache(proj_cache, srid_from, srid_to);
 
        return LW_SUCCESS;
 }
 
-int
-spheroid_init_from_srid(FunctionCallInfo fcinfo, int srid, SPHEROID *s)
-{
-       projPJ pj1, pj2;
-#if POSTGIS_PROJ_VERSION >= 48
-       double major_axis, minor_axis, eccentricity_squared;
-#endif
 
-       if ( GetProjectionsUsingFCInfo(fcinfo, srid, srid, &pj1, &pj2) == LW_FAILURE)
-               return LW_FAILURE;
-
-       if ( ! pj_is_latlong(pj1) )
-               return LW_FAILURE;
-
-#if POSTGIS_PROJ_VERSION >= 48
-       /* For newer versions of Proj we can pull the spheroid paramaeters and initialize */
-       /* using them */
-       pj_get_spheroid_defn(pj1, &major_axis, &eccentricity_squared);
-       minor_axis = major_axis * sqrt(1-eccentricity_squared);
-       spheroid_init(s, major_axis, minor_axis);
+static int
+proj_pj_is_latlong(const PJ* pj)
+{
+#if POSTGIS_PROJ_VERSION < 60
+       return pj_is_latlong(pj->pj_from);
 #else
-       /* For old versions of Proj we cannot lookup the spheroid parameters from the API */
-       /* So we use the WGS84 parameters (boo!) */
-       spheroid_init(s, WGS84_MAJOR_AXIS, WGS84_MINOR_AXIS);
+       PJ_TYPE pj_type = proj_get_type(proj_get_source_crs(NULL, pj));
+       return (pj_type == PJ_TYPE_GEOGRAPHIC_2D_CRS) ||
+              (pj_type == PJ_TYPE_GEOGRAPHIC_3D_CRS);
 #endif
-
-       return LW_SUCCESS;
 }
 
-void srid_is_latlong(FunctionCallInfo fcinfo, int srid)
+static int
+srid_is_latlong(FunctionCallInfo fcinfo, int srid)
 {
-       projPJ pj1;
-       projPJ pj2;
-
-       if ( srid == SRID_DEFAULT || srid == SRID_UNKNOWN )
-               return;
+       PJ* pj;
+       if ( GetPJUsingFCInfo(fcinfo, srid, srid, &pj) == LW_FAILURE)
+               return LW_FALSE;
+       return proj_pj_is_latlong(pj);
+}
 
-       if ( GetProjectionsUsingFCInfo(fcinfo, srid, srid, &pj1, &pj2) == LW_FAILURE)
+void
+srid_check_latlong(FunctionCallInfo fcinfo, int srid)
+{
+       if (srid == SRID_DEFAULT || srid == SRID_UNKNOWN)
                return;
 
-       if ( pj_is_latlong(pj1) )
+       if (srid_is_latlong(fcinfo, srid))
                return;
 
        ereport(ERROR, (
@@ -784,14 +900,10 @@ void srid_is_latlong(FunctionCallInfo fcinfo, int srid)
                    errmsg("Only lon/lat coordinate systems are supported in geography.")));
 }
 
-
-srs_precision srid_axis_precision(FunctionCallInfo fcinfo, int srid, int precision)
+srs_precision
+srid_axis_precision(FunctionCallInfo fcinfo, int srid, int precision)
 {
-       projPJ pj1;
-       projPJ pj2;
-
        srs_precision sp;
-
        sp.precision_xy = precision;
        sp.precision_z = precision;
        sp.precision_m = precision;
@@ -799,10 +911,7 @@ srs_precision srid_axis_precision(FunctionCallInfo fcinfo, int srid, int precisi
        if ( srid == SRID_UNKNOWN )
                return sp;
 
-       if ( GetProjectionsUsingFCInfo(fcinfo, srid, srid, &pj1, &pj2) == LW_FAILURE)
-               return sp;
-
-       if ( pj_is_latlong(pj1) )
+       if ( srid_is_latlong(fcinfo, srid) )
        {
                sp.precision_xy += 5;
                return sp;
@@ -810,3 +919,59 @@ srs_precision srid_axis_precision(FunctionCallInfo fcinfo, int srid, int precisi
 
        return sp;
 }
+
+int
+spheroid_init_from_srid(FunctionCallInfo fcinfo, int srid, SPHEROID *s)
+{
+       PJ* pj;
+#if POSTGIS_PROJ_VERSION >= 60
+       double out_semi_major_metre, out_semi_minor_metre, out_inv_flattening;
+       int out_is_semi_minor_computed;
+       PJ *pj_ellps, *pj_crs;
+#elif POSTGIS_PROJ_VERSION >= 48
+       double major_axis, minor_axis, eccentricity_squared;
+#endif
+
+       if ( GetPJUsingFCInfo(fcinfo, srid, srid, &pj) == LW_FAILURE)
+               return LW_FAILURE;
+
+#if POSTGIS_PROJ_VERSION >= 60
+       if (!proj_pj_is_latlong(pj))
+               return LW_FAILURE;
+       pj_crs = proj_get_source_crs(NULL, pj);
+       if (!pj_crs)
+       {
+               lwerror("%s: proj_get_source_crs returned NULL", __func__);
+       }
+       pj_ellps = proj_get_ellipsoid(NULL, pj_crs);
+       if (!pj_ellps)
+       {
+               proj_destroy(pj_crs);
+               lwerror("%s: proj_get_ellipsoid returned NULL", __func__);
+       }
+       proj_ellipsoid_get_parameters(NULL, pj_ellps,
+               &out_semi_major_metre, &out_semi_minor_metre,
+               &out_is_semi_minor_computed, &out_inv_flattening);
+       proj_destroy(pj_ellps);
+       proj_destroy(pj_crs);
+       spheroid_init(s, out_semi_major_metre, out_semi_minor_metre);
+
+#elif POSTGIS_PROJ_VERSION >= 48
+       if (!pj_is_latlong(pj->pj_from))
+               return LW_FAILURE;
+       /* For newer versions of Proj we can pull the spheroid paramaeters and initialize */
+       /* using them */
+       pj_get_spheroid_defn(pj->pj_from, &major_axis, &eccentricity_squared);
+       minor_axis = major_axis * sqrt(1-eccentricity_squared);
+       spheroid_init(s, major_axis, minor_axis);
+
+#else
+       if (!pj_is_latlong(pj->pj_from))
+               return LW_FAILURE;
+       /* For old versions of Proj we cannot lookup the spheroid parameters from the API */
+       /* So we use the WGS84 parameters (boo!) */
+       spheroid_init(s, WGS84_MAJOR_AXIS, WGS84_MINOR_AXIS);
+#endif
+
+       return LW_SUCCESS;
+}
index f44b39273e5a94be367c46866556fbcf12596b3f..8c9e45161b8ce6899c124b0142fd4084cb04e9f5 100644 (file)
  **********************************************************************/
 
 #include "postgres.h"
-#include "liblwgeom.h"
+#include "liblwgeom_internal.h"
 #include "lwgeom_pg.h"
 
+
 typedef struct srs_precision
 {
        int precision_xy;
@@ -20,23 +21,19 @@ typedef struct srs_precision
        int precision_m;
 } srs_precision;
 
-char* GetProj4StringSPI(int srid);
-void SetPROJ4LibPath(void) ;
+char * GetProj4String(int srid);
 
 /**
  * Opaque type to use in the projection cache API.
  */
-typedef void *Proj4Cache ;
-
-void SetPROJ4LibPath(void);
-Proj4Cache GetPROJ4Cache(FunctionCallInfo fcinfo) ;
-bool IsInPROJ4Cache(Proj4Cache cache, int srid) ;
-void AddToPROJ4Cache(Proj4Cache cache, int srid, int other_srid);
-void DeleteFromPROJ4Cache(Proj4Cache cache, int srid) ;
-projPJ GetProjectionFromPROJ4Cache(Proj4Cache cache, int srid);
-int GetProjectionsUsingFCInfo(FunctionCallInfo fcinfo, int srid1, int srid2, projPJ *pj1, projPJ *pj2);
+typedef void *ProjCache ;
+
+void SetPROJLibPath(void);
+bool IsInPROJCache(ProjCache cache, int srid_from, int srid_to);
+PJ* GetPJFromPROJCache(ProjCache cache, int srid_from, int srid_to);
+int GetPJUsingFCInfo(FunctionCallInfo fcinfo, int srid_from, int srid_to, PJ** pj);
 int spheroid_init_from_srid(FunctionCallInfo fcinfo, int srid, SPHEROID *s);
-void srid_is_latlong(FunctionCallInfo fcinfo, int srid);
+void srid_check_latlong(FunctionCallInfo fcinfo, int srid);
 srs_precision srid_axis_precision(FunctionCallInfo fcinfo, int srid, int precision);
 
 /**
index f8671ec7ef16f7e2ae64999692b97deba58e0849..6d7471ca890382f694c0d24ea07bb302e73187dc 100644 (file)
@@ -14,29 +14,53 @@ dnl Return the PROJ.4 version number
 dnl
 
 AC_DEFUN([AC_PROJ_VERSION], [
-       AC_RUN_IFELSE(
-               [AC_LANG_PROGRAM([
-               #ifdef HAVE_STDINT_H
-                       #include <stdio.h>
-               #endif
-               #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H 1
-               #include "proj_api.h"
-       ],
-       [
-               FILE *fp;
 
-               fp = fopen("conftest.out", "w");
-               fprintf(fp, "%d\n", PJ_VERSION);
-               fclose(fp)])
-       ],
-        [
-               dnl The program ran successfully, so return the version number in the form MAJORMINOR
-               $1=`cat conftest.out | sed 's/\([[0-9]]\)\([[0-9]]\)\([[0-9]]\)/\1\2/'`
-       ],
-        [
-               dnl The program failed so return an empty variable
-               $1=""
-       ]
-        )
+       AC_CHECK_HEADER([proj.h], [
+               dnl Proj >= 6 include and version string
+               AC_RUN_IFELSE([
+                       AC_LANG_PROGRAM([
+                               #ifdef HAVE_STDINT_H
+                               #include <stdio.h>
+                               #endif
+                               #include "proj.h"
+                       ],[
+                               FILE *fp;
+                               int vernum;
+
+                               fp = fopen("conftest.out", "w");
+                               vernum = (100 * PROJ_VERSION_MAJOR) + (10 * PROJ_VERSION_MINOR) + PROJ_VERSION_PATCH;
+                               fprintf(fp, "%d\n", vernum);
+                               fclose(fp);
+                       ])
+               ],[
+                       dnl The program ran successfully, so return the version number in the form MAJORMINOR
+                       $1=`cat conftest.out | sed 's/\([[0-9]]\)\([[0-9]]\)\([[0-9]]\)/\1\2/'`
+               ],[
+                       dnl The program failed so return an empty variable
+                       $1=""
+               ])
+       ],[
+               dnl Proj < 6 include and version string
+               AC_RUN_IFELSE(
+                       [AC_LANG_PROGRAM([
+                               #ifdef HAVE_STDINT_H
+                               #include <stdio.h>
+                               #endif
+                               #include "proj_api.h"
+                       ],[
+                               FILE *fp;
+
+                               fp = fopen("conftest.out", "w");
+                               fprintf(fp, "%d\n", PJ_VERSION);
+                               fclose(fp);
+                       ])
+               ],[
+                       dnl The program ran successfully, so return the version number in the form MAJORMINOR
+                       $1=`cat conftest.out | sed 's/\([[0-9]]\)\([[0-9]]\)\([[0-9]]\)/\1\2/'`
+               ],[
+                       dnl The program failed so return an empty variable
+                       $1=""
+               ])
+    ])
 ])
 
index ad0deb838b690ef4d7a423d4a7ca8c120ddce9d3..02b128b79384f4f99780087b8cf2bf24a3213a84 100644 (file)
@@ -171,7 +171,7 @@ Datum geography_in(PG_FUNCTION_ARGS)
        }
 
        /* Error on any SRID != default */
-       srid_is_latlong(fcinfo, lwgeom->srid);
+       srid_check_latlong(fcinfo, lwgeom->srid);
 
        /* Convert to gserialized */
        g_ser = gserialized_geography_from_lwgeom(lwgeom, geog_typmod);
@@ -563,7 +563,7 @@ Datum geography_from_text(PG_FUNCTION_ARGS)
                PG_PARSER_ERROR(lwg_parser_result);
 
        /* Error on any SRID != default */
-       srid_is_latlong(fcinfo, lwg_parser_result.geom->srid);
+       srid_check_latlong(fcinfo, lwg_parser_result.geom->srid);
 
        /* Clean up string */
        pfree(wkt);
@@ -591,7 +591,7 @@ Datum geography_from_binary(PG_FUNCTION_ARGS)
                lwpgerror("Unable to parse WKB");
 
        /* Error on any SRID != default */
-       srid_is_latlong(fcinfo, lwgeom->srid);
+       srid_check_latlong(fcinfo, lwgeom->srid);
 
        gser = gserialized_geography_from_lwgeom(lwgeom, -1);
        lwgeom_free(lwgeom);
@@ -617,7 +617,7 @@ Datum geography_from_geometry(PG_FUNCTION_ARGS)
        }
 
        /* Error on any SRID != default */
-       srid_is_latlong(fcinfo, lwgeom->srid);
+       srid_check_latlong(fcinfo, lwgeom->srid);
 
        /* Force the geometry to have valid geodetic coordinate range. */
        lwgeom_nudge_geodetic(lwgeom);
@@ -681,7 +681,7 @@ Datum geography_recv(PG_FUNCTION_ARGS)
        lwgeom = lwgeom_from_wkb((uint8_t*)buf->data, buf->len, LW_PARSER_CHECK_ALL);
 
        /* Error on any SRID != default */
-       srid_is_latlong(fcinfo, lwgeom->srid);
+       srid_check_latlong(fcinfo, lwgeom->srid);
 
        g_ser = gserialized_geography_from_lwgeom(lwgeom, geog_typmod);
 
index a1c9b0f6e4db3844bca846ef8df083885ad9cf45..3385c820c6390dedda27cd4d653c2f26a5a400f0 100644 (file)
@@ -39,7 +39,7 @@
 #include "geography_measurement_trees.h" /* For circ_tree caching */
 #include "lwgeom_transform.h" /* For SRID functions */
 
-#if PROJ_GEODESIC
+#ifdef PROJ_GEODESIC
 /* round to 10 nm precision */
 #define INVMINDIST 1.0e8
 #else
@@ -531,7 +531,7 @@ Datum geography_area(PG_FUNCTION_ARGS)
        else
                lwgeom_calculate_gbox_geodetic(lwgeom, &gbox);
 
-#if ! PROJ_GEODESIC
+#ifndef PROJ_GEODESIC
        /* Test for cases that are currently not handled by spheroid code */
        if ( use_spheroid )
        {
@@ -542,7 +542,7 @@ Datum geography_area(PG_FUNCTION_ARGS)
                if ( gbox.zmax > 0.0 && gbox.zmin < 0.0 )
                        use_spheroid = LW_FALSE;
        }
-#endif /* if ! PROJ_GEODESIC */
+#endif /* ifndef PROJ_GEODESIC */
 
        /* User requests spherical calculation, turn our spheroid into a sphere */
        if ( ! use_spheroid )
index e04e8868868e8a5a9279995985bb1cff45316324..a41c6e6a1b179b843c8dca4ba52582504a4abff1 100644 (file)
@@ -52,6 +52,7 @@
 
 
 #include <float.h> /* For FLT_MAX */
+#include <math.h>
 
 /*
 ** When is a node split not so good? If more than 90% of the entries
index b9dc2649b7cb7855eaf80473b694d527f3680e36..e0dacffd759566234c3bb5056a3e9a2209beb1bd 100644 (file)
@@ -49,6 +49,7 @@
 #include "geography.h"
 
 #include <assert.h>
+#include <math.h>
 
 /*
 ** When is a node split not so good? If more than 90% of the entries
index 7c0df228429b742e7fb1de503bf23420a375fd39..ee86ab3501fad034302f4e0b040f6c9c425cf966 100644 (file)
@@ -82,6 +82,7 @@
 #include "lwgeom_pg.h"        /* For debugging macros. */
 #include <gserialized_gist.h> /* For utility functions. */
 #include <lwgeom_pg.h>        /* For debugging macros. */
+#include <math.h>
 
 /*
 ** SP-GiST 2D index function prototypes
index e4f767b0a39284597c161992f6ffa0db03d5f71e..9df9c3fe0a7258b64599d695c2c9b00a1ccfd5b3 100644 (file)
@@ -294,7 +294,7 @@ Datum geography_typmod_in(PG_FUNCTION_ARGS)
        int32 typmod = gserialized_typmod_in(arr, LW_TRUE);
        int srid = TYPMOD_GET_SRID(typmod);
        /* Check the SRID is legal (geographic coordinates) */
-       srid_is_latlong(fcinfo, srid);
+       srid_check_latlong(fcinfo, srid);
 
        PG_RETURN_INT32(typmod);
 }
index cf9bd259bec3974bcb1a6c48b422aa191e8a87b9..4eb505ab5301979498fff3f4ae58001bbd49a4ef 100644 (file)
@@ -292,36 +292,68 @@ static xmlNodePtr get_xlink_node(xmlNodePtr xnode)
 }
 
 
+
 /**
- * Use Proj4 to reproject a given POINTARRAY
+ * Use Proj to reproject a given POINTARRAY
  */
+
+#if POSTGIS_PROJ_VERSION < 60
+
 static POINTARRAY* gml_reproject_pa(POINTARRAY *pa, int srid_in, int srid_out)
 {
-       projPJ in_pj, out_pj;
+       PJ pj;
        char *text_in, *text_out;
 
        if (srid_in == SRID_UNKNOWN) return pa; /* nothing to do */
        if (srid_out == SRID_UNKNOWN) gml_lwpgerror("invalid GML representation", 3);
 
-       text_in = GetProj4StringSPI(srid_in);
-       text_out = GetProj4StringSPI(srid_out);
+       text_in = GetProj4String(srid_in);
+       text_out = GetProj4String(srid_out);
 
-       in_pj = lwproj_from_string(text_in);
-       out_pj = lwproj_from_string(text_out);
+       pj.pj_from = lwproj_from_string(text_in);
+       pj.pj_to = lwproj_from_string(text_out);
 
        lwfree(text_in);
        lwfree(text_out);
 
-       if ( ptarray_transform(pa, in_pj, out_pj) == LW_FAILURE )
+       if ( ptarray_transform(pa, &pj) == LW_FAILURE )
        {
                elog(ERROR, "gml_reproject_pa: reprojection failed");
        }
 
-       pj_free(in_pj);
-       pj_free(out_pj);
+       pj_free(pj.pj_from);
+       pj_free(pj.pj_to);
+
+       return pa;
+}
+#else
+/*
+ * TODO: rework GML projection handling to skip the spatial_ref_sys
+ * lookups, and use the Proj 6+ EPSG catalogue and built-in SRID
+ * lookups directly. Drop this ugly hack.
+ */
+static POINTARRAY* gml_reproject_pa(POINTARRAY *pa, int srid_in, int srid_out)
+{
+       PJ *pj;
+       char text_in[32];
+       char text_out[32];
+
+       if (srid_in == SRID_UNKNOWN) return pa; /* nothing to do */
+       if (srid_out == SRID_UNKNOWN) gml_lwpgerror("invalid GML representation", 3);
+
+       snprintf(text_in, 32, "EPSG:%d", srid_in);
+       snprintf(text_out, 32, "EPSG:%d", srid_out);
+       pj = proj_create_crs_to_crs(NULL, text_in, text_out, NULL);
+
+       if (ptarray_transform(pa, pj) == LW_FAILURE)
+       {
+               elog(ERROR, "gml_reproject_pa: reprojection failed");
+       }
+       proj_destroy(pj);
 
        return pa;
 }
+#endif /* POSTGIS_PROJ_VERSION */
 
 
 /**
index 26f2a7194eca1e61d8332834c39b8609b4e0910a..8acf34ef78f2b5101c05251801a64b33e5aa1245 100644 (file)
@@ -439,7 +439,7 @@ Datum LWGEOM_length_ellipsoid_linestring(PG_FUNCTION_ARGS)
  *    (if deltaX is 1 degrees, then that distance represents 1/360 of a circle of radius S.)
  *
  *
- *  Parts taken from PROJ4 - geodetic_to_geocentric() (for calculating Rn)
+ *  Parts taken from PROJ - geodetic_to_geocentric() (for calculating Rn)
  *
  *  remember that lat1/long1/lat2/long2 are comming in a *RADIANS* not degrees.
  *
index eb2c8e6031fc142311499097cc3c29ad57c5cc73..ef1194afdbcaeffd0d95580b9b76f8e0c9baac6a 100644 (file)
@@ -44,23 +44,23 @@ Datum postgis_proj_version(PG_FUNCTION_ARGS);
 PG_FUNCTION_INFO_V1(transform);
 Datum transform(PG_FUNCTION_ARGS)
 {
-       GSERIALIZED *geom;
-       GSERIALIZED *result=NULL;
-       LWGEOM *lwgeom;
-       projPJ input_pj, output_pj;
-       int32 output_srid, input_srid;
-
-       output_srid = PG_GETARG_INT32(1);
-       if (output_srid == SRID_UNKNOWN)
+       GSERIALIZEDgeom;
+       GSERIALIZEDresult=NULL;
+       LWGEOMlwgeom;
+       PJ* pj;
+       int32 srid_to, srid_from;
+
+       srid_to = PG_GETARG_INT32(1);
+       if (srid_to == SRID_UNKNOWN)
        {
                elog(ERROR, "%d is an invalid target SRID", SRID_UNKNOWN);
                PG_RETURN_NULL();
        }
 
        geom = PG_GETARG_GSERIALIZED_P_COPY(0);
-       input_srid = gserialized_get_srid(geom);
+       srid_from = gserialized_get_srid(geom);
 
-       if ( input_srid == SRID_UNKNOWN )
+       if ( srid_from == SRID_UNKNOWN )
        {
                PG_FREE_IF_COPY(geom, 0);
                elog(ERROR, "Input geometry has unknown (%d) SRID", SRID_UNKNOWN);
@@ -68,10 +68,10 @@ Datum transform(PG_FUNCTION_ARGS)
        }
 
        /* Input SRID and output SRID are equal, noop */
-       if ( input_srid == output_srid )
+       if ( srid_from == srid_to )
                PG_RETURN_POINTER(geom);
 
-       if ( GetProjectionsUsingFCInfo(fcinfo, input_srid, output_srid, &input_pj, &output_pj) == LW_FAILURE )
+       if ( GetPJUsingFCInfo(fcinfo, srid_from, srid_to, &pj) == LW_FAILURE )
        {
                PG_FREE_IF_COPY(geom, 0);
                elog(ERROR, "Failure reading projections from spatial_ref_sys.");
@@ -80,8 +80,8 @@ Datum transform(PG_FUNCTION_ARGS)
 
        /* now we have a geometry, and input/output PJ structs. */
        lwgeom = lwgeom_from_gserialized(geom);
-       lwgeom_transform(lwgeom, input_pj, output_pj);
-       lwgeom->srid = output_srid;
+       lwgeom_transform(lwgeom, pj);
+       lwgeom->srid = srid_to;
 
        /* Re-compute bbox if input had one (COMPUTE_BBOX TAINTING) */
        if ( lwgeom->bbox )
@@ -107,96 +107,57 @@ Datum transform(PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1(transform_geom);
 Datum transform_geom(PG_FUNCTION_ARGS)
 {
-       GSERIALIZED *geom;
-       GSERIALIZED *result=NULL;
-       LWGEOM *lwgeom;
-       projPJ input_pj, output_pj;
-       char *input_proj4, *output_proj4;
-       text *input_proj4_text;
-       text *output_proj4_text;
-       int32 result_srid ;
-       char *pj_errstr;
+       GSERIALIZED *gser, *gser_result=NULL;
+       LWGEOM *geom;
+       char *input_srs, *output_srs;
+       int32 result_srid;
+       int rv;
 
-       result_srid = PG_GETARG_INT32(3);
-       geom = PG_GETARG_GSERIALIZED_P_COPY(0);
+       /* Take a copy, since we will be altering the coordinates */
+       gser = PG_GETARG_GSERIALIZED_P_COPY(0);
 
        /* Set the search path if we haven't already */
-       SetPROJ4LibPath();
-
-       /* Read the arguments */
-       input_proj4_text  = (PG_GETARG_TEXT_P(1));
-       output_proj4_text = (PG_GETARG_TEXT_P(2));
+       SetPROJLibPath();
 
        /* Convert from text to cstring for libproj */
-       input_proj4 = text_to_cstring(input_proj4_text);
-       output_proj4 = text_to_cstring(output_proj4_text);
-
-       /* make input and output projection objects */
-       input_pj = lwproj_from_string(input_proj4);
-       if ( input_pj == NULL )
-       {
-               pj_errstr = pj_strerrno(*pj_get_errno_ref());
-               if ( ! pj_errstr ) pj_errstr = "";
-
-               /* we need this for error reporting */
-               /* pfree(input_proj4); */
-               pfree(output_proj4);
-               pfree(geom);
-
-               elog(ERROR,
-                   "transform_geom: could not parse proj4 string '%s' %s",
-                   input_proj4, pj_errstr);
-               PG_RETURN_NULL();
-       }
-       pfree(input_proj4);
+       input_srs = text_to_cstring(PG_GETARG_TEXT_P(1));
+       output_srs = text_to_cstring(PG_GETARG_TEXT_P(2));
+       result_srid = PG_GETARG_INT32(3);
 
-       output_pj = lwproj_from_string(output_proj4);
+       /* now we have a geometry, and input/output PJ structs. */
+       geom = lwgeom_from_gserialized(gser);
+       rv = lwgeom_transform_from_str(geom, input_srs, output_srs);
+       pfree(input_srs);
+       pfree(output_srs);
 
-       if ( output_pj == NULL )
+       if (rv == LW_FAILURE)
        {
-               pj_errstr = pj_strerrno(*pj_get_errno_ref());
-               if ( ! pj_errstr ) pj_errstr = "";
-
-               /* we need this for error reporting */
-               /* pfree(output_proj4); */
-               pj_free(input_pj);
-               pfree(geom);
-
-               elog(ERROR,
-                       "transform_geom: couldn't parse proj4 output string: '%s': %s",
-                       output_proj4, pj_errstr);
+               elog(ERROR, "coordinate transformation failed");
                PG_RETURN_NULL();
        }
-       pfree(output_proj4);
-
-       /* now we have a geometry, and input/output PJ structs. */
-       lwgeom = lwgeom_from_gserialized(geom);
-       lwgeom_transform(lwgeom, input_pj, output_pj);
-       lwgeom->srid = result_srid;
-
-       /* clean up */
-       pj_free(input_pj);
-       pj_free(output_pj);
 
        /* Re-compute bbox if input had one (COMPUTE_BBOX TAINTING) */
-       if ( lwgeom->bbox )
-       {
-               lwgeom_refresh_bbox(lwgeom);
-       }
-
-       result = geometry_serialize(lwgeom);
+       geom->srid = result_srid;
+       if (geom->bbox)
+               lwgeom_refresh_bbox(geom);
 
-       lwgeom_free(lwgeom);
-       PG_FREE_IF_COPY(geom, 0);
+       gser_result = geometry_serialize(geom);
+       lwgeom_free(geom);
+       PG_FREE_IF_COPY(gser, 0);
 
-       PG_RETURN_POINTER(result); /* new geometry */
+       PG_RETURN_POINTER(gser_result); /* new geometry */
 }
 
 
 PG_FUNCTION_INFO_V1(postgis_proj_version);
 Datum postgis_proj_version(PG_FUNCTION_ARGS)
 {
+#if POSTGIS_PROJ_VERSION < 60
        const char *ver = pj_get_release();
        text *result = cstring_to_text(ver);
+#else
+       PJ_INFO pji = proj_info();
+       text *result =  cstring_to_text(pji.version);
+#endif
        PG_RETURN_POINTER(result);
 }
index 68cfc16053ed8e78d2863b78f215d3e6d45c2b04..de336c8b9762083f10d95b231b0dcb1357c0e9fd 100644 (file)
@@ -20,7 +20,7 @@ gml_prefix_01|<Point srsName="EPSG:4326"><coordinates>1,2</coordinates></Point>
 gml_prefix_02|<Point srsName="EPSG:4326"><pos srsDimension="2">1 2</pos></Point>
 gml_prefix_03|<foo:Point srsName="EPSG:4326"><foo:coordinates>1,2</foo:coordinates></foo:Point>
 gml_prefix_04|<foo:Point srsName="EPSG:4326"><foo:pos srsDimension="2">1 2</foo:pos></foo:Point>
-ERROR:  GetProj4StringSPI: Cannot find SRID (10) in spatial_ref_sys
+ERROR:  Cannot find SRID (10) in spatial_ref_sys
 kml_srid_02|<Point><coordinates>0,1</coordinates></Point>
 kml_empty_geom|
 kml_precision_01|<Point><coordinates>1,1</coordinates></Point>
@@ -48,8 +48,8 @@ geojson_crs_01|{"type":"Point","crs":{"type":"name","properties":{"name":"EPSG:4
 geojson_crs_02|{"type":"Point","crs":{"type":"name","properties":{"name":"EPSG:4326"}},"coordinates":[1,1]}
 geojson_crs_03|{"type":"Point","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:EPSG::4326"}},"coordinates":[1,1]}
 geojson_crs_04|{"type":"Point","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:EPSG::4326"}},"coordinates":[1,1]}
-ERROR:  GetProj4StringSPI: Cannot find SRID (1) in spatial_ref_sys
-ERROR:  GetProj4StringSPI: Cannot find SRID (1) in spatial_ref_sys
+ERROR:  Cannot find SRID (1) in spatial_ref_sys
+ERROR:  Cannot find SRID (1) in spatial_ref_sys
 geojson_bbox_01|{"type":"LineString","coordinates":[[1,1],[2,2],[3,3],[4,4]]}
 geojson_bbox_02|{"type":"LineString","bbox":[1,1,4,4],"coordinates":[[1,1],[2,2],[3,3],[4,4]]}
 geojson_bbox_03|{"type":"LineString","crs":{"type":"name","properties":{"name":"EPSG:4326"}},"bbox":[1,1,4,4],"coordinates":[[1,1],[2,2],[3,3],[4,4]]}
index 6242bc67d810d7bebd923a6239d36564b92b2981..f07a786ab22a126604b9348608b757a9172cdf19 100644 (file)
@@ -31,7 +31,7 @@ gml_out_curve_02|<gml:Curve><gml:segments><gml:ArcString><gml:posList srsDimensi
 gml_out_curve_03|<gml:Polygon><gml:exterior><gml:Ring><gml:curveMember><gml:Curve><gml:segments><gml:ArcString><gml:posList srsDimension="2">-2 0 -1 -1 0 0 1 -1 2 0 0 2 -2 0</gml:posList></gml:ArcString></gml:segments></gml:Curve></gml:curveMember></gml:Ring></gml:exterior><gml:interior><gml:LinearRing><gml:posList srsDimension="2">-1 0 0 0.5 1 0 0 1 -1 0</gml:posList></gml:LinearRing></gml:interior></gml:Polygon>
 gml_out_curve_04|<gml:MultiCurve><gml:curveMember><gml:Curve><gml:segments><gml:LineStringSegment><gml:posList srsDimension="2">5 5 3 5 3 3 0 3</gml:posList></gml:LineStringSegment></gml:segments></gml:Curve></gml:curveMember><gml:curveMember><gml:Curve><gml:segments><gml:ArcString><gml:posList srsDimension="2">0 0 2 1 2 2</gml:posList></gml:ArcString></gml:segments></gml:Curve></gml:curveMember></gml:MultiCurve>
 gml_out_curve_05|<gml:MultiSurface><gml:Polygon><gml:exterior><gml:Ring><gml:curveMember><gml:Curve><gml:segments><gml:ArcString><gml:posList srsDimension="2">-2 0 -1 -1 0 0 1 -1 2 0 0 2 -2 0</gml:posList></gml:ArcString></gml:segments></gml:Curve></gml:curveMember></gml:Ring></gml:exterior><gml:interior><gml:LinearRing><gml:posList srsDimension="2">-1 0 0 0.5 1 0 0 1 -1 0</gml:posList></gml:LinearRing></gml:interior></gml:Polygon><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList srsDimension="2">7 8 10 10 6 14 4 11 7 8</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:MultiSurface>
-ERROR:  GetProj4StringSPI: Cannot find SRID (10) in spatial_ref_sys
+ERROR:  Cannot find SRID (10) in spatial_ref_sys
 ERROR:  Input geometry has unknown (0) SRID
 kml_empty_geom|
 kml_precision_01|<Point><coordinates>1,1</coordinates></Point>
index 26412c0935c8f2d1409ba9cee2a75194ea1ab741..bb2cd08e2ee335c6dbc3642f773a522739e3c332 100644 (file)
@@ -10,4 +10,4 @@ ERROR:  Input geometry has unknown (0) SRID
 9|POINT(574600 5316780)
 10|POINT(574600 5316780)
 11|SRID=100001;POINT(574600 5316780)
-ERROR:  transform_geom: couldn't parse proj4 output string: 'invalid projection': no arguments in initialization list
+ERROR:  could not parse proj string 'invalid projection'
index 8d887a981ad6b97cd2f88f695447c3f49a87ceae..5f4669e192a47bf5c2588809ad154084d1d85b44 100644 (file)
@@ -187,7 +187,7 @@ ERROR:  MultiSurface cannot contain MultiPoint element at character 8
 #852.2|1|t|t
 #852.2|2|t|t
 #1489|MULTIPOINT EMPTY|0|MULTILINESTRING EMPTY|0|MULTIPOLYGON EMPTY|0|GEOMETRYCOLLECTION EMPTY|0
-ERROR:  AddToPROJ4SRSCache: could not parse proj4 string '' 
+ERROR:  got NULL for SRID (500001)
 #1038|
 #1042|2
 #1170|90
@@ -197,7 +197,7 @@ ERROR:  AddToPROJ4SRSCache: could not parse proj4 string ''
 #1543|MULTILINESTRING((0 0,10 0,10 10,0 0),(0 0))|POLYGON((0 0,10 10,10 0,0 0))
 #1578|f|f
 #1580.1|Point[S]
-ERROR:  transform: couldn't project point (180 90 0): tolerance condition error (-20)
+ERROR:  transform: tolerance condition error (-20)
 #1580.3|Point[S]
 #1596.1|public.road_pg.roads_geom SRID:3395 TYPE:POINT DIMS:2 
 ERROR:  invalid SRID: 330000 not found in spatial_ref_sys