[postgis-tickets] [SCM] PostGIS branch master updated. 3.2.0-849-g368aeeccd

git at osgeo.org git at osgeo.org
Sun May 15 10:24:11 PDT 2022


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".

The branch, master has been updated
       via  368aeeccd07735249a81cf0b9657768fd87d4f7a (commit)
       via  94a1cdb860b90930672ae55168fbcf70e26c3513 (commit)
       via  ea85a830559e30d4b165e4789a1623ac06ee8adf (commit)
       via  2e4cd52c10605343365054efb782de99a754071a (commit)
       via  1c0d69cf98b27902b385a27276124815547eed03 (commit)
       via  fc13e8b7a3f3a36b55c745de0a175d97381702e0 (commit)
       via  508eae766759df7ec7fc25e9ae864cba827cd02c (commit)
       via  6a4155543b4b051c13bc8d34ff4f620f91a63af6 (commit)
       via  c3b12f011c8034d2016f4999d293a266f02dd206 (commit)
      from  fe5a38407b4a228903d6141476c55dd9444c59e9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 368aeeccd07735249a81cf0b9657768fd87d4f7a
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Sun May 15 20:23:40 2022 +0300

    NEWS entry
    
    Closes #5148

diff --git a/NEWS b/NEWS
index b143d9bf6..7f50c0499 100644
--- a/NEWS
+++ b/NEWS
@@ -45,6 +45,8 @@ PostGIS 3.3.0dev
     to include for PG15+ (Regina Obe)
    - #5088, Memory corruption in mvt_agg_transfn (Victor Collod)
    - #5137, resetting interrupt flags before query execution (Sergei Shoulbakov)
+   - #5148, ST_Clip is more robust to alignment of raster and clip geometry (Sergei Shoulbakov)
+
 
 PostGIS 3.2.0 (Olivier Courtin Edition)
 2021/12/17

commit 94a1cdb860b90930672ae55168fbcf70e26c3513
Merge: fe5a38407 ea85a8305
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Sun May 15 20:21:31 2022 +0300

    Merge remote-tracking branch 'gh/pr/668/merge'


commit ea85a830559e30d4b165e4789a1623ac06ee8adf
Merge: 322680984 2e4cd52c1
Author: Paul Ramsey <pramsey+github at cleverelephant.ca>
Date:   Fri May 13 01:27:17 2022 +0200

    Merge 2e4cd52c10605343365054efb782de99a754071a into 32268098405b097431a74be2963317f6585df56a


commit 2e4cd52c10605343365054efb782de99a754071a
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed Feb 23 11:21:16 2022 -0800

    Add IAU_2015 to default list

