diff -rupN postgis-2.5.5/autogen.sh postgis-2.5.5-new/autogen.sh --- postgis-2.5.5/autogen.sh 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/autogen.sh 2022-08-06 12:13:08.260741405 +0200 @@ -81,6 +81,9 @@ ${ACLOCAL} -I macros || giveup echo "* Running ${AUTOCONF} (${AUTOCONF_VER})" ${AUTOCONF} || giveup +# Work around an autoconf bug insisting in having this file +touch build-aux/config.rpath + if test -f "${PWD}/configure"; then echo "======================================" echo "Now you are ready to run './configure'" diff -rupN postgis-2.5.5/configure.ac postgis-2.5.5-new/configure.ac --- postgis-2.5.5/configure.ac 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/configure.ac 2022-08-06 12:15:04.877744014 +0200 @@ -851,42 +851,64 @@ if test ! "x$PROJDIR" = "x"; then AC_MSG_RESULT([Using user-specified proj directory: $PROJDIR]) dnl Add the include directory to PROJ_CPPFLAGS - PROJ_CPPFLAGS="-I$PROJDIR/include -DACCEPT_USE_OF_DEPRECATED_PROJ_API_H=1" - PROJ_LDFLAGS="-L$PROJDIR/lib" + PROJ_CPPFLAGS="-I$PROJDIR/include" + PROJ_LDFLAGS="-L$PROJDIR/lib -lproj" else AC_MSG_ERROR([the --with-projdir directory "$PROJDIR" cannot be found]) fi fi +elif test ! -z "$PKG_CONFIG"; then + dnl To keep compatibility with PROJ pre 4.8, default to lproj if not found + PKG_CHECK_MODULES([PROJ], [proj], + [ + PROJ_CPPFLAGS="$PROJ_CFLAGS" + PROJ_LDFLAGS="$PROJ_LIBS" + POSTGIS_PROJ_VERSION=`$PKG_CONFIG proj --modversion | sed 's/\([[0-9]]\).*\([[0-9]]\).*\([[0-9]]\)/\1\2/'` + ], + [ + PROJ_LDFLAGS="-lproj" + ]) +else + dnl To keep compatibility with PROJ pre 4.8, default to lproj + PROJ_LDFLAGS="-lproj" 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 -]) - -dnl Return the PROJ.4 version number -AC_PROJ_VERSION([POSTGIS_PROJ_VERSION]) +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 if not detected by pkg-config +if test "x$POSTGIS_PROJ_VERSION" = "x"; then + AC_PROJ_VERSION([POSTGIS_PROJ_VERSION]) +fi AC_DEFINE_UNQUOTED([POSTGIS_PROJ_VERSION], [$POSTGIS_PROJ_VERSION], [PROJ library version]) AC_SUBST([POSTGIS_PROJ_VERSION]) CPPFLAGS="$CPPFLAGS_SAVE" +dnl Ensure that we are using PROJ >= 4.9.0 (requires pj_set_searchpath) +if test ! "$POSTGIS_PROJ_VERSION" -ge 49; then + AC_MSG_ERROR([PostGIS requires PROJ >= 4.9.0]) +fi + AC_SUBST([PROJ_CPPFLAGS]) AC_SUBST([PROJ_LDFLAGS]) -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]) -fi - dnl Ensure we can link against libproj 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_CHECK_LIB([proj], [pj_get_release], [], + [AC_CHECK_LIB([proj], [proj_info], [], + [AC_MSG_ERROR([could not find libproj - you may need to specify the directory of a PROJ installation using --with-projdir])], + [] + )], []) LIBS="$LIBS_SAVE" diff -rupN postgis-2.5.5/liblwgeom/cunit/cu_geodetic.c postgis-2.5.5-new/liblwgeom/cunit/cu_geodetic.c --- postgis-2.5.5/liblwgeom/cunit/cu_geodetic.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/cunit/cu_geodetic.c 2022-08-06 11:49:41.821709943 +0200 @@ -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 // printf("XXXXX %d\n", PJ_VERSION); a2 = lwgeom_area_spheroid(lwg, &s); CU_ASSERT_DOUBLE_EQUAL(a2, 12286884908.946891319597874, 0.1); @@ -1386,7 +1386,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 @@ -1399,7 +1399,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 @@ -1412,7 +1412,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 diff -rupN postgis-2.5.5/liblwgeom/cunit/cu_geos_cluster.c postgis-2.5.5-new/liblwgeom/cunit/cu_geos_cluster.c --- postgis-2.5.5/liblwgeom/cunit/cu_geos_cluster.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/cunit/cu_geos_cluster.c 2022-08-06 11:48:23.729708197 +0200 @@ -10,6 +10,8 @@ * **********************************************************************/ +#include + #include "CUnit/Basic.h" #include "../lwgeom_log.h" diff -rupN postgis-2.5.5/liblwgeom/g_box.c postgis-2.5.5-new/liblwgeom/g_box.c --- postgis-2.5.5/liblwgeom/g_box.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/g_box.c 2022-08-06 11:48:23.730708197 +0200 @@ -398,11 +398,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); diff -rupN postgis-2.5.5/liblwgeom/liblwgeom.h.in postgis-2.5.5-new/liblwgeom/liblwgeom.h.in --- postgis-2.5.5/liblwgeom/liblwgeom.h.in 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/liblwgeom.h.in 2022-08-06 12:19:54.803750500 +0200 @@ -32,15 +32,25 @@ #include #include #include -#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H 1 + +#include "../postgis_config.h" + +#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 defined(PJ_VERSION) && PJ_VERSION >= 490 -/* Enable new geodesic functions */ -#define PROJ_GEODESIC 1 +#if POSTGIS_PROJ_VERSION < 49 +#undef PROJ_GEODESIC #else -/* Use the old (pre-2.2) geodesic functions */ -#define PROJ_GEODESIC 0 +/* Enable new geodesic functions API */ +#define PROJ_GEODESIC #endif /** @@ -82,13 +92,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 @@ -270,16 +280,16 @@ extern lwinterrupt_callback *lwgeom_regi /******************************************************************/ 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; @@ -291,15 +301,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; @@ -313,13 +323,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; @@ -328,31 +338,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; @@ -365,14 +375,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; @@ -381,10 +391,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; @@ -396,198 +406,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; @@ -954,7 +964,7 @@ extern POINTARRAY *ptarray_addPoint(cons /** * @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); @@ -1647,8 +1657,8 @@ extern double lwgeom_length_spheroid(con 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); @@ -2003,7 +2013,7 @@ extern int gserialized_get_gbox_p(const #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 @@ -2011,14 +2021,14 @@ extern int gserialized_get_gbox_p(const */ 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; @@ -2043,20 +2053,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 /* @@ -2200,27 +2210,27 @@ LWGEOM *lwgeom_unstroke(const LWGEOM *ge * 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 { @@ -2229,7 +2239,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 @@ -2249,7 +2259,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; /** @@ -2332,6 +2342,9 @@ int lwgeom_is_simple(const LWGEOM *lwgeo * 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 * @@ -2339,14 +2352,14 @@ int lwgeom_is_simple(const LWGEOM *lwgeo */ 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); /******************************************************************************* diff -rupN postgis-2.5.5/liblwgeom/liblwgeom_internal.h postgis-2.5.5-new/liblwgeom/liblwgeom_internal.h --- postgis-2.5.5/liblwgeom/liblwgeom_internal.h 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/liblwgeom_internal.h 2022-08-06 11:48:23.733708197 +0200 @@ -38,13 +38,14 @@ #include #include #include +#include +#include +#include #if HAVE_IEEEFP_H #include #endif -#include - #include "liblwgeom.h" /** @@ -363,11 +364,6 @@ char lwcollection_same(const LWCOLLECTIO char lwcircstring_same(const LWCIRCSTRING *p1, const LWCIRCSTRING *p2); /* -* Transform -*/ -int point4d_transform(POINT4D *pt, projPJ srcpj, projPJ dstpj); - -/* * Shift */ void ptarray_longitude_shift(POINTARRAY *pa); @@ -502,5 +498,6 @@ int ptarray_npoints_in_rect(const POINTA 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 */ diff -rupN postgis-2.5.5/liblwgeom/lwgeom_transform.c postgis-2.5.5-new/liblwgeom/lwgeom_transform.c --- postgis-2.5.5/liblwgeom/lwgeom_transform.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/lwgeom_transform.c 2022-08-06 11:59:22.562722935 +0200 @@ -45,12 +45,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; @@ -58,20 +99,51 @@ ptarray_transform(POINTARRAY *pa, projPJ 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; @@ -87,7 +159,7 @@ lwgeom_transform(LWGEOM *geom, projPJ in 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: @@ -95,7 +167,7 @@ lwgeom_transform(LWGEOM *geom, projPJ in 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; } @@ -113,7 +185,7 @@ lwgeom_transform(LWGEOM *geom, projPJ in 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; } @@ -127,52 +199,287 @@ lwgeom_transform(LWGEOM *geom, projPJ in return LW_SUCCESS; } -int -point4d_transform(POINT4D *pt, projPJ srcpj, projPJ dstpj) +projPJ +lwproj_from_string(const char *str1) { - POINT3D orig_pt = {pt->x, pt->y, pt->z}; /* Copy for error report*/ + if (!str1 || str1[0] == '\0') + { + return NULL; + } + return pj_init_plus(str1); +} - if (pj_is_latlong(srcpj)) to_rad(pt) ; +/***************************************************************************/ - 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)); +#else /* POSTGIS_PROJ_VERION >= 60 */ - if (pj_transform(srcpj, dstpj, 1, 0, &(pt->x), &(pt->y), &(pt->z)) != 0) +int +lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr) +{ + PJ *pj = proj_create_crs_to_crs(NULL, instr, outstr, NULL); + if (!pj) { - int pj_errno_val = *pj_get_errno_ref(); - if (pj_errno_val == -38) + PJ *pj_in = proj_create(NULL, instr); + PJ *pj_out = proj_create(NULL, outstr); + if (!pj_in) { - 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); + lwerror("could not parse proj string '%s'", instr); } - else + if (!pj_out) { - 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); + proj_destroy(pj_in); + lwerror("could not parse proj string '%s'", outstr); } return LW_FAILURE; } - if (pj_is_latlong(dstpj)) to_dec(pt); + return lwgeom_transform(geom, pj); +} + +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; + + switch(geom->type) + { + case POINTTYPE: + case LINETYPE: + case CIRCSTRINGTYPE: + case TRIANGLETYPE: + { + LWLINE *g = (LWLINE*)geom; + if (!ptarray_transform(g->points, pj)) + return LW_FAILURE; + break; + } + case POLYGONTYPE: + { + 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; } -projPJ -lwproj_from_string(const char *str1) +static int +proj_crs_is_swapped(const PJ* pj_crs) { - if (!str1 || str1[0] == '\0') + PJ *pj_cs; + int rv = LW_FALSE; + + if (proj_get_type(pj_crs) == PJ_TYPE_COMPOUND_CRS) { - return NULL; + 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); } - return pj_init_plus(str1); + 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 (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; } +#if 0 +int +point4d_transform(POINT4D *pt, PJ* pj) +{ + 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) + { + 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; + } + + 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 diff -rupN postgis-2.5.5/liblwgeom/lwin_encoded_polyline.c postgis-2.5.5-new/liblwgeom/lwin_encoded_polyline.c --- postgis-2.5.5/liblwgeom/lwin_encoded_polyline.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/lwin_encoded_polyline.c 2022-08-06 11:48:23.734708197 +0200 @@ -25,6 +25,8 @@ #include #include +#include + #include "liblwgeom.h" #include "../postgis_config.h" diff -rupN postgis-2.5.5/liblwgeom/lwspheroid.c postgis-2.5.5-new/liblwgeom/lwspheroid.c --- postgis-2.5.5/liblwgeom/lwspheroid.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/lwspheroid.c 2022-08-06 11:48:23.735708197 +0200 @@ -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 #endif @@ -45,7 +45,7 @@ void spheroid_init(SPHEROID *s, double a 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(cons return fabs(area); } -/* Above use GeographicLib */ +/* Above use Proj GeographicLib */ #else /* ! PROJ_GEODESIC */ /* Below use pre-version 2.2 geodesic functions */ diff -rupN postgis-2.5.5/liblwgeom/lwunionfind.c postgis-2.5.5-new/liblwgeom/lwunionfind.c --- postgis-2.5.5/liblwgeom/lwunionfind.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/lwunionfind.c 2022-08-06 11:48:23.735708197 +0200 @@ -26,6 +26,7 @@ #include "liblwgeom.h" #include "lwunionfind.h" #include +#include static int cmp_int(const void *a, const void *b); static int cmp_int_ptr(const void *a, const void *b); diff -rupN postgis-2.5.5/liblwgeom/lwutil.c postgis-2.5.5-new/liblwgeom/lwutil.c --- postgis-2.5.5/liblwgeom/lwutil.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/lwutil.c 2022-08-06 11:48:23.735708197 +0200 @@ -246,6 +246,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 diff -rupN postgis-2.5.5/liblwgeom/ptarray.c postgis-2.5.5-new/liblwgeom/ptarray.c --- postgis-2.5.5/liblwgeom/ptarray.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/liblwgeom/ptarray.c 2022-08-06 11:48:23.736708197 +0200 @@ -26,6 +26,7 @@ #include #include +#include #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 4*/ @@ -390,8 +391,8 @@ ptarray_swap_ordinates(POINTARRAY *pa, L 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); diff -rupN postgis-2.5.5/libpgcommon/lwgeom_cache.c postgis-2.5.5-new/libpgcommon/lwgeom_cache.c --- postgis-2.5.5/libpgcommon/lwgeom_cache.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/libpgcommon/lwgeom_cache.c 2022-08-06 11:48:23.737708197 +0200 @@ -77,35 +77,36 @@ GetGenericCacheCollection(FunctionCallIn /** -* 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; diff -rupN postgis-2.5.5/libpgcommon/lwgeom_cache.h postgis-2.5.5-new/libpgcommon/lwgeom_cache.h --- postgis-2.5.5/libpgcommon/lwgeom_cache.h 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/libpgcommon/lwgeom_cache.h 2022-08-06 11:48:23.737708197 +0200 @@ -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, diff -rupN postgis-2.5.5/libpgcommon/lwgeom_transform.c postgis-2.5.5-new/libpgcommon/lwgeom_transform.c --- postgis-2.5.5/libpgcommon/lwgeom_transform.c 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/libpgcommon/lwgeom_transform.c 2022-08-06 12:22:51.237754447 +0200 @@ -34,37 +34,51 @@ #include +/** +* Global variable to hold cached information about what +* schema functions are installed in. Currently used by +* 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; @@ -73,42 +87,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); @@ -117,7 +181,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() @@ -126,7 +190,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 @@ -135,7 +199,7 @@ PROJ4SRSCacheReset(MemoryContext context } static bool -PROJ4SRSCacheIsEmpty(MemoryContext context) +PROJSRSCacheIsEmpty(MemoryContext context) { /* * Always return false since this call is mandatory according to tgl @@ -145,19 +209,19 @@ PROJ4SRSCacheIsEmpty(MemoryContext conte } 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 @@ -167,26 +231,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 */ @@ -213,10 +277,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; @@ -225,7 +289,7 @@ static void AddPJHashEntry(MemoryContext /* 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 */ @@ -234,12 +298,12 @@ static void AddPJHashEntry(MemoryContext } 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; @@ -266,118 +330,140 @@ static void DeletePJHashEntry(MemoryCont 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"); } - static char *proj_str_tmpl = - "SELECT proj4text, auth_name, auth_srid, srtext " - "FROM %s " - "WHERE srid = %d " - "LIMIT 1"; - snprintf(proj4_spi_buffer, 255, proj_str_tmpl, postgis_spatial_ref_sys(), srid); + /* + * This global is allocated in CacheMemoryContext (lifespan of this backend) + * and is set by SetSpatialRefSysSchema the first time + * 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) + { + snprintf(proj_spi_buffer, spibufferlen, proj_str_tmpl, spatialRefSysSchema, ".", srid); + } + else + { + 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 ) - { - /* Make a projection object out of it */ - strncpy(proj_str, proj4text, maxproj4len - 1); - } - else - { - proj_str[0] = 0; - } + /* 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)) + { + 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; } @@ -387,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. */ @@ -434,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 ) - { - elog(ERROR, "GetProj4String returned NULL for SRID (%d)", srid); - } + 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); - projection = lwproj_from_string(proj_str); - if ( projection == NULL ) - { - char *pj_errstr = pj_strerrno(*pj_get_errno_ref()); - if ( ! pj_errstr ) - pj_errstr = ""; + 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++) + { + 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; + } + if (!projection) + { 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); - - DeleteFromPROJ4SRSCache(PROJ4Cache, PROJ4Cache->PROJ4SRSCache[i].srid); - PROJ4Cache->PROJ4SRSCacheCount = 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); + + DeleteFromPROJSRSCache(PROJCache, + PROJCache->PROJSRSCache[i].srid_from, + PROJCache->PROJSRSCache[i].srid_to); + PROJCache->PROJSRSCacheCount = i; found = true; } } @@ -539,16 +716,21 @@ AddToPROJ4SRSCache(PROJ4PortalCache *PRO * 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 */ @@ -557,7 +739,7 @@ AddToPROJ4SRSCache(PROJ4PortalCache *PRO { MemoryContextCallback *callback = MemoryContextAlloc(PJMemoryContext, sizeof(MemoryContextCallback)); callback->arg = (void*)PJMemoryContext; - callback->func = PROJ4SRSCacheDelete; + callback->func = PROJSRSCacheDelete; MemoryContextRegisterResetCallback(PJMemoryContext, callback); } #endif @@ -574,43 +756,37 @@ AddToPROJ4SRSCache(PROJ4PortalCache *PRO 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; } } } @@ -628,13 +804,13 @@ static void DeleteFromPROJ4SRSCache(PROJ * 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 @@ -649,91 +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); -} 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 */ - postgis_initialize_cache(fcinfo); + 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, ( @@ -741,14 +900,10 @@ void srid_is_latlong(FunctionCallInfo fc 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; @@ -756,10 +911,7 @@ srs_precision srid_axis_precision(Functi 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; @@ -767,3 +919,59 @@ srs_precision srid_axis_precision(Functi 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; +} diff -rupN postgis-2.5.5/libpgcommon/lwgeom_transform.h postgis-2.5.5-new/libpgcommon/lwgeom_transform.h --- postgis-2.5.5/libpgcommon/lwgeom_transform.h 2020-08-15 20:43:43.000000000 +0200 +++ postgis-2.5.5-new/libpgcommon/lwgeom_transform.h 2022-08-06 11:48:23.739708197 +0200 @@ -10,9 +10,10 @@ **********************************************************************/ #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 ; +typedef void *ProjCache ; -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); +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); /** diff -rupN postgis-2.5.5/macros/ac_proj4_version.m4 postgis-2.5.5-new/macros/ac_proj4_version.m4 --- postgis-2.5.5/macros/ac_proj4_version.m4 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/macros/ac_proj4_version.m4 2022-08-06 11:48:23.740708197 +0200 @@ -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 - #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 + #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 + #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="" + ]) + ]) ]) diff -rupN postgis-2.5.5/postgis/geography_inout.c postgis-2.5.5-new/postgis/geography_inout.c --- postgis-2.5.5/postgis/geography_inout.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/geography_inout.c 2022-08-06 11:48:23.740708197 +0200 @@ -172,7 +172,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); @@ -568,7 +568,7 @@ Datum geography_from_text(PG_FUNCTION_AR 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); @@ -596,7 +596,7 @@ Datum geography_from_binary(PG_FUNCTION_ 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); @@ -622,7 +622,7 @@ Datum geography_from_geometry(PG_FUNCTIO } /* 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); @@ -686,7 +686,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); diff -rupN postgis-2.5.5/postgis/geography_measurement.c postgis-2.5.5-new/postgis/geography_measurement.c --- postgis-2.5.5/postgis/geography_measurement.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/geography_measurement.c 2022-08-06 11:48:23.741708197 +0200 @@ -40,7 +40,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 @@ -532,7 +532,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 ) { @@ -543,7 +543,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 ) diff -rupN postgis-2.5.5/postgis/gserialized_gist_2d.c postgis-2.5.5-new/postgis/gserialized_gist_2d.c --- postgis-2.5.5/postgis/gserialized_gist_2d.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/gserialized_gist_2d.c 2022-08-06 11:48:23.742708197 +0200 @@ -60,6 +60,7 @@ #endif #include /* For FLT_MAX */ +#include /* ** When is a node split not so good? If more than 90% of the entries diff -rupN postgis-2.5.5/postgis/gserialized_gist_nd.c postgis-2.5.5-new/postgis/gserialized_gist_nd.c --- postgis-2.5.5/postgis/gserialized_gist_nd.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/gserialized_gist_nd.c 2022-08-06 11:48:23.743708197 +0200 @@ -52,6 +52,7 @@ #include "geography.h" #include +#include /* Fall back to older finite() if necessary */ diff -rupN postgis-2.5.5/postgis/gserialized_spgist_2d.c postgis-2.5.5-new/postgis/gserialized_spgist_2d.c --- postgis-2.5.5/postgis/gserialized_spgist_2d.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/gserialized_spgist_2d.c 2022-08-06 11:48:23.744708197 +0200 @@ -81,6 +81,7 @@ #include "lwgeom_pg.h" /* For debugging macros. */ #include /* For utility functions. */ #include /* For debugging macros. */ +#include /* ** SP-GiST 2D index function prototypes diff -rupN postgis-2.5.5/postgis/gserialized_typmod.c postgis-2.5.5-new/postgis/gserialized_typmod.c --- postgis-2.5.5/postgis/gserialized_typmod.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/gserialized_typmod.c 2022-08-06 11:48:23.744708197 +0200 @@ -295,7 +295,7 @@ Datum geography_typmod_in(PG_FUNCTION_AR 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); } diff -rupN postgis-2.5.5/postgis/lwgeom_in_gml.c postgis-2.5.5-new/postgis/lwgeom_in_gml.c --- postgis-2.5.5/postgis/lwgeom_in_gml.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/lwgeom_in_gml.c 2022-08-06 11:48:23.745708197 +0200 @@ -297,36 +297,68 @@ static xmlNodePtr get_xlink_node(xmlNode } + /** - * 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 */ /** diff -rupN postgis-2.5.5/postgis/lwgeom_spheroid.c postgis-2.5.5-new/postgis/lwgeom_spheroid.c --- postgis-2.5.5/postgis/lwgeom_spheroid.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/lwgeom_spheroid.c 2022-08-06 11:48:23.745708197 +0200 @@ -440,7 +440,7 @@ Datum LWGEOM_length_ellipsoid_linestring * (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. * diff -rupN postgis-2.5.5/postgis/lwgeom_transform.c postgis-2.5.5-new/postgis/lwgeom_transform.c --- postgis-2.5.5/postgis/lwgeom_transform.c 2020-08-15 20:43:44.000000000 +0200 +++ postgis-2.5.5-new/postgis/lwgeom_transform.c 2022-08-06 11:48:23.746708197 +0200 @@ -47,23 +47,23 @@ Datum postgis_proj_version(PG_FUNCTION_A 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; + GSERIALIZED* geom; + GSERIALIZED* result=NULL; + LWGEOM* lwgeom; + PJ* pj; + int32 srid_to, srid_from; - output_srid = PG_GETARG_INT32(1); - if (output_srid == SRID_UNKNOWN) + 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); @@ -71,10 +71,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."); @@ -83,8 +83,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 ) @@ -110,96 +110,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); - } + geom->srid = result_srid; + if (geom->bbox) + lwgeom_refresh_bbox(geom); + + gser_result = geometry_serialize(geom); + lwgeom_free(geom); + PG_FREE_IF_COPY(gser, 0); - result = geometry_serialize(lwgeom); - - lwgeom_free(lwgeom); - PG_FREE_IF_COPY(geom, 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); }