[postgis-tickets] [SCM] PostGIS branch master updated. 3.3.0rc2-933-gb85f9dcf6

git at osgeo.org git at osgeo.org
Thu May 25 08:33:29 PDT 2023


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  b85f9dcf6af50b90bf3112c86a47b1a08d601c79 (commit)
      from  1cf9e7b197334fc35f883415dc79d706bc9e2d5f (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 b85f9dcf6af50b90bf3112c86a47b1a08d601c79
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Thu May 25 08:30:57 2023 -0700

    Functions to expose metadata about Proj entries.
    Works with Proj6+, and queries the internal Proj database
    for information about the CRS entries.
    postgis_srs_codes() lists all codes in an authority
    postgis_srs_all() lists all codes in the proj db
    postgis_srs() shows info about specific code
    postgis_srs_search() finds planar CRSs that fit query bounds

diff --git a/doc/reference_srs.xml b/doc/reference_srs.xml
index 05c401fed..ed773806e 100644
--- a/doc/reference_srs.xml
+++ b/doc/reference_srs.xml
@@ -489,4 +489,243 @@ SELECT ST_AsText(ST_TransformPipeline('SRID=4939;POINT(143.0 -37.0)'::geometry,
     </refsection>
   </refentry>
 
+
+  <refentry id="postgis_srs_codes">
+    <refnamediv>
+    <refname>postgis_srs_codes</refname>
+
+    <refpurpose>Return the list of SRS codes associated with the given authority.</refpurpose>
+    </refnamediv>
+
+    <refsynopsisdiv>
+        <funcsynopsis>
+            <funcprototype>
+                <funcdef>setof text <function>postgis_srs_codes</function></funcdef>
+                <paramdef><type>text </type> <parameter>auth_name</parameter></paramdef>
+            </funcprototype>
+        </funcsynopsis>
+    </refsynopsisdiv>
+
+    <refsection>
+      <title>Description</title>
+
+      <para>Returns a set of all <varname>auth_srid</varname> for the given <varname>auth_name</varname>.</para>
+
+      <para>Availability: 3.4.0</para>
+      <para>Proj version 6+</para>
+
+    </refsection>
+
+    <refsection>
+      <title>Examples</title>
+      <para>List the first ten codes associated with the EPSG authority.</para>
+      <programlisting>
+SELECT * FROM postgis_srs_codes('EPSG') LIMIT 10;
+
+ postgis_srs_codes
+-------------------
+ 2000
+ 20004
+ 20005
+ 20006
+ 20007
+ 20008
+ 20009
+ 2001
+ 20010
+ 20011
+    </programlisting>
+    </refsection>
+
+    <!-- Optionally add a "See Also" section -->
+    <refsection>
+    <title>See Also</title>
+    <para><xref linkend="postgis_srs" />, <xref linkend="postgis_srs_all" />, <xref linkend="postgis_srs_search" /></para>
+    </refsection>
+
+  </refentry>
+
+
+  <refentry id="postgis_srs">
+    <refnamediv>
+    <refname>postgis_srs</refname>
+
+    <refpurpose>Return a metadata record for the requested authority and srid.</refpurpose>
+    </refnamediv>
+
+    <refsynopsisdiv>
+        <funcsynopsis>
+            <funcprototype>
+                <funcdef>setof record <function>postgis_srs</function></funcdef>
+                <paramdef><type>text </type> <parameter>auth_name</parameter></paramdef>
+                <paramdef><type>text </type> <parameter>auth_srid</parameter></paramdef>
+            </funcprototype>
+        </funcsynopsis>
+    </refsynopsisdiv>
+
+    <refsection>
+      <title>Description</title>
+
+      <para>Returns a metadata record for the requested <varname>auth_srid</varname> for the given <varname>auth_name</varname>. The record will have the <varname>auth_name</varname>, <varname>auth_srid</varname>, <varname>srname</varname>, <varname>srtext</varname>, <varname>proj4text</varname>, and the corners of the area of usage, <varname>point_sw</varname> and <varname>point_ne</varname>.</para>
+
+      <para>Availability: 3.4.0</para>
+      <para>Proj version 6+</para>
+
+    </refsection>
+
+    <refsection>
+      <title>Examples</title>
+      <para>Get the metadata for EPSG:3005.</para>
+      <programlisting>
+SELECT * FROM postgis_srs('EPSG', '3005');
+
+auth_name | EPSG
+auth_srid | 3005
+srname    | NAD83 / BC Albers
+srtext    | PROJCS["NAD83 / BC Albers", ... ]]
+proj4text | +proj=aea +lat_0=45 +lon_0=-126 +lat_1=50 +lat_2=58.5 +x_0=1000000 +y_0=0 +datum=NAD83 +units=m +no_defs +type=crs
+point_sw  | 0101000020E6100000E17A14AE476161C00000000000204840
+point_ne  | 0101000020E610000085EB51B81E855CC0E17A14AE47014E40
+    </programlisting>
+    </refsection>
+
+    <!-- Optionally add a "See Also" section -->
+    <refsection>
+    <title>See Also</title>
+    <para><xref linkend="postgis_srs_codes" />, <xref linkend="postgis_srs_all" />, <xref linkend="postgis_srs_search" /></para>
+    </refsection>
+
+  </refentry>
+
+
+  <refentry id="postgis_srs_all">
+    <refnamediv>
+    <refname>postgis_srs_all</refname>
+
+    <refpurpose>Return metadata records for every spatial reference system in the underlying Proj database.</refpurpose>
+    </refnamediv>
+
+    <refsynopsisdiv>
+        <funcsynopsis>
+            <funcprototype>
+                <funcdef>setof record <function>postgis_srs_all</function></funcdef>
+                <void/>
+            </funcprototype>
+        </funcsynopsis>
+    </refsynopsisdiv>
+
+    <refsection>
+      <title>Description</title>
+
+      <para>Returns a set of all metadata records in the underlying Proj database. The records will have the <varname>auth_name</varname>, <varname>auth_srid</varname>, <varname>srname</varname>, <varname>srtext</varname>, <varname>proj4text</varname>, and the corners of the area of usage, <varname>point_sw</varname> and <varname>point_ne</varname>.</para>
+
+      <para>Availability: 3.4.0</para>
+      <para>Proj version 6+</para>
+
+    </refsection>
+
+    <refsection>
+      <title>Examples</title>
+      <para>Get the first 10 metadata records from the Proj database.</para>
+      <programlisting>
+SELECT auth_name, auth_srid, srname FROM postgis_srs_all() LIMIT 10;
+
+ auth_name | auth_srid |                  srname
+-----------+-----------+------------------------------------------
+ EPSG      | 2000      | Anguilla 1957 / British West Indies Grid
+ EPSG      | 20004     | Pulkovo 1995 / Gauss-Kruger zone 4
+ EPSG      | 20005     | Pulkovo 1995 / Gauss-Kruger zone 5
+ EPSG      | 20006     | Pulkovo 1995 / Gauss-Kruger zone 6
+ EPSG      | 20007     | Pulkovo 1995 / Gauss-Kruger zone 7
+ EPSG      | 20008     | Pulkovo 1995 / Gauss-Kruger zone 8
+ EPSG      | 20009     | Pulkovo 1995 / Gauss-Kruger zone 9
+ EPSG      | 2001      | Antigua 1943 / British West Indies Grid
+ EPSG      | 20010     | Pulkovo 1995 / Gauss-Kruger zone 10
+ EPSG      | 20011     | Pulkovo 1995 / Gauss-Kruger zone 11    </programlisting>
+    </refsection>
+
+    <!-- Optionally add a "See Also" section -->
+    <refsection>
+    <title>See Also</title>
+    <para><xref linkend="postgis_srs_codes" />, <xref linkend="postgis_srs" />, <xref linkend="postgis_srs_search" /></para>
+    </refsection>
+
+  </refentry>
+
+
+
+
+  <refentry id="postgis_srs_search">
+    <refnamediv>
+    <refname>postgis_srs_search</refname>
+
+    <refpurpose>Return metadata records for projected coordinate systems that have areas of useage that fully contain the bounds parameter.</refpurpose>
+    </refnamediv>
+
+    <refsynopsisdiv>
+        <funcsynopsis>
+            <funcprototype>
+                <funcdef>setof record <function>postgis_srs_search</function></funcdef>
+                <paramdef><type>geometry </type> <parameter>bounds</parameter></paramdef>
+                <paramdef choice="opt"><type>text </type> <parameter>auth_name=EPSG</parameter></paramdef>
+            </funcprototype>
+        </funcsynopsis>
+    </refsynopsisdiv>
+
+    <refsection>
+      <title>Description</title>
+
+      <para>Return a set of metadata records for projected coordinate systems that have areas of useage that fully contain the bounds parameter. Each record will have the <varname>auth_name</varname>, <varname>auth_srid</varname>, <varname>srname</varname>, <varname>srtext</varname>, <varname>proj4text</varname>, and the corners of the area of usage, <varname>point_sw</varname> and <varname>point_ne</varname>.
+      </para>
+
+      <para>The search only looks for projected coordinate systems, and is intended for users to explore the possible systems that work for the extent of their data.</para>
+
+      <para>Availability: 3.4.0</para>
+      <para>Proj version 6+</para>
+
+    </refsection>
+
+    <refsection>
+      <title>Examples</title>
+      <para>Search for projected coordinate systems in Louisiana.</para>
+      <programlisting>
+SELECT auth_name, auth_srid, srname,
+  ST_AsText(point_sw) AS point_sw,
+  ST_AsText(point_ne) AS point_ne
+FROM postgis_srs_search('SRID=4326;LINESTRING(-90 30, -91 31)')
+LIMIT 3;
+
+ auth_name | auth_srid |                srname                |      point_sw       |      point_ne
+-----------+-----------+--------------------------------------+---------------------+---------------------
+ EPSG      | 2801      | NAD83(HARN) / Louisiana South        | POINT(-93.94 28.85) | POINT(-88.75 31.07)
+ EPSG      | 3452      | NAD83 / Louisiana South (ftUS)       | POINT(-93.94 28.85) | POINT(-88.75 31.07)
+ EPSG      | 3457      | NAD83(HARN) / Louisiana South (ftUS) | POINT(-93.94 28.85) | POINT(-88.75 31.07)
+</programlisting>
+
+      <para>Scan a table for max extent and find projected coordinate systems that might suit.</para>
+
+      <programlisting>
+WITH ext AS (
+  SELECT ST_Extent(geom) AS geom, Max(ST_SRID(geom)) AS srid
+  FROM foo
+)
+SELECT auth_name, auth_srid, srname,
+  ST_AsText(point_sw) AS point_sw,
+  ST_AsText(point_ne) AS point_ne
+FROM ext
+CROSS JOIN postgis_srs_search(ST_SetSRID(ext.geom, ext.srid))
+LIMIT 3;</programlisting>
+
+    </refsection>
+
+    <!-- Optionally add a "See Also" section -->
+    <refsection>
+    <title>See Also</title>
+    <para><xref linkend="postgis_srs_codes" />, <xref linkend="postgis_srs_all" />, <xref linkend="postgis_srs" /></para>
+    </refsection>
+
+  </refentry>
+
+
+
   </sect1>
diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in
index 13a5fd936..f79303a57 100644
--- a/liblwgeom/liblwgeom.h.in
+++ b/liblwgeom/liblwgeom.h.in
@@ -2452,6 +2452,7 @@ int lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outst
  */
 int lwgeom_transform(LWGEOM *geom, LWPROJ* pj);
 int ptarray_transform(POINTARRAY *pa, LWPROJ* pj);
+int box3d_transform(GBOX *box, LWPROJ *pj);
 
 /**
  * Allocate a new LWPROJ containing the reference to the PROJ's PJ
diff --git a/liblwgeom/lwgeom_transform.c b/liblwgeom/lwgeom_transform.c
index 64e2a3ecc..8adf4da44 100644
--- a/liblwgeom/lwgeom_transform.c
+++ b/liblwgeom/lwgeom_transform.c
@@ -205,6 +205,27 @@ lwgeom_transform_pipeline(LWGEOM *geom, const char* pipelinestr, bool is_forward
 	return ret;
 }
 
+int
+box3d_transform(GBOX *gbox, LWPROJ *pj)
+{
+	POINT4D pt;
+	POINTARRAY *pa = ptarray_construct(0, 0, 4);
+	pt = (POINT4D){gbox->xmin, gbox->ymin, 0, 0};
+	ptarray_set_point4d(pa, 0, &pt);
+
+	pt = (POINT4D){gbox->xmax, gbox->ymin, 0, 0};
+	ptarray_set_point4d(pa, 1, &pt);
+
+	pt = (POINT4D){gbox->xmax, gbox->ymax, 0, 0};
+	ptarray_set_point4d(pa, 2, &pt);
+
+	pt = (POINT4D){gbox->xmin, gbox->ymax, 0, 0};
+	ptarray_set_point4d(pa, 3, &pt);
+
+	ptarray_transform(pa, pj);
+	return ptarray_calculate_gbox_cartesian(pa, gbox);
+}
+
 int
 ptarray_transform(POINTARRAY *pa, LWPROJ *pj)
 {
diff --git a/postgis/lwgeom_transform.c b/postgis/lwgeom_transform.c
index 646f0e6dd..3d631faa8 100644
--- a/postgis/lwgeom_transform.c
+++ b/postgis/lwgeom_transform.c
@@ -25,10 +25,12 @@
 
 #include "postgres.h"
 #include "fmgr.h"
+#include "funcapi.h"
 #include "utils/builtins.h"
 
 #include "../postgis_config.h"
 #include "liblwgeom.h"
+#include "lwgeodetic.h"
 #include "lwgeom_transform.h"
 
 
@@ -198,9 +200,13 @@ Datum transform_pipeline_geom(PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1(postgis_proj_version);
 Datum postgis_proj_version(PG_FUNCTION_ARGS)
 {
-
+#if POSTGIS_PROJ_VERSION < 61
+	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);
 }
 
@@ -267,3 +273,483 @@ 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;
+	double sort;
+};
+
+struct srs_data {
+	struct srs_entry* entries;
+	uint32_t num_entries;
+	uint32_t capacity;
+	uint32_t current_entry;
+};
+
+static int
+srs_entry_cmp (const void *a, const void *b)
+{
+	const struct srs_entry *entry_a = (const struct srs_entry*)(a);
+	const struct srs_entry *entry_b = (const struct srs_entry*)(b);
+	if      (entry_a->sort < entry_b->sort) return -1;
+	else if (entry_a->sort > entry_b->sort) return 1;
+	else return 0;
+}
+
+static Datum
+srs_tuple_from_entry(const struct srs_entry* entry, TupleDesc tuple_desc)
+{
+	HeapTuple tuple;
+	Datum tuple_data[7] = {0, 0, 0, 0, 0, 0, 0};
+	bool  tuple_null[7] = {true, true, true, true, true, true, true};
+	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;
+	const char * srname;
+	double w_lon, s_lat, e_lon, n_lat;
+	int ok;
+
+	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);
+	srname = proj_get_name(obj);
+	ok = proj_get_area_of_use(ctx, obj, &w_lon, &s_lat, &e_lon, &n_lat, NULL);
+
+	if (entry->auth_name) {
+		tuple_data[0] = PointerGetDatum(entry->auth_name);
+		tuple_null[0] = false;
+	}
+
+	if (entry->auth_code) {
+		tuple_data[1] = PointerGetDatum(entry->auth_code);
+		tuple_null[1] = false;
+	}
+
+	if (srname) {
+		tuple_data[2] = PointerGetDatum(cstring_to_text(srname));
+		tuple_null[2] = false;
+	}
+
+	if (srtext) {
+		tuple_data[3] = PointerGetDatum(cstring_to_text(srtext));
+		tuple_null[3] = false;
+	}
+
+	if (proj4text) {
+		tuple_data[4] = PointerGetDatum(cstring_to_text(proj4text));
+		tuple_null[4] = false;
+	}
+
+	if (ok) {
+		LWPOINT *p_sw = lwpoint_make2d(4326, w_lon, s_lat);
+		LWPOINT *p_ne = lwpoint_make2d(4326, e_lon, n_lat);
+		GSERIALIZED *g_sw = geometry_serialize((LWGEOM*)p_sw);
+		GSERIALIZED *g_ne = geometry_serialize((LWGEOM*)p_ne);
+		tuple_data[5] = PointerGetDatum(g_sw);
+		tuple_null[5] = false;
+		tuple_data[6] = PointerGetDatum(g_ne);
+		tuple_null[6] = false;
+	}
+
+	tuple = heap_form_tuple(tuple_desc, tuple_data, tuple_null);
+	proj_destroy(obj);
+
+	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)
+		{
+			/* Read current code and move forward one entry */
+			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);
+	}
+}
+
+static void
+srs_find_planar(const char *auth_name, const LWGEOM *bounds, struct srs_data *state)
+{
+	int32_t srid_from = lwgeom_get_srid(bounds);
+	const int32_t srid_to = 4326;
+	GBOX gbox = *(lwgeom_get_bbox(bounds));
+	PJ_TYPE types[1] = {PJ_TYPE_PROJECTED_CRS};
+	PROJ_CRS_INFO **crs_list_ptr, **crs_list;
+	int crs_count;
+	PJ_CONTEXT *ctx = NULL;
+
+	PROJ_CRS_LIST_PARAMETERS *params = proj_get_crs_list_parameters_create();
+	params->types = types;
+	params->typesCount = 1;
+	params->crs_area_of_use_contains_bbox = true;
+	params->bbox_valid = true;
+	params->allow_deprecated = false;
+
+#if POSTGIS_PROJ_VERSION >= 81
+	params->celestial_body_name = "Earth";
+#endif
+
+	if (srid_from != srid_to)
+	{
+		LWPROJ *pj;
+		if (lwproj_lookup(srid_from, srid_to, &pj) == LW_FAILURE)
+			elog(ERROR, "%s: Lookup of SRID %u => %u transform failed",
+			    __func__, srid_from, srid_to);
+
+		box3d_transform(&gbox, pj);
+	}
+
+	params->west_lon_degree = gbox.xmin;
+	params->south_lat_degree = gbox.ymin;
+	params->east_lon_degree = gbox.xmax;
+	params->north_lat_degree = gbox.ymax;
+
+	crs_list = crs_list_ptr = proj_get_crs_info_list_from_database(
+	                            ctx, auth_name, params, &crs_count);
+
+	/* TODO, throw out really huge / dumb areas? */
+
+	while (crs_list && *crs_list)
+	{
+		/* Read current crs and move forward one entry */
+		PROJ_CRS_INFO *crs = *crs_list++;
+
+		/* Read the corners of the CRS area of use */
+		double area;
+		double height = crs->north_lat_degree - crs->south_lat_degree;
+		double width = crs->east_lon_degree - crs->west_lon_degree;
+		if (width < 0.0)
+			width = 360 - (crs->west_lon_degree - crs->east_lon_degree);
+		area = width * height;
+
+		/* 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(crs->auth_name);
+		state->entries[state->num_entries].auth_code = cstring_to_text(crs->code);
+		state->entries[state->num_entries].sort = area;
+		state->num_entries++;
+	}
+
+	/* Put the list of entries into order of area size, smallest to largest */
+	qsort(state->entries, state->num_entries, sizeof(struct srs_data), srs_entry_cmp);
+
+	proj_crs_info_list_destroy(crs_list_ptr);
+	proj_get_crs_list_parameters_destroy(params);
+}
+#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 < 60
+	elog(ERROR, "%s is not supported with Proj < 6.0", __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 < 60
+	elog(ERROR, "%s is not supported with Proj < 6.0", __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())
+	{
+		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.
+		*/
+		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
+		* 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
+}
+
+
+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);
+	SRF_RETURN_DONE(funcctx);
+#endif
+}
+
+
+/**
+ * Search for projections given extent and (optional) auth_name
+ * returns TABLE(auth_name, auth_srid, srtext, proj4text, point_sw, point_ne)
+ */
+Datum postgis_srs_search(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(postgis_srs_search);
+Datum postgis_srs_search(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;
+
+	/*
+	* 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())
+	{
+		GSERIALIZED *gbounds = PG_GETARG_GSERIALIZED_P(0);
+		LWGEOM *bounds = lwgeom_from_gserialized(gbounds);
+		text *auth_name = PG_GETARG_TEXT_P(1);
+
+		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.
+		*/
+		state = srs_state_init();
+
+		/* Run the Proj query */
+		srs_find_planar(text_to_cstring(auth_name), bounds, state);
+
+		/*
+		* 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 f30d75425..19d2f2c80 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -2872,6 +2872,65 @@ CREATE OR REPLACE FUNCTION postgis_transform_geometry(geom geometry, text, text,
 	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_MEDIUM;
 
+------------------------------------------------------------------------
+
+-- Availability: 3.4.0
+CREATE OR REPLACE FUNCTION postgis_srs_codes(auth_name text)
+	RETURNS SETOF TEXT
+	AS 'MODULE_PATHNAME', 'postgis_srs_codes'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_MEDIUM;
+
+-- Availability: 3.4.0
+CREATE OR REPLACE FUNCTION postgis_srs(auth_name text, auth_srid text)
+	RETURNS TABLE(
+		auth_name TEXT,
+		auth_srid TEXT,
+		srname TEXT,
+		srtext TEXT,
+		proj4text TEXT,
+		point_sw GEOMETRY,
+		point_ne GEOMETRY
+		)
+	AS 'MODULE_PATHNAME', 'postgis_srs_entry'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_MEDIUM;
+
+-- Availability: 3.4.0
+CREATE OR REPLACE FUNCTION postgis_srs_all()
+	RETURNS TABLE(
+		auth_name TEXT,
+		auth_srid TEXT,
+		srname TEXT,
+		srtext TEXT,
+		proj4text TEXT,
+		point_sw GEOMETRY,
+		point_ne GEOMETRY
+		)
+	AS 'MODULE_PATHNAME', 'postgis_srs_entry_all'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_HIGH;
+
+-- Availability: 3.4.0
+CREATE OR REPLACE FUNCTION postgis_srs_search(
+		bounds geometry,
+		authname text DEFAULT 'EPSG')
+	RETURNS TABLE(
+		auth_name TEXT,
+		auth_srid TEXT,
+		srname TEXT,
+		srtext TEXT,
+		proj4text TEXT,
+		point_sw GEOMETRY,
+		point_ne GEOMETRY
+		)
+	AS 'MODULE_PATHNAME', 'postgis_srs_search'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_HIGH;
+
+
+------------------------------------------------------------------------
+
 -- PostGIS equivalent of old function: transform(geometry,integer)
 CREATE OR REPLACE FUNCTION ST_Transform(geometry,integer)
 	RETURNS geometry

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

Summary of changes:
 doc/reference_srs.xml        | 239 +++++++++++++++++++++
 liblwgeom/liblwgeom.h.in     |   1 +
 liblwgeom/lwgeom_transform.c |  21 ++
 postgis/lwgeom_transform.c   | 488 ++++++++++++++++++++++++++++++++++++++++++-
 postgis/postgis.sql.in       |  59 ++++++
 5 files changed, 807 insertions(+), 1 deletion(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list