diff --git a/postgis/lwgeom_transform.c b/postgis/lwgeom_transform.c
index fa272d58d..1154f4eb3 100644
--- a/postgis/lwgeom_transform.c
+++ b/postgis/lwgeom_transform.c
@@ -335,6 +335,7 @@ srs_state_codes(const char* auth_name, struct srs_data *state)
 		PROJ_STRING_LIST codes = codes_ptr;
 		const char *code;
 		while(codes && *codes) {
+			/* Read current code and move forward one entry */
 			code = *codes++;
 			/* Ensure there is space in the entry list */
 			srs_state_memcheck(state);
@@ -417,6 +418,7 @@ Datum postgis_srs_entry_all(PG_FUNCTION_ARGS)
 		state = srs_state_init();
 		srs_state_codes("EPSG", state);
 		srs_state_codes("ESRI", state);
+		srs_state_codes("IAU_2015", state);
 
 		/*
 		* Read the TupleDesc from the FunctionCallInfo. The SQL definition

commit 1c0d69cf98b27902b385a27276124815547eed03
Merge: fc13e8b7a c3b12f011
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed Feb 23 10:46:03 2022 -0800

    merge

diff --cc postgis/lwgeom_transform.c
index be9ddb40e,0f05307ee..fa272d58d
--- a/postgis/lwgeom_transform.c
+++ b/postgis/lwgeom_transform.c
@@@ -296,60 -296,6 +296,59 @@@ srs_tuple_from_entry(const struct srs_e
  	return HeapTupleGetDatum(tuple);
  }
  
- 
 +static struct srs_data *
 +srs_state_init()
 +{
 +	struct srs_data *state = palloc0(sizeof(*state));
 +	state->capacity = 8192;
 +	state->num_entries = 0;
 +	state->entries = palloc0(state->capacity * sizeof(*(state->entries)));
 +	return state;
 +}
 +
 +static void
 +srs_state_memcheck(struct srs_data *state)
 +{
 +	if (state->num_entries == state->capacity) {
 +		state->capacity *= 2;
 +		state->entries = repalloc(state->entries, state->capacity * sizeof(*(state->entries)));
 +	}
 +	return;
 +}
 +
 +static void
 +srs_state_codes(const char* auth_name, struct srs_data *state)
 +{
 +	/*
 +	* Only a subset of supported proj types actually
 +	* show up in spatial_ref_sys
 +	*/
 +	#define ntypes 3
 +	PJ_TYPE types[ntypes] = {PJ_TYPE_PROJECTED_CRS, PJ_TYPE_GEOGRAPHIC_CRS, PJ_TYPE_COMPOUND_CRS};
 +	uint32_t j;
 +
 +	for (j = 0; j < ntypes; j++) {
 +		PJ_CONTEXT *ctx = NULL;
 +		int allow_deprecated = 0;
 +		PJ_TYPE type = types[j];
 +		PROJ_STRING_LIST codes_ptr = proj_get_codes_from_database(ctx, auth_name, type, allow_deprecated);
 +		PROJ_STRING_LIST codes = codes_ptr;
 +		const char *code;
 +		while(codes && *codes) {
 +			code = *codes++;
 +			/* Ensure there is space in the entry list */
 +			srs_state_memcheck(state);
 +
 +			/* Write the entry into the entry list and increment */
 +			state->entries[state->num_entries].auth_name = cstring_to_text(auth_name);
 +			state->entries[state->num_entries].auth_code = cstring_to_text(code);
 +			state->num_entries++;
 +		}
 +		/* Clean up system allocated memory */
 +		proj_string_list_destroy(codes_ptr);
 +	}
 +}
 +
  #endif
  
  /**

commit fc13e8b7a3f3a36b55c745de0a175d97381702e0
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed Feb 23 10:42:51 2022 -0800

    Add access to codes via postgis_spatial_ref_sys(auth_name) and factor out some common code into helpers

diff --git a/postgis/lwgeom_transform.c b/postgis/lwgeom_transform.c
index 0f05307ee..be9ddb40e 100644
--- a/postgis/lwgeom_transform.c
+++ b/postgis/lwgeom_transform.c
@@ -296,6 +296,60 @@ srs_tuple_from_entry(const struct srs_entry* entry, TupleDesc tuple_desc)
 	return HeapTupleGetDatum(tuple);
 }
 
+
+static struct srs_data *
+srs_state_init()
+{
+	struct srs_data *state = palloc0(sizeof(*state));
+	state->capacity = 8192;
+	state->num_entries = 0;
+	state->entries = palloc0(state->capacity * sizeof(*(state->entries)));
+	return state;
+}
+
+static void
+srs_state_memcheck(struct srs_data *state)
+{
+	if (state->num_entries == state->capacity) {
+		state->capacity *= 2;
+		state->entries = repalloc(state->entries, state->capacity * sizeof(*(state->entries)));
+	}
+	return;
+}
+
+static void
+srs_state_codes(const char* auth_name, struct srs_data *state)
+{
+	/*
+	* Only a subset of supported proj types actually
+	* show up in spatial_ref_sys
+	*/
+	#define ntypes 3
+	PJ_TYPE types[ntypes] = {PJ_TYPE_PROJECTED_CRS, PJ_TYPE_GEOGRAPHIC_CRS, PJ_TYPE_COMPOUND_CRS};
+	uint32_t j;
+
+	for (j = 0; j < ntypes; j++) {
+		PJ_CONTEXT *ctx = NULL;
+		int allow_deprecated = 0;
+		PJ_TYPE type = types[j];
+		PROJ_STRING_LIST codes_ptr = proj_get_codes_from_database(ctx, auth_name, type, allow_deprecated);
+		PROJ_STRING_LIST codes = codes_ptr;
+		const char *code;
+		while(codes && *codes) {
+			code = *codes++;
+			/* Ensure there is space in the entry list */
+			srs_state_memcheck(state);
+
+			/* Write the entry into the entry list and increment */
+			state->entries[state->num_entries].auth_name = cstring_to_text(auth_name);
+			state->entries[state->num_entries].auth_code = cstring_to_text(code);
+			state->num_entries++;
+		}
+		/* Clean up system allocated memory */
+		proj_string_list_destroy(codes_ptr);
+	}
+}
+
 #endif
 
 /**
@@ -306,8 +360,8 @@ Datum postgis_srs_entry(PG_FUNCTION_ARGS);
 PG_FUNCTION_INFO_V1(postgis_srs_entry);
 Datum postgis_srs_entry(PG_FUNCTION_ARGS)
 {
-#if POSTGIS_PROJ_VERSION < 61
-	elog(ERROR, "%s is not supported with Proj < 6.1", __func__);
+#if POSTGIS_PROJ_VERSION < 60
+	elog(ERROR, "%s is not supported with Proj < 6.0", __func__);
 #else
 	Datum result;
 	struct srs_entry entry;
@@ -337,8 +391,8 @@ Datum postgis_srs_entry_all(PG_FUNCTION_ARGS);
 PG_FUNCTION_INFO_V1(postgis_srs_entry_all);
 Datum postgis_srs_entry_all(PG_FUNCTION_ARGS)
 {
-#if POSTGIS_PROJ_VERSION < 61
-	elog(ERROR, "%s is not supported with Proj < 6.1", __func__);
+#if POSTGIS_PROJ_VERSION < 60
+	elog(ERROR, "%s is not supported with Proj < 6.0", __func__);
 #else
 	FuncCallContext *funcctx;
 	MemoryContext oldcontext;
@@ -352,55 +406,18 @@ Datum postgis_srs_entry_all(PG_FUNCTION_ARGS)
 	* one isolated call per pair.
 	*/
 	if (SRF_IS_FIRSTCALL()) {
-		uint32_t i, j;
-		/*
-		* Only a subset of supported proj types actually
-		* show up in spatial_ref_sys
-		*/
-		#define ntypes 3
-		PJ_TYPE types[ntypes] = {PJ_TYPE_PROJECTED_CRS, PJ_TYPE_GEOGRAPHIC_CRS, PJ_TYPE_COMPOUND_CRS};
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
 		/*
 		* Could read all authorities from database, but includes
 		* authorities (IGN, OGC) that use non-integral values in
 		* auth_srid. So hand-coded list for now.
 		*/
-		#define nauths 2
-		const char* auth_names[nauths] = {"EPSG", "ESRI"};
-
-		funcctx = SRF_FIRSTCALL_INIT();
-		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
-		state = palloc0(sizeof(*state));
-		state->capacity = 8192;
-		state->num_entries = 0;
-		state->entries = palloc0(state->capacity * sizeof(*(state->entries)));
-
-		for (i = 0; i < nauths; i++) {
-			const char* auth_name = auth_names[i];
-			for (j = 0; j < ntypes; j++) {
-				PJ_CONTEXT *ctx = NULL;
-				int allow_deprecated = 0;
-				PJ_TYPE type = types[j];
-				PROJ_STRING_LIST codes_ptr = proj_get_codes_from_database(ctx, auth_name, type, allow_deprecated);
-				PROJ_STRING_LIST codes = codes_ptr;
-				const char *code;
-				while(codes && *codes) {
-					code = *codes++;
-					/* Ensure there is space in the entry list */
-					if (state->num_entries == state->capacity) {
-						state->capacity *= 2;
-						state->entries = repalloc(state->entries, state->capacity * sizeof(*(state->entries)));
-					}
-					/* Write the entry into the entry list and increment */
-					state->entries[state->num_entries].auth_name = cstring_to_text(auth_name);
-					state->entries[state->num_entries].auth_code = cstring_to_text(code);
-					state->num_entries++;
-				}
-				/* Clean up system allocated memory */
-				proj_string_list_destroy(codes_ptr);
-			}
-		}
+		state = srs_state_init();
+		srs_state_codes("EPSG", state);
+		srs_state_codes("ESRI", state);
 
 		/*
 		* Read the TupleDesc from the FunctionCallInfo. The SQL definition
@@ -438,3 +455,60 @@ Datum postgis_srs_entry_all(PG_FUNCTION_ARGS)
 	SRF_RETURN_DONE(funcctx);
 #endif
 }
+
+
+Datum postgis_srs_codes(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(postgis_srs_codes);
+Datum postgis_srs_codes(PG_FUNCTION_ARGS)
+{
+#if POSTGIS_PROJ_VERSION < 60
+	elog(ERROR, "%s is not supported with Proj < 6.0", __func__);
+#else
+	FuncCallContext *funcctx;
+	MemoryContext oldcontext;
+	struct srs_data *state;
+	Datum result;
+	text* auth_name = PG_GETARG_TEXT_P(0);
+	text* auth_code;
+
+	/*
+	* On the first call, fill in the state with all
+	* of the auth_name/auth_srid pairings in the
+	* proj database. Then the per-call routine is just
+	* one isolated call per pair.
+	*/
+	if (SRF_IS_FIRSTCALL()) {
+		/*
+		* Only a subset of supported proj types actually
+		* show up in spatial_ref_sys
+		*/
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+		state = srs_state_init();
+		srs_state_codes(text_to_cstring(auth_name), state);
+		funcctx->user_fctx = state;
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* Stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+	state = funcctx->user_fctx;
+
+	/* Exit when we've read all entries */
+	if (!state->num_entries || state->current_entry == state->num_entries) {
+		SRF_RETURN_DONE(funcctx);
+	}
+
+	/* Read the code for this entry */
+	auth_code = state->entries[state->current_entry++].auth_code;
+	result = PointerGetDatum(auth_code);
+
+	/* We are returning setof(text) */
+	if (result)
+		SRF_RETURN_NEXT(funcctx, result);
+
+	/* Stop if lookup fails drastically */
+	SRF_RETURN_DONE(funcctx);
+#endif
+}
+
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 852087821..9fd6fabf2 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -2859,14 +2859,20 @@ CREATE OR REPLACE FUNCTION ST_Transform(geom geometry, from_proj text, to_srid i
 	LANGUAGE 'sql' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_HIGH;
 
-CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys(auth_name_in text, auth_srid_in text, OUT auth_name text, OUT auth_srid TEXT, OUT srtext TEXT, OUT proj4text TEXT)
-	RETURNS RECORD
+CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys(auth_name text)
+	RETURNS SETOF TEXT
+	AS 'MODULE_PATHNAME', 'postgis_srs_codes'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_MEDIUM;
+
+CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys(auth_name text, auth_srid text)
+	RETURNS TABLE(auth_name text, auth_srid TEXT, srtext TEXT, proj4text TEXT)
 	AS 'MODULE_PATHNAME', 'postgis_srs_entry'
 	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_MEDIUM;
 
-CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys(OUT auth_name text, OUT auth_srid TEXT, OUT srtext TEXT, OUT proj4text TEXT)
-	RETURNS SETOF RECORD
+CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys()
+	RETURNS TABLE(auth_name text, auth_srid TEXT, srtext TEXT, proj4text TEXT)
 	AS 'MODULE_PATHNAME', 'postgis_srs_entry_all'
 	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_HIGH;

commit 508eae766759df7ec7fc25e9ae864cba827cd02c
Merge: 6a4155543 c4380f19d
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed Feb 23 10:09:19 2022 -0800

    Merge branch 'master' of https://git.osgeo.org/gitea/postgis/postgis into master-proj-srs


commit 6a4155543b4b051c13bc8d34ff4f620f91a63af6
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Fri Feb 18 14:29:28 2022 -0800

    Direct access to Proj data via functions

diff --git a/postgis/lwgeom_transform.c b/postgis/lwgeom_transform.c
index 758e7996c..0f05307ee 100644
--- a/postgis/lwgeom_transform.c
+++ b/postgis/lwgeom_transform.c
@@ -26,6 +26,7 @@
 #include "postgres.h"
 #include "fmgr.h"
 #include "utils/builtins.h"
+#include "funcapi.h"
 
 #include "../postgis_config.h"
 #include "liblwgeom.h"
@@ -225,3 +226,215 @@ Datum LWGEOM_asKML(PG_FUNCTION_ARGS)
 		PG_RETURN_TEXT_P(kml);
 	PG_RETURN_NULL();
 }
+
+
+/********************************************************************************
+ * PROJ database reading functions
+ */
+
+
+#if POSTGIS_PROJ_VERSION >= 61
+
+struct srs_entry {
+	text* auth_name;
+	text* auth_code;
+};
+
+struct srs_data {
+	struct srs_entry* entries;
+	uint32_t num_entries;
+	uint32_t capacity;
+	uint32_t current_entry;
+};
+
+static Datum
+srs_tuple_from_entry(const struct srs_entry* entry, TupleDesc tuple_desc)
+{
+	HeapTuple tuple;
+	Datum tuple_data[4] = {0, 0, 0, 0};
+	bool  tuple_null[4] = {false, false, false, false};
+	PJ_CONTEXT *ctx = NULL;
+	const char* const empty_options[2] = {NULL};
+	const char* const wkt_options[2] = {"MULTILINE=NO", NULL};
+	const char *srtext;
+	const char *proj4text;
+
+	PJ *obj = proj_create_from_database(ctx,
+		text_to_cstring(entry->auth_name),
+		text_to_cstring(entry->auth_code),
+		PJ_CATEGORY_CRS, 0, empty_options);
+
+	if (!obj)
+		return (Datum) 0;
+
+	srtext = proj_as_wkt(ctx, obj, PJ_WKT1_GDAL, wkt_options);
+	proj4text = proj_as_proj_string(ctx, obj, PJ_PROJ_4, empty_options);
+
+	if (entry->auth_name)
+		tuple_data[0] = PointerGetDatum(entry->auth_name);
+	else
+		tuple_null[0] = true;
+
+	if (entry->auth_code)
+		tuple_data[1] = PointerGetDatum(entry->auth_code);
+	else
+		tuple_null[1] = true;
+
+	if (srtext)
+		tuple_data[2] = PointerGetDatum(cstring_to_text(srtext));
+	else
+		tuple_null[2] = true;
+
+	if (proj4text)
+		tuple_data[3] = PointerGetDatum(cstring_to_text(proj4text));
+	else
+		tuple_null[3] = true;
+
+	tuple = heap_form_tuple(tuple_desc, tuple_data, tuple_null);
+	proj_destroy(obj);
+
+	return HeapTupleGetDatum(tuple);
+}
+
+#endif
+
+/**
+ * Search for srtext and proj4text given auth_name and auth_srid,
+ * returns TABLE(auth_name text, auth_srid text, srtext text, proj4text text)
+ */
+Datum postgis_srs_entry(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(postgis_srs_entry);
+Datum postgis_srs_entry(PG_FUNCTION_ARGS)
+{
+#if POSTGIS_PROJ_VERSION < 61
+	elog(ERROR, "%s is not supported with Proj < 6.1", __func__);
+#else
+	Datum result;
+	struct srs_entry entry;
+	text* auth_name = PG_GETARG_TEXT_P(0);
+	text* auth_code = PG_GETARG_TEXT_P(1);
+	TupleDesc tuple_desc;
+
+	if (get_call_result_type(fcinfo, 0, &tuple_desc) != TYPEFUNC_COMPOSITE) {
+		ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("%s called with incompatible return type", __func__)));
+	}
+	BlessTupleDesc(tuple_desc);
+
+	entry.auth_name = auth_name;
+	entry.auth_code = auth_code;
+	result = srs_tuple_from_entry(&entry, tuple_desc);
+
+	if (result)
+		PG_RETURN_DATUM(srs_tuple_from_entry(&entry, tuple_desc));
+	else
+		PG_RETURN_NULL();
+#endif
+}
+
+
+Datum postgis_srs_entry_all(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(postgis_srs_entry_all);
+Datum postgis_srs_entry_all(PG_FUNCTION_ARGS)
+{
+#if POSTGIS_PROJ_VERSION < 61
+	elog(ERROR, "%s is not supported with Proj < 6.1", __func__);
+#else
+	FuncCallContext *funcctx;
+	MemoryContext oldcontext;
+	struct srs_data *state;
+	Datum result;
+
+	/*
+	* On the first call, fill in the state with all
+	* of the auth_name/auth_srid pairings in the
+	* proj database. Then the per-call routine is just
+	* one isolated call per pair.
+	*/
+	if (SRF_IS_FIRSTCALL()) {
+		uint32_t i, j;
+		/*
+		* Only a subset of supported proj types actually
+		* show up in spatial_ref_sys
+		*/
+		#define ntypes 3
+		PJ_TYPE types[ntypes] = {PJ_TYPE_PROJECTED_CRS, PJ_TYPE_GEOGRAPHIC_CRS, PJ_TYPE_COMPOUND_CRS};
+
+		/*
+		* Could read all authorities from database, but includes
+		* authorities (IGN, OGC) that use non-integral values in
+		* auth_srid. So hand-coded list for now.
+		*/
+		#define nauths 2
+		const char* auth_names[nauths] = {"EPSG", "ESRI"};
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc0(sizeof(*state));
+		state->capacity = 8192;
+		state->num_entries = 0;
+		state->entries = palloc0(state->capacity * sizeof(*(state->entries)));
+
+		for (i = 0; i < nauths; i++) {
+			const char* auth_name = auth_names[i];
+			for (j = 0; j < ntypes; j++) {
+				PJ_CONTEXT *ctx = NULL;
+				int allow_deprecated = 0;
+				PJ_TYPE type = types[j];
+				PROJ_STRING_LIST codes_ptr = proj_get_codes_from_database(ctx, auth_name, type, allow_deprecated);
+				PROJ_STRING_LIST codes = codes_ptr;
+				const char *code;
+				while(codes && *codes) {
+					code = *codes++;
+					/* Ensure there is space in the entry list */
+					if (state->num_entries == state->capacity) {
+						state->capacity *= 2;
+						state->entries = repalloc(state->entries, state->capacity * sizeof(*(state->entries)));
+					}
+					/* Write the entry into the entry list and increment */
+					state->entries[state->num_entries].auth_name = cstring_to_text(auth_name);
+					state->entries[state->num_entries].auth_code = cstring_to_text(code);
+					state->num_entries++;
+				}
+				/* Clean up system allocated memory */
+				proj_string_list_destroy(codes_ptr);
+			}
+		}
+
+		/*
+		* Read the TupleDesc from the FunctionCallInfo. The SQL definition
+		* of the function must have the right number of fields and types
+		* to match up to this C code.
+		*/
+		if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE) {
+			ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("%s called with incompatible return type", __func__)));
+		}
+
+		BlessTupleDesc(funcctx->tuple_desc);
+		funcctx->user_fctx = state;
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* Stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+	state = funcctx->user_fctx;
+
+	/* Exit when we've read all entries */
+	if (!state->num_entries || state->current_entry == state->num_entries) {
+		SRF_RETURN_DONE(funcctx);
+	}
+
+	/* Lookup the srtext/proj4text for this entry */
+	result = srs_tuple_from_entry(
+		state->entries + state->current_entry++,
+		funcctx->tuple_desc);
+
+	if (result)
+		SRF_RETURN_NEXT(funcctx, result);
+
+	/* Stop if lookup fails drastically */
+	SRF_RETURN_DONE(funcctx);
+#endif
+}
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index a66f82370..852087821 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -2859,6 +2859,18 @@ CREATE OR REPLACE FUNCTION ST_Transform(geom geometry, from_proj text, to_srid i
 	LANGUAGE 'sql' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_HIGH;
 
+CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys(auth_name_in text, auth_srid_in text, OUT auth_name text, OUT auth_srid TEXT, OUT srtext TEXT, OUT proj4text TEXT)
+	RETURNS RECORD
+	AS 'MODULE_PATHNAME', 'postgis_srs_entry'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_MEDIUM;
+
+CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys(OUT auth_name text, OUT auth_srid TEXT, OUT srtext TEXT, OUT proj4text TEXT)
+	RETURNS SETOF RECORD
+	AS 'MODULE_PATHNAME', 'postgis_srs_entry_all'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_HIGH;
+
 -----------------------------------------------------------------------
 -- POSTGIS_VERSION()
 -----------------------------------------------------------------------

commit c3b12f011c8034d2016f4999d293a266f02dd206
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Fri Feb 18 14:29:28 2022 -0800

    Direct access to Proj data via functions

diff --git a/postgis/lwgeom_transform.c b/postgis/lwgeom_transform.c
index 758e7996c..0f05307ee 100644
--- a/postgis/lwgeom_transform.c
+++ b/postgis/lwgeom_transform.c
@@ -26,6 +26,7 @@
 #include "postgres.h"
 #include "fmgr.h"
 #include "utils/builtins.h"
+#include "funcapi.h"
 
 #include "../postgis_config.h"
 #include "liblwgeom.h"
@@ -225,3 +226,215 @@ Datum LWGEOM_asKML(PG_FUNCTION_ARGS)
 		PG_RETURN_TEXT_P(kml);
 	PG_RETURN_NULL();
 }
+
+
+/********************************************************************************
+ * PROJ database reading functions
+ */
+
+
+#if POSTGIS_PROJ_VERSION >= 61
+
+struct srs_entry {
+	text* auth_name;
+	text* auth_code;
+};
+
+struct srs_data {
+	struct srs_entry* entries;
+	uint32_t num_entries;
+	uint32_t capacity;
+	uint32_t current_entry;
+};
+
+static Datum
+srs_tuple_from_entry(const struct srs_entry* entry, TupleDesc tuple_desc)
+{
+	HeapTuple tuple;
+	Datum tuple_data[4] = {0, 0, 0, 0};
+	bool  tuple_null[4] = {false, false, false, false};
+	PJ_CONTEXT *ctx = NULL;
+	const char* const empty_options[2] = {NULL};
+	const char* const wkt_options[2] = {"MULTILINE=NO", NULL};
+	const char *srtext;
+	const char *proj4text;
+
+	PJ *obj = proj_create_from_database(ctx,
+		text_to_cstring(entry->auth_name),
+		text_to_cstring(entry->auth_code),
+		PJ_CATEGORY_CRS, 0, empty_options);
+
+	if (!obj)
+		return (Datum) 0;
+
+	srtext = proj_as_wkt(ctx, obj, PJ_WKT1_GDAL, wkt_options);
+	proj4text = proj_as_proj_string(ctx, obj, PJ_PROJ_4, empty_options);
+
+	if (entry->auth_name)
+		tuple_data[0] = PointerGetDatum(entry->auth_name);
+	else
+		tuple_null[0] = true;
+
+	if (entry->auth_code)
+		tuple_data[1] = PointerGetDatum(entry->auth_code);
+	else
+		tuple_null[1] = true;
+
+	if (srtext)
+		tuple_data[2] = PointerGetDatum(cstring_to_text(srtext));
+	else
+		tuple_null[2] = true;
+
+	if (proj4text)
+		tuple_data[3] = PointerGetDatum(cstring_to_text(proj4text));
+	else
+		tuple_null[3] = true;
+
+	tuple = heap_form_tuple(tuple_desc, tuple_data, tuple_null);
+	proj_destroy(obj);
+
+	return HeapTupleGetDatum(tuple);
+}
+
+#endif
+
+/**
+ * Search for srtext and proj4text given auth_name and auth_srid,
+ * returns TABLE(auth_name text, auth_srid text, srtext text, proj4text text)
+ */
+Datum postgis_srs_entry(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(postgis_srs_entry);
+Datum postgis_srs_entry(PG_FUNCTION_ARGS)
+{
+#if POSTGIS_PROJ_VERSION < 61
+	elog(ERROR, "%s is not supported with Proj < 6.1", __func__);
+#else
+	Datum result;
+	struct srs_entry entry;
+	text* auth_name = PG_GETARG_TEXT_P(0);
+	text* auth_code = PG_GETARG_TEXT_P(1);
+	TupleDesc tuple_desc;
+
+	if (get_call_result_type(fcinfo, 0, &tuple_desc) != TYPEFUNC_COMPOSITE) {
+		ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("%s called with incompatible return type", __func__)));
+	}
+	BlessTupleDesc(tuple_desc);
+
+	entry.auth_name = auth_name;
+	entry.auth_code = auth_code;
+	result = srs_tuple_from_entry(&entry, tuple_desc);
+
+	if (result)
+		PG_RETURN_DATUM(srs_tuple_from_entry(&entry, tuple_desc));
+	else
+		PG_RETURN_NULL();
+#endif
+}
+
+
+Datum postgis_srs_entry_all(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(postgis_srs_entry_all);
+Datum postgis_srs_entry_all(PG_FUNCTION_ARGS)
+{
+#if POSTGIS_PROJ_VERSION < 61
+	elog(ERROR, "%s is not supported with Proj < 6.1", __func__);
+#else
+	FuncCallContext *funcctx;
+	MemoryContext oldcontext;
+	struct srs_data *state;
+	Datum result;
+
+	/*
+	* On the first call, fill in the state with all
+	* of the auth_name/auth_srid pairings in the
+	* proj database. Then the per-call routine is just
+	* one isolated call per pair.
+	*/
+	if (SRF_IS_FIRSTCALL()) {
+		uint32_t i, j;
+		/*
+		* Only a subset of supported proj types actually
+		* show up in spatial_ref_sys
+		*/
+		#define ntypes 3
+		PJ_TYPE types[ntypes] = {PJ_TYPE_PROJECTED_CRS, PJ_TYPE_GEOGRAPHIC_CRS, PJ_TYPE_COMPOUND_CRS};
+
+		/*
+		* Could read all authorities from database, but includes
+		* authorities (IGN, OGC) that use non-integral values in
+		* auth_srid. So hand-coded list for now.
+		*/
+		#define nauths 2
+		const char* auth_names[nauths] = {"EPSG", "ESRI"};
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc0(sizeof(*state));
+		state->capacity = 8192;
+		state->num_entries = 0;
+		state->entries = palloc0(state->capacity * sizeof(*(state->entries)));
+
+		for (i = 0; i < nauths; i++) {
+			const char* auth_name = auth_names[i];
+			for (j = 0; j < ntypes; j++) {
+				PJ_CONTEXT *ctx = NULL;
+				int allow_deprecated = 0;
+				PJ_TYPE type = types[j];
+				PROJ_STRING_LIST codes_ptr = proj_get_codes_from_database(ctx, auth_name, type, allow_deprecated);
+				PROJ_STRING_LIST codes = codes_ptr;
+				const char *code;
+				while(codes && *codes) {
+					code = *codes++;
+					/* Ensure there is space in the entry list */
+					if (state->num_entries == state->capacity) {
+						state->capacity *= 2;
+						state->entries = repalloc(state->entries, state->capacity * sizeof(*(state->entries)));
+					}
+					/* Write the entry into the entry list and increment */
+					state->entries[state->num_entries].auth_name = cstring_to_text(auth_name);
+					state->entries[state->num_entries].auth_code = cstring_to_text(code);
+					state->num_entries++;
+				}
+				/* Clean up system allocated memory */
+				proj_string_list_destroy(codes_ptr);
+			}
+		}
+
+		/*
+		* Read the TupleDesc from the FunctionCallInfo. The SQL definition
+		* of the function must have the right number of fields and types
+		* to match up to this C code.
+		*/
+		if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE) {
+			ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("%s called with incompatible return type", __func__)));
+		}
+
+		BlessTupleDesc(funcctx->tuple_desc);
+		funcctx->user_fctx = state;
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* Stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+	state = funcctx->user_fctx;
+
+	/* Exit when we've read all entries */
+	if (!state->num_entries || state->current_entry == state->num_entries) {
+		SRF_RETURN_DONE(funcctx);
+	}
+
+	/* Lookup the srtext/proj4text for this entry */
+	result = srs_tuple_from_entry(
+		state->entries + state->current_entry++,
+		funcctx->tuple_desc);
+
+	if (result)
+		SRF_RETURN_NEXT(funcctx, result);
+
+	/* Stop if lookup fails drastically */
+	SRF_RETURN_DONE(funcctx);
+#endif
+}
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index a66f82370..852087821 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -2859,6 +2859,18 @@ CREATE OR REPLACE FUNCTION ST_Transform(geom geometry, from_proj text, to_srid i
 	LANGUAGE 'sql' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_HIGH;
 
+CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys(auth_name_in text, auth_srid_in text, OUT auth_name text, OUT auth_srid TEXT, OUT srtext TEXT, OUT proj4text TEXT)
+	RETURNS RECORD
+	AS 'MODULE_PATHNAME', 'postgis_srs_entry'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_MEDIUM;
+
+CREATE OR REPLACE FUNCTION postgis_spatial_ref_sys(OUT auth_name text, OUT auth_srid TEXT, OUT srtext TEXT, OUT proj4text TEXT)
+	RETURNS SETOF RECORD
+	AS 'MODULE_PATHNAME', 'postgis_srs_entry_all'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_HIGH;
+
 -----------------------------------------------------------------------
 -- POSTGIS_VERSION()
 -----------------------------------------------------------------------

-----------------------------------------------------------------------

Summary of changes:
 NEWS                       |   2 +
 postgis/lwgeom_transform.c | 288 +++++++++++++++++++++++++++++++++++++++++++++
 postgis/postgis.sql.in     |  18 +++
 3 files changed, 308 insertions(+)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list