[SCM] PostGIS branch master updated. 3.6.0beta1-40-g69d326a43

git at osgeo.org git at osgeo.org
Sat Aug 16 15:53:39 PDT 2025


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  69d326a43118f39204bae44a85aaad3c5e4b142d (commit)
       via  af18e8f2fc5df5ef6ab96e897249b382cd89e88a (commit)
       via  1b0336578b0f1107d472e2c85f35b9cfa29edf58 (commit)
       via  55aa87c653fefad2a23e4cce48b58a580a9027db (commit)
      from  3b832a41769240442443cf5bc7f0f401eeb83952 (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 69d326a43118f39204bae44a85aaad3c5e4b142d
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Sat Aug 16 15:53:02 2025 -0700

    Add ST_IntersectionFractions to expose new GEOSGridIntersectionFractions function in GEOS 3.14+
    References #5963

diff --git a/NEWS b/NEWS
index 711eef38a..0346015d6 100644
--- a/NEWS
+++ b/NEWS
@@ -52,4 +52,5 @@ Dapeng Wang, Zuo Chenwei from HighGo (Chinese Team)
     coverages (Paul Ramsey) from GEOS 3.14 (Martin Davis)
   - Add ST_ReclassExact to quickly remap values in raster (Paul Ramsey)
   - #3110, GT-242 [topology] Support for bigint (Ayo Adesugba, U.S. Census Bureau)
+  - #5963, add ST_IntersectionFractions (Paul Ramsey)
 
diff --git a/configure.ac b/configure.ac
index e1439766c..ac2d805c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1705,6 +1705,7 @@ if test "x$with_raster" != "xno"; then
 		raster/test/Makefile \
 		raster/test/cunit/Makefile \
 		raster/test/regress/Makefile \
+		raster/test/regress/tests.mk \
 		raster/scripts/Makefile \
 		raster/scripts/python/Makefile"
 
diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml
index 724761662..cd7ab9b8f 100644
--- a/doc/reference_raster.xml
+++ b/doc/reference_raster.xml
@@ -14434,6 +14434,72 @@ MULTIPOLYGON(((3427928 5793243.85,3427928 5793243.8,3427928 5793243.75,3427927.8
                 </refsection>
             </refentry>
 
+            <refentry xml:id="RT_ST_IntersectionFractions">
+              <refnamediv>
+                    <refname>ST_IntersectionFractions</refname>
+                    <refpurpose>Calculates the fraction of each raster cell that is covered by a given geometry.</refpurpose>
+              </refnamediv>
+
+              <refsynopsisdiv>
+                    <funcsynopsis>
+                    <funcprototype>
+                            <funcdef>raster <function>ST_IntersectionFractions</function></funcdef>
+                            <paramdef><type>raster </type> <parameter>rast</parameter></paramdef>
+                            <paramdef><type>geometry </type> <parameter>geom</parameter></paramdef>
+                      </funcprototype>
+                    </funcsynopsis>
+              </refsynopsisdiv>
+
+                <refsection>
+                    <title>Description</title>
+                    <para>Calculates the fraction of each raster cell that is covered by a given geometry. The first argument is a raster, which defines the grid geometry to use for the calculation. The extent and cell size are read from the raster parameter. The second argument is a geometry, which is overlaid with the grid, and each grid populated based on overlaying the geometry on the grid. For polygons, the value returned for each cell is the proportion of its area that is covered by the geometry. For linestrings, the value returned for each cell is the length contained in the cell.</para>
+                    <para role="availability" conformance="3.6.0">Availability: 3.6.0 Requires GEOS 3.14 or higher.</para>
+                </refsection>
+
+            <refsection>
+                    <title>Examples</title>
+                    <programlisting>
+CREATE TABLE raster_proportions_rast (
+    name text,
+    rast raster
+);
+
+INSERT INTO raster_proportions_rast (name, rast) VALUES (
+  '2x2 raster covering 0,0 to 10,10',
+  ST_MakeEmptyRaster(
+    2,  2,   -- raster width/height in pixels
+    0, 10,   -- upper-left corner x/y coordinates
+    5, -5,   -- pixel width/height in ground units
+    0,  0,   -- skew x/y
+    0        -- SRID
+  ));
+
+--
+-- This rotated square polygon covers half of each cell in the
+-- raster.
+--
+SELECT name, ST_DumpValues(
+    ST_IntersectionFractions(
+        rast,
+        'POLYGON((5 0, 0 5, 5 10, 10 5, 5 0))'::geometry),1)
+FROM raster_proportions_rast;
+
+
+ 2x2 raster covering 0,0 to 10,10
+---------------------------------
+ {{0.5,0.5},{0.5,0.5}}
+</programlisting>
+                </refsection>
+
+                <refsection>
+                    <title>See Also</title>
+                    <para>
+                        <xref linkend="RT_ST_MakeEmptyRaster"/>
+                    </para>
+                </refsection>
+            </refentry>
+
+
     </section> <!-- /misc -->
 
     <section xml:id="RT_Operators">
diff --git a/raster/rt_core/librtcore.h b/raster/rt_core/librtcore.h
index 399f9d8e2..b4395ba2b 100644
--- a/raster/rt_core/librtcore.h
+++ b/raster/rt_core/librtcore.h
@@ -2202,6 +2202,25 @@ rt_raster rt_raster_colormap(
 	rt_colormap colormap
 );
 
+#if POSTGIS_GEOS_VERSION >= 31400
+/**
+ * Calculates the fraction of each raster cell that is covered by a given geometry.
+ *
+ * This function serves as a wrapper around the GEOSGridIntersectionFractions C-API function.
+ * It takes a PostGIS raster and a geometry, and returns a new single-band raster
+ * where each pixel's value is a float between 0.0 and 1.0, representing the
+ * fraction of that cell covered by the geometry.
+ *
+ * @param rast_in The input raster which defines the grid (dimensions and georeference).
+ * @param geom The input geometry to compute intersections with.
+ * @return A new single-band raster of pixel type PT_32BF on success, or NULL on failure.
+ */
+rt_raster rt_raster_intersection_fractions(
+	const rt_raster rast_in,
+	const LWGEOM *geom
+);
+#endif
+
 /*- utilities -------------------------------------------------------*/
 
 /*
diff --git a/raster/rt_core/rt_spatial_relationship.c b/raster/rt_core/rt_spatial_relationship.c
index d60e3a810..374dd07f2 100644
--- a/raster/rt_core/rt_spatial_relationship.c
+++ b/raster/rt_core/rt_spatial_relationship.c
@@ -1293,3 +1293,99 @@ rt_raster_intersects(
 	return ES_NONE;
 }
 
+#if POSTGIS_GEOS_VERSION >= 31400
+
+rt_raster
+rt_raster_intersection_fractions(
+	const rt_raster rast_in,
+	const LWGEOM *geom
+) {
+	rt_raster rast_out = NULL;
+	rt_envelope rast_env;
+	rt_band band_out = NULL;
+	int band_num;
+	unsigned int width, height;
+	GEOSGeometry *geos_geom = NULL;
+	int geos_result;
+
+	/*  Validate inputs */
+	if (!rast_in || !geom)
+	{
+		rterror("%s: NULL input raster or geometry provided", __func__);
+		return NULL;
+	}
+
+	/* Extract raster properties */
+	width = rt_raster_get_width(rast_in);
+	height = rt_raster_get_height(rast_in);
+
+	if (width == 0 || height == 0)
+	{
+		rterror("%s: Input raster has zero width or height", __func__);
+		return NULL;
+	}
+
+	/* Get the full bounding box of the raster in world coordinates */
+	if (rt_raster_get_envelope(rast_in, &rast_env) != ES_NONE)
+	{
+		rterror("%s: Could not determine raster envelope", __func__);
+		return NULL;
+	}
+
+	/* Shallow clone a new raster with no bands */
+	rast_out = rt_raster_clone(rast_in, 0);
+	/* Add a float4 band to the empty raster */
+	band_num = rt_raster_generate_new_band(
+		rast_out, /* rast */
+		PT_32BF, /* pixel type */
+		0.0, /* initial value */
+		0, 0.0, /* hasnodata, nodataval */
+		0); /* index */
+
+	/* Get a reference to the new band */
+	band_out = rt_raster_get_band(rast_out, band_num);
+	if (!band_out)
+	{
+		rt_raster_destroy(rast_out);
+		rterror("%s: Failed to create output band", __func__);
+		return NULL;
+	}
+
+	/* Prepare for GEOS call */
+	/* Initialize GEOS to handle notices and errors */
+	initGEOS(rtinfo, lwgeom_geos_error);
+
+	/* Convert the PostGIS LWGEOM into a GEOS-compatible geometry */
+	/* Note: lwgeom_to_geos is a common function within PostGIS for this task */
+	geos_geom = LWGEOM2GEOS(geom, 0);
+	if (!geos_geom)
+	{
+		rt_raster_destroy(rast_out);
+		rterror("%s: Could not convert LWGEOM to GEOSGeometry", __func__);
+		return NULL;
+	}
+
+	/* Call the core GEOS function */
+	geos_result = GEOSGridIntersectionFractions(
+		geos_geom,
+		rast_env.MinX, rast_env.MinY,
+		rast_env.MaxX, rast_env.MaxY,
+		width, height,
+		rt_band_get_data(band_out)
+	);
+
+	/* The GEOS geometry is no longer needed after this call, so we can clean it up */
+	GEOSGeom_destroy(geos_geom);
+
+	/* Check if the GEOS function executed successfully (it returns 1 on success) */
+	if (geos_result != 1)
+	{
+		rt_raster_destroy(rast_out);
+		rterror("%s: GEOSGridIntersectionFractions call failed", __func__);
+		return NULL;
+	}
+
+	return rast_out; /* Return the newly created raster and band*/
+}
+#endif /* POSTGIS_GEOS_VERSION >= 31400 */
+
diff --git a/raster/rt_pg/rtpg_spatial_relationship.c b/raster/rt_pg/rtpg_spatial_relationship.c
index 98232fcbf..4a83d76d5 100644
--- a/raster/rt_pg/rtpg_spatial_relationship.c
+++ b/raster/rt_pg/rtpg_spatial_relationship.c
@@ -67,6 +67,10 @@ Datum RASTER_dfullywithin(PG_FUNCTION_ARGS);
 Datum RASTER_sameAlignment(PG_FUNCTION_ARGS);
 Datum RASTER_notSameAlignmentReason(PG_FUNCTION_ARGS);
 
+/* calculate the proportion of each cell in a grid covered */
+/* by an input geometry */
+Datum RASTER_intersectionFractions(PG_FUNCTION_ARGS);
+
 /**
  * See if two rasters intersect
  */
@@ -1313,3 +1317,45 @@ Datum RASTER_notSameAlignmentReason(PG_FUNCTION_ARGS)
 	result = cstring_to_text(reason);
 	PG_RETURN_TEXT_P(result);
 }
+
+
+/* calculate the proportion of each cell in a grid covered */
+/* by an input geometry */
+PG_FUNCTION_INFO_V1(RASTER_intersectionFractions);
+Datum RASTER_intersectionFractions(PG_FUNCTION_ARGS)
+{
+#if POSTGIS_GEOS_VERSION < 31400
+	elog(ERROR, "The GEOS version this PostGIS binary "
+	            "was compiled against (%d) does not include the "
+	            "'GEOSGridIntersectionFractions' function (3.14.0+ required)",
+	            POSTGIS_GEOS_VERSION);
+	            PG_RETURN_NULL();
+#else
+	rt_pgraster *pgrast_in = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+	GSERIALIZED *gser = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+	LWGEOM *lwg = lwgeom_from_gserialized(gser);
+	rt_raster rast_out = NULL;
+	rt_pgraster *pgrast_out = NULL;
+	rt_raster rast_in = rt_raster_deserialize(pgrast_in, FALSE);
+
+	int gtype = lwgeom_get_type(lwg);
+	if (gtype != POLYGONTYPE && gtype != MULTIPOLYGONTYPE &&
+		gtype != LINETYPE && gtype != MULTILINETYPE)
+	{
+		elog(ERROR, "ST_IntersectionFractions: Unsupported geometry type '%s'", lwtype_name(gtype));
+	}
+
+	rast_out = rt_raster_intersection_fractions(rast_in, lwg);
+	if (!rast_out)
+		elog(ERROR, "ST_IntersectionFractions: calculation returned NULL");
+
+	pgrast_out = rt_raster_serialize(rast_out);
+	rt_raster_destroy(rast_out);
+	if (!pgrast_out)
+		PG_RETURN_NULL();
+
+	SET_VARSIZE(pgrast_out, pgrast_out->size);
+	PG_RETURN_POINTER(pgrast_out);
+
+#endif /* POSTGIS_GEOS_VERSION < 31400 */
+}
diff --git a/raster/rt_pg/rtpostgis.sql.in b/raster/rt_pg/rtpostgis.sql.in
index 3b2a2d4c4..566a23b86 100644
--- a/raster/rt_pg/rtpostgis.sql.in
+++ b/raster/rt_pg/rtpostgis.sql.in
@@ -6310,6 +6310,15 @@ CREATE OR REPLACE FUNCTION ST_Disjoint(rast1 raster, rast2 raster)
 	LANGUAGE 'sql' IMMUTABLE PARALLEL SAFE
 	COST 1000;
 
+-----------------------------------------------------------------------
+-- ST_IntersectionFractions(raster, geometry) returns raster
+-----------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION ST_IntersectionFractions(rast raster, geom geometry)
+	RETURNS raster
+	AS 'MODULE_PATHNAME','RASTER_intersectionFractions'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE;
+
 -----------------------------------------------------------------------
 -- ST_Intersection(geometry, raster) in geometry-space
 -- Changed: 3.0.3, fail fast on mismatch srid and schema qualify funcs
diff --git a/raster/test/regress/rt_intersection_fractions.sql b/raster/test/regress/rt_intersection_fractions.sql
new file mode 100644
index 000000000..f3ea96d16
--- /dev/null
+++ b/raster/test/regress/rt_intersection_fractions.sql
@@ -0,0 +1,75 @@
+
+---------------------------------------------------------------
+----- ST_IntersectionFractions --------------------------------
+
+CREATE TABLE raster_proportions_rast (
+	name text,
+	rast raster
+);
+
+INSERT INTO raster_proportions_rast (name, rast) VALUES (
+	'2x2 raster covering 0,0 to 10,10',
+	ST_MakeEmptyRaster(
+		2,  2,   -- width/height in pixels
+		0, 10,   -- x/y coordinate of the upper-left corner
+		5, -5,   -- pixel width/height in ground units
+		0,  0,   -- skew x/y
+		0        -- SRID
+	));
+
+INSERT INTO raster_proportions_rast (name, rast) VALUES (
+	'10x10 raster covering 0,0 to 10,10',
+	ST_MakeEmptyRaster(
+		10, 10,   -- width/height in pixels
+		 0, 10,   -- x/y coordinate of the upper-left corner
+		 1, -1,   -- pixel width/height in ground units
+		 0,  0,   -- skew x/y
+		 0         -- SRID
+	));
+
+INSERT INTO raster_proportions_rast (name, rast) VALUES (
+	'2x2 raster covering -10,-10 to 0,0',
+	ST_MakeEmptyRaster(
+		 2,  2,   -- width/height in pixels
+	 -10,  0,   -- x/y coordinate of the upper-left corner
+		 5, -5,   -- pixel width/height in ground units
+		 0,  0,   -- skew x/y
+		 0         -- SRID
+	));
+
+INSERT INTO raster_proportions_rast (name, rast) VALUES (
+	'2x2 raster covering 5,5 to 6,6',
+	ST_MakeEmptyRaster(
+		 2,  2,   -- width/height in pixels
+		 5,  6,   -- x/y coordinate of the upper-left corner
+	 0.5, -0.5, -- pixel width/height in ground units
+		 0,  0,   -- skew x/y
+		 0        -- SRID
+	));
+
+SET extra_float_digits = -8;
+SELECT 'polygon', name, ST_DumpValues(
+	ST_IntersectionFractions(
+		rast,
+		'POLYGON((5 0, 0 5, 5 10, 10 5, 5 0))'::geometry),1)
+FROM raster_proportions_rast;
+
+SELECT 'multipolygon', name, ST_DumpValues(
+	ST_IntersectionFractions(
+		rast,
+		'MULTIPOLYGON(((5 0, 0 5, 5 10, 10 5, 5 0)))'::geometry),1)
+FROM raster_proportions_rast;
+
+SELECT 'linestring', name, ST_DumpValues(
+	ST_IntersectionFractions(
+		rast,
+		'LINESTRING(0 0, 10 10)'::geometry), 1)
+FROM raster_proportions_rast;
+
+SELECT 'multilinestring', name, ST_DumpValues(
+	ST_IntersectionFractions(
+		rast,
+		'MULTILINESTRING((0 0, 10 10), (0 10, 10 0))'::geometry), 1)
+FROM raster_proportions_rast;
+
+DROP TABLE IF EXISTS raster_proportions_rast;
diff --git a/raster/test/regress/rt_intersection_fractions_expected b/raster/test/regress/rt_intersection_fractions_expected
new file mode 100644
index 000000000..7318eb76f
--- /dev/null
+++ b/raster/test/regress/rt_intersection_fractions_expected
@@ -0,0 +1,16 @@
+polygon|2x2 raster covering 0,0 to 10,10|{{0.5,0.5},{0.5,0.5}}
+polygon|10x10 raster covering 0,0 to 10,10|{{0,0,0,0,0.5,0.5,0,0,0,0},{0,0,0,0.5,1,1,0.5,0,0,0},{0,0,0.5,1,1,1,1,0.5,0,0},{0,0.5,1,1,1,1,1,1,0.5,0},{0.5,1,1,1,1,1,1,1,1,0.5},{0.5,1,1,1,1,1,1,1,1,0.5},{0,0.5,1,1,1,1,1,1,0.5,0},{0,0,0.5,1,1,1,1,0.5,0,0},{0,0,0,0.5,1,1,0.5,0,0,0},{0,0,0,0,0.5,0.5,0,0,0,0}}
+polygon|2x2 raster covering -10,-10 to 0,0|{{0,0},{0,0}}
+polygon|2x2 raster covering 5,5 to 6,6|{{1,1},{1,1}}
+multipolygon|2x2 raster covering 0,0 to 10,10|{{0.5,0.5},{0.5,0.5}}
+multipolygon|10x10 raster covering 0,0 to 10,10|{{0,0,0,0,0.5,0.5,0,0,0,0},{0,0,0,0.5,1,1,0.5,0,0,0},{0,0,0.5,1,1,1,1,0.5,0,0},{0,0.5,1,1,1,1,1,1,0.5,0},{0.5,1,1,1,1,1,1,1,1,0.5},{0.5,1,1,1,1,1,1,1,1,0.5},{0,0.5,1,1,1,1,1,1,0.5,0},{0,0,0.5,1,1,1,1,0.5,0,0},{0,0,0,0.5,1,1,0.5,0,0,0},{0,0,0,0,0.5,0.5,0,0,0,0}}
+multipolygon|2x2 raster covering -10,-10 to 0,0|{{0,0},{0,0}}
+multipolygon|2x2 raster covering 5,5 to 6,6|{{1,1},{1,1}}
+linestring|2x2 raster covering 0,0 to 10,10|{{0,7.071068},{7.071068,0}}
+linestring|10x10 raster covering 0,0 to 10,10|{{0,0,0,0,0,0,0,0,0,1.414214},{0,0,0,0,0,0,0,0,1.414214,0},{0,0,0,0,0,0,0,1.414214,0,0},{0,0,0,0,0,0,1.414214,0,0,0},{0,0,0,0,0,1.414214,0,0,0,0},{0,0,0,0,1.414214,0,0,0,0,0},{0,0,0,1.414214,0,0,0,0,0,0},{0,0,1.414214,0,0,0,0,0,0,0},{0,1.414214,0,0,0,0,0,0,0,0},{1.414214,0,0,0,0,0,0,0,0,0}}
+linestring|2x2 raster covering -10,-10 to 0,0|{{0,0},{0,0}}
+linestring|2x2 raster covering 5,5 to 6,6|{{0,0.7071068},{0.7071068,0}}
+multilinestring|2x2 raster covering 0,0 to 10,10|{{7.071068,7.071068},{7.071068,7.071068}}
+multilinestring|10x10 raster covering 0,0 to 10,10|{{1.414214,0,0,0,0,0,0,0,0,1.414214},{0,1.414214,0,0,0,0,0,0,1.414214,0},{0,0,1.414214,0,0,0,0,1.414214,0,0},{0,0,0,1.414214,0,0,1.414214,0,0,0},{0,0,0,0,1.414214,1.414214,0,0,0,0},{0,0,0,0,1.414214,1.414214,0,0,0,0},{0,0,0,1.414214,0,0,1.414214,0,0,0},{0,0,1.414214,0,0,0,0,1.414214,0,0},{0,1.414214,0,0,0,0,0,0,1.414214,0},{1.414214,0,0,0,0,0,0,0,0,1.414214}}
+multilinestring|2x2 raster covering -10,-10 to 0,0|{{0,0},{0,0}}
+multilinestring|2x2 raster covering 5,5 to 6,6|{{0,0.7071068},{0.7071068,0}}
diff --git a/raster/test/regress/tests.mk b/raster/test/regress/tests.mk
index 9d75e50e1..7bcdda63b 100644
--- a/raster/test/regress/tests.mk
+++ b/raster/test/regress/tests.mk
@@ -10,6 +10,8 @@
 # *
 # **********************************************************************
 
+POSTGIS_GEOS_VERSION=31400
+
 override RUNTESTFLAGS := $(RUNTESTFLAGS) --raster
 
 override RUNTESTFLAGS_INTERNAL := \
@@ -147,3 +149,8 @@ RASTER_TESTS := $(RASTER_TEST_FIRST) \
 	$(RASTER_TEST_LAST)
 
 TESTS += $(RASTER_TESTS)
+
+ifeq ($(shell expr "$(POSTGIS_GEOS_VERSION)" ">=" 31400),1)
+    TESTS += \
+        $(top_srcdir)/raster/test/regress/rt_intersection_fractions
+endif
diff --git a/raster/test/regress/tests.mk b/raster/test/regress/tests.mk.in
similarity index 97%
copy from raster/test/regress/tests.mk
copy to raster/test/regress/tests.mk.in
index 9d75e50e1..111be04e7 100644
--- a/raster/test/regress/tests.mk
+++ b/raster/test/regress/tests.mk.in
@@ -10,6 +10,8 @@
 # *
 # **********************************************************************
 
+POSTGIS_GEOS_VERSION=@POSTGIS_GEOS_VERSION@
+
 override RUNTESTFLAGS := $(RUNTESTFLAGS) --raster
 
 override RUNTESTFLAGS_INTERNAL := \
@@ -147,3 +149,8 @@ RASTER_TESTS := $(RASTER_TEST_FIRST) \
 	$(RASTER_TEST_LAST)
 
 TESTS += $(RASTER_TESTS)
+
+ifeq ($(shell expr "$(POSTGIS_GEOS_VERSION)" ">=" 31400),1)
+    TESTS += \
+        $(top_srcdir)/raster/test/regress/rt_intersection_fractions
+endif
diff --git a/topology/test/load_large_topology-4326.sql b/topology/test/load_large_topology-4326.sql
new file mode 100644
index 000000000..77b2c94a3
--- /dev/null
+++ b/topology/test/load_large_topology-4326.sql
@@ -0,0 +1,205 @@
+--
+-- From examples in chapter 1.12.1 of
+-- "Spatial Topology and Network Data Models" (Oracle manual)
+--
+-- Modified to use postgis-based topology model.
+-- Loads the whole topology represented in Figure 1-1 of the
+-- manual
+--
+
+--ORA---------------------------------------------------------------------
+--ORA---- Main steps for using the topology data model with a topology
+--ORA---- built from edge, node, and face data
+--ORA---------------------------------------------------------------------
+--ORA---- 1. Create a topology.
+--ORA---- 2. Load (normally bulk-load) topology data
+--ORA----    (node, edge, and face tables).
+
+BEGIN;
+
+-- 1. Create the topology.
+--
+-- NOTE:
+--  Returns topology id... which depend on how many
+--  topologies where created in the regress database
+--  so we just check it is a number greater than 0
+--
+SELECT NULL FROM topology.CreateTopology('large_city_data', 4326, 0, false, 0, true);
+
+-- 2. Load topology data (node, edge, and face tables).
+-- Use INSERT statements here instead of a bulk-load utility.
+
+-- 2A. Insert data into <topology_name>.FACE table.
+
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000001); -- F1
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000002); -- F2
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000003); -- F3
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000004); -- F4
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000005); -- F5
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000006); -- F6
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000007); -- F7
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000008); -- F8
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000009); -- F9
+
+-- UPDATE Face id sequence
+SELECT NULL FROM setval('large_city_data.face_face_id_seq', 1000000000009);
+
+-- 2B. Insert data into <topology_name>.NODE table.
+-- N1
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000001, 'SRID=4326;POINT(8 30)', NULL);
+-- N2
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000002, 'SRID=4326;POINT(25 30)', NULL);
+-- N3
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000003, 'SRID=4326;POINT(25 35)', NULL);
+-- N4
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000004, 'SRID=4326;POINT(20 37)', 1000000000002);
+-- N5
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000005, 'SRID=4326;POINT(36 38)', NULL);
+-- N6
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000006, 'SRID=4326;POINT(57 33)', NULL);
+-- N7
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000007, 'SRID=4326;POINT(41 40)', NULL);
+-- N8
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000008, 'SRID=4326;POINT(9 6)', NULL);
+-- N9
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000009, 'SRID=4326;POINT(21 6)', NULL);
+-- N10
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000010, 'SRID=4326;POINT(35 6)', NULL);
+-- N11
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000011, 'SRID=4326;POINT(47 6)', NULL);
+-- N12
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000012, 'SRID=4326;POINT(47 14)', NULL);
+-- N13
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000013, 'SRID=4326;POINT(35 14)', NULL);
+-- N14
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000014, 'SRID=4326;POINT(21 14)', NULL);
+-- N15
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000015, 'SRID=4326;POINT(9 14)', NULL);
+-- N16
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000016, 'SRID=4326;POINT(9 22)', NULL);
+-- N17
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000017, 'SRID=4326;POINT(21 22)', NULL);
+-- N18
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000018, 'SRID=4326;POINT(35 22)', NULL);
+-- N19
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000019, 'SRID=4326;POINT(47 22)', NULL);
+-- N20
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000020, 'SRID=4326;POINT(4 31)', NULL);
+-- N21
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000021, 'SRID=4326;POINT(9 35)', NULL);
+-- N22
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000022, 'SRID=4326;POINT(13 35)', NULL);
+
+-- UPDATE Node id sequence
+SELECT NULL FROM setval('large_city_data.node_node_id_seq', 1000000000022);
+
+-- 2C. Insert data into <topology_name>.EDGE table.
+-- E1
+INSERT INTO large_city_data.edge VALUES(1000000000001, 1000000000001, 1000000000001, 1000000000001, -1000000000001, 1000000000001, 0,
+  'SRID=4326;LINESTRING(8 30, 16 30, 16 38, 3 38, 3 30, 8 30)');
+-- E2
+INSERT INTO large_city_data.edge VALUES(1000000000002, 1000000000002, 1000000000002, 1000000000003, -1000000000002, 1000000000002, 0,
+  'SRID=4326;LINESTRING(25 30, 31 30, 31 40, 17 40, 17 30, 25 30)');
+-- E3
+INSERT INTO large_city_data.edge VALUES(1000000000003, 1000000000002, 1000000000003, -1000000000003, 1000000000002, 1000000000002, 1000000000002,
+  'SRID=4326;LINESTRING(25 30, 25 35)');
+-- E4
+INSERT INTO large_city_data.edge VALUES(1000000000004, 1000000000005, 1000000000006, -1000000000005, 1000000000004, 0, 0,
+    'SRID=4326;LINESTRING(36 38, 38 35, 41 34, 42 33, 45 32, 47 28, 50 28, 52 32, 57 33)');
+-- E5
+INSERT INTO large_city_data.edge VALUES(1000000000005, 1000000000007, 1000000000006, -1000000000004, 1000000000005, 0, 0,
+    'SRID=4326;LINESTRING(41 40, 45 40, 47 42, 62 41, 61 38, 59 39, 57 36, 57 33)');
+-- E6
+INSERT INTO large_city_data.edge VALUES(1000000000006, 1000000000016, 1000000000017, 1000000000007, -1000000000021, 0, 1000000000003,
+    'SRID=4326;LINESTRING(9 22, 21 22)');
+-- E7
+INSERT INTO large_city_data.edge VALUES(1000000000007, 1000000000017, 1000000000018, 1000000000008, -1000000000019, 0, 1000000000004,
+    'SRID=4326;LINESTRING(21 22, 35 22)');
+-- E8
+INSERT INTO large_city_data.edge VALUES(1000000000008, 1000000000018, 1000000000019, -1000000000015, -1000000000017, 0, 1000000000005,
+    'SRID=4326;LINESTRING(35 22, 47 22)');
+-- E9
+INSERT INTO large_city_data.edge VALUES(1000000000009, 1000000000015, 1000000000014, 1000000000019, -1000000000022, 1000000000003, 1000000000006,
+    'SRID=4326;LINESTRING(9 14, 21 14)');
+-- E10
+INSERT INTO large_city_data.edge VALUES(1000000000010, 1000000000013, 1000000000014, -1000000000020, 1000000000017, 1000000000007, 1000000000004,
+    'SRID=4326;LINESTRING(35 14, 21 14)');
+-- E11
+INSERT INTO large_city_data.edge VALUES(1000000000011, 1000000000013, 1000000000012, 1000000000015, -1000000000018, 1000000000005, 1000000000008,
+    'SRID=4326;LINESTRING(35 14, 47 14)');
+-- E12
+INSERT INTO large_city_data.edge VALUES(1000000000012, 1000000000008, 1000000000009, 1000000000020, 1000000000022, 1000000000006, 0,
+    'SRID=4326;LINESTRING(9 6, 21 6)');
+-- E13
+INSERT INTO large_city_data.edge VALUES(1000000000013, 1000000000009, 1000000000010, 1000000000018, -1000000000012, 1000000000007, 0,
+    'SRID=4326;LINESTRING(21 6, 35 6)');
+-- E14
+INSERT INTO large_city_data.edge VALUES(1000000000014, 1000000000010, 1000000000011, 1000000000016, -1000000000013, 1000000000008, 0,
+    'SRID=4326;LINESTRING(35 6, 47 6)');
+-- E15
+INSERT INTO large_city_data.edge VALUES(1000000000015, 1000000000012, 1000000000019, -1000000000008, -1000000000016, 1000000000005, 0,
+    'SRID=4326;LINESTRING(47 14, 47 22)');
+-- E16
+INSERT INTO large_city_data.edge VALUES(1000000000016, 1000000000011, 1000000000012, -1000000000011, -1000000000014, 1000000000008, 0,
+    'SRID=4326;LINESTRING(47 6, 47 14)');
+-- E17
+INSERT INTO large_city_data.edge VALUES(1000000000017, 1000000000013, 1000000000018, -1000000000007, 1000000000011, 1000000000004, 1000000000005,
+    'SRID=4326;LINESTRING(35 14, 35 22)');
+-- E18
+INSERT INTO large_city_data.edge VALUES(1000000000018, 1000000000010, 1000000000013, 1000000000010, 1000000000014, 1000000000007, 1000000000008,
+    'SRID=4326;LINESTRING(35 6, 35 14)');
+-- E19
+INSERT INTO large_city_data.edge VALUES(1000000000019, 1000000000014, 1000000000017, -1000000000006, -1000000000010, 1000000000003, 1000000000004,
+    'SRID=4326;LINESTRING(21 14, 21 22)');
+-- E20
+INSERT INTO large_city_data.edge VALUES(1000000000020, 1000000000009, 1000000000014, -1000000000009, 1000000000013, 1000000000006, 1000000000007,
+    'SRID=4326;LINESTRING(21 6, 21 14)');
+-- E21
+INSERT INTO large_city_data.edge VALUES(1000000000021, 1000000000015, 1000000000016, 1000000000006, 1000000000009, 0, 1000000000003,
+    'SRID=4326;LINESTRING(9 14, 9 22)');
+-- E22
+INSERT INTO large_city_data.edge VALUES(1000000000022, 1000000000008, 1000000000015, 1000000000021, 1000000000012, 0, 1000000000006,
+    'SRID=4326;LINESTRING(9 6, 9 14)');
+-- E25
+INSERT INTO large_city_data.edge VALUES(1000000000025, 1000000000021, 1000000000022, -1000000000025, 1000000000025, 1000000000001, 1000000000001,
+  'SRID=4326;LINESTRING(9 35, 13 35)');
+-- E26
+INSERT INTO large_city_data.edge VALUES(1000000000026, 1000000000020, 1000000000020, 1000000000026, -1000000000026, 1000000000009, 1000000000001,
+  'SRID=4326;LINESTRING(4 31, 7 31, 7 34, 4 34, 4 31)');
+
+-- UPDATE Edge id sequence
+SELECT NULL FROM setval('large_city_data.edge_data_edge_id_seq', 1000000000026);
+
+-- Set face minimum bounding rectangle
+UPDATE large_city_data.face set mbr = ST_SetSRID( ( select st_extent(geom) from large_city_data.edge where left_face = face_id or right_face = face_id ), 4326 ) where face_id != 0;
+
+-- Validate the loaded topology
+SELECT 'load_validation', *
+FROM ValidateTopology('large_city_data')
+WHERE error IS NOT NULL;
+
+END;
+
+
diff --git a/topology/test/load_large_topology.sql b/topology/test/load_large_topology.sql
new file mode 100644
index 000000000..d003f49b3
--- /dev/null
+++ b/topology/test/load_large_topology.sql
@@ -0,0 +1,205 @@
+--
+-- From examples in chapter 1.12.1 of
+-- "Spatial Topology and Network Data Models" (Oracle manual)
+--
+-- Modified to use postgis-based topology model.
+-- Loads the whole topology represented in Figure 1-1 of the
+-- manual
+--
+
+--ORA---------------------------------------------------------------------
+--ORA---- Main steps for using the topology data model with a topology
+--ORA---- built from edge, node, and face data
+--ORA---------------------------------------------------------------------
+--ORA---- 1. Create a topology.
+--ORA---- 2. Load (normally bulk-load) topology data
+--ORA----    (node, edge, and face tables).
+
+BEGIN;
+
+-- 1. Create the topology.
+--
+-- NOTE:
+--  Returns topology id... which depend on how many
+--  topologies where created in the regress database
+--  so we just check it is a number greater than 0
+--
+SELECT NULL FROM topology.CreateTopology('large_city_data', -1, 0, false, 0, true);
+
+-- 2. Load topology data (node, edge, and face tables).
+-- Use INSERT statements here instead of a bulk-load utility.
+
+-- 2A. Insert data into <topology_name>.FACE table.
+
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000001); -- F1
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000002); -- F2
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000003); -- F3
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000004); -- F4
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000005); -- F5
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000006); -- F6
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000007); -- F7
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000008); -- F8
+INSERT INTO large_city_data.face(face_id) VALUES(1000000000009); -- F9
+
+-- UPDATE Face id sequence
+SELECT NULL FROM setval('large_city_data.face_face_id_seq', 1000000000009);
+
+-- 2B. Insert data into <topology_name>.NODE table.
+-- N1
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000001, 'SRID=-1;POINT(8 30)', NULL);
+-- N2
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000002, 'SRID=-1;POINT(25 30)', NULL);
+-- N3
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000003, 'SRID=-1;POINT(25 35)', NULL);
+-- N4
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000004, 'SRID=-1;POINT(20 37)', 1000000000002);
+-- N5
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000005, 'SRID=-1;POINT(36 38)', NULL);
+-- N6
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000006, 'SRID=-1;POINT(57 33)', NULL);
+-- N7
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000007, 'SRID=-1;POINT(41 40)', NULL);
+-- N8
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000008, 'SRID=-1;POINT(9 6)', NULL);
+-- N9
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000009, 'SRID=-1;POINT(21 6)', NULL);
+-- N10
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000010, 'SRID=-1;POINT(35 6)', NULL);
+-- N11
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000011, 'SRID=-1;POINT(47 6)', NULL);
+-- N12
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000012, 'SRID=-1;POINT(47 14)', NULL);
+-- N13
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000013, 'SRID=-1;POINT(35 14)', NULL);
+-- N14
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000014, 'SRID=-1;POINT(21 14)', NULL);
+-- N15
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000015, 'SRID=-1;POINT(9 14)', NULL);
+-- N16
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000016, 'SRID=-1;POINT(9 22)', NULL);
+-- N17
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000017, 'SRID=-1;POINT(21 22)', NULL);
+-- N18
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000018, 'SRID=-1;POINT(35 22)', NULL);
+-- N19
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000019, 'SRID=-1;POINT(47 22)', NULL);
+-- N20
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000020, 'SRID=-1;POINT(4 31)', NULL);
+-- N21
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000021, 'SRID=-1;POINT(9 35)', NULL);
+-- N22
+INSERT INTO large_city_data.node(node_id, geom, containing_face)
+	VALUES(1000000000022, 'SRID=-1;POINT(13 35)', NULL);
+
+-- UPDATE Node id sequence
+SELECT NULL FROM setval('large_city_data.node_node_id_seq', 1000000000022);
+
+-- 2C. Insert data into <topology_name>.EDGE table.
+-- E1
+INSERT INTO large_city_data.edge VALUES(1000000000001, 1000000000001, 1000000000001, 1000000000001, -1000000000001, 1000000000001, 0,
+  'SRID=-1;LINESTRING(8 30, 16 30, 16 38, 3 38, 3 30, 8 30)');
+-- E2
+INSERT INTO large_city_data.edge VALUES(1000000000002, 1000000000002, 1000000000002, 1000000000003, -1000000000002, 1000000000002, 0,
+  'SRID=-1;LINESTRING(25 30, 31 30, 31 40, 17 40, 17 30, 25 30)');
+-- E3
+INSERT INTO large_city_data.edge VALUES(1000000000003, 1000000000002, 1000000000003, -1000000000003, 1000000000002, 1000000000002, 1000000000002,
+  'SRID=-1;LINESTRING(25 30, 25 35)');
+-- E4
+INSERT INTO large_city_data.edge VALUES(1000000000004, 1000000000005, 1000000000006, -1000000000005, 1000000000004, 0, 0,
+    'SRID=-1;LINESTRING(36 38, 38 35, 41 34, 42 33, 45 32, 47 28, 50 28, 52 32, 57 33)');
+-- E5
+INSERT INTO large_city_data.edge VALUES(1000000000005, 1000000000007, 1000000000006, -1000000000004, 1000000000005, 0, 0,
+    'SRID=-1;LINESTRING(41 40, 45 40, 47 42, 62 41, 61 38, 59 39, 57 36, 57 33)');
+-- E6
+INSERT INTO large_city_data.edge VALUES(1000000000006, 1000000000016, 1000000000017, 1000000000007, -1000000000021, 0, 1000000000003,
+    'SRID=-1;LINESTRING(9 22, 21 22)');
+-- E7
+INSERT INTO large_city_data.edge VALUES(1000000000007, 1000000000017, 1000000000018, 1000000000008, -1000000000019, 0, 1000000000004,
+    'SRID=-1;LINESTRING(21 22, 35 22)');
+-- E8
+INSERT INTO large_city_data.edge VALUES(1000000000008, 1000000000018, 1000000000019, -1000000000015, -1000000000017, 0, 1000000000005,
+    'SRID=-1;LINESTRING(35 22, 47 22)');
+-- E9
+INSERT INTO large_city_data.edge VALUES(1000000000009, 1000000000015, 1000000000014, 1000000000019, -1000000000022, 1000000000003, 1000000000006,
+    'SRID=-1;LINESTRING(9 14, 21 14)');
+-- E10
+INSERT INTO large_city_data.edge VALUES(1000000000010, 1000000000013, 1000000000014, -1000000000020, 1000000000017, 1000000000007, 1000000000004,
+    'SRID=-1;LINESTRING(35 14, 21 14)');
+-- E11
+INSERT INTO large_city_data.edge VALUES(1000000000011, 1000000000013, 1000000000012, 1000000000015, -1000000000018, 1000000000005, 1000000000008,
+    'SRID=-1;LINESTRING(35 14, 47 14)');
+-- E12
+INSERT INTO large_city_data.edge VALUES(1000000000012, 1000000000008, 1000000000009, 1000000000020, 1000000000022, 1000000000006, 0,
+    'SRID=-1;LINESTRING(9 6, 21 6)');
+-- E13
+INSERT INTO large_city_data.edge VALUES(1000000000013, 1000000000009, 1000000000010, 1000000000018, -1000000000012, 1000000000007, 0,
+    'SRID=-1;LINESTRING(21 6, 35 6)');
+-- E14
+INSERT INTO large_city_data.edge VALUES(1000000000014, 1000000000010, 1000000000011, 1000000000016, -1000000000013, 1000000000008, 0,
+    'SRID=-1;LINESTRING(35 6, 47 6)');
+-- E15
+INSERT INTO large_city_data.edge VALUES(1000000000015, 1000000000012, 1000000000019, -1000000000008, -1000000000016, 1000000000005, 0,
+    'SRID=-1;LINESTRING(47 14, 47 22)');
+-- E16
+INSERT INTO large_city_data.edge VALUES(1000000000016, 1000000000011, 1000000000012, -1000000000011, -1000000000014, 1000000000008, 0,
+    'SRID=-1;LINESTRING(47 6, 47 14)');
+-- E17
+INSERT INTO large_city_data.edge VALUES(1000000000017, 1000000000013, 1000000000018, -1000000000007, 1000000000011, 1000000000004, 1000000000005,
+    'SRID=-1;LINESTRING(35 14, 35 22)');
+-- E18
+INSERT INTO large_city_data.edge VALUES(1000000000018, 1000000000010, 1000000000013, 1000000000010, 1000000000014, 1000000000007, 1000000000008,
+    'SRID=-1;LINESTRING(35 6, 35 14)');
+-- E19
+INSERT INTO large_city_data.edge VALUES(1000000000019, 1000000000014, 1000000000017, -1000000000006, -1000000000010, 1000000000003, 1000000000004,
+    'SRID=-1;LINESTRING(21 14, 21 22)');
+-- E20
+INSERT INTO large_city_data.edge VALUES(1000000000020, 1000000000009, 1000000000014, -1000000000009, 1000000000013, 1000000000006, 1000000000007,
+    'SRID=-1;LINESTRING(21 6, 21 14)');
+-- E21
+INSERT INTO large_city_data.edge VALUES(1000000000021, 1000000000015, 1000000000016, 1000000000006, 1000000000009, 0, 1000000000003,
+    'SRID=-1;LINESTRING(9 14, 9 22)');
+-- E22
+INSERT INTO large_city_data.edge VALUES(1000000000022, 1000000000008, 1000000000015, 1000000000021, 1000000000012, 0, 1000000000006,
+    'SRID=-1;LINESTRING(9 6, 9 14)');
+-- E25
+INSERT INTO large_city_data.edge VALUES(1000000000025, 1000000000021, 1000000000022, -1000000000025, 1000000000025, 1000000000001, 1000000000001,
+  'SRID=-1;LINESTRING(9 35, 13 35)');
+-- E26
+INSERT INTO large_city_data.edge VALUES(1000000000026, 1000000000020, 1000000000020, 1000000000026, -1000000000026, 1000000000009, 1000000000001,
+  'SRID=-1;LINESTRING(4 31, 7 31, 7 34, 4 34, 4 31)');
+
+-- UPDATE Edge id sequence
+SELECT NULL FROM setval('large_city_data.edge_data_edge_id_seq', 1000000000026);
+
+-- Set face minimum bounding rectangle
+UPDATE large_city_data.face set mbr = ST_SetSRID( ( select st_extent(geom) from large_city_data.edge where left_face = face_id or right_face = face_id ), -1 ) where face_id != 0;
+
+-- Validate the loaded topology
+SELECT 'load_validation', *
+FROM ValidateTopology('large_city_data')
+WHERE error IS NOT NULL;
+
+END;
+
+

commit af18e8f2fc5df5ef6ab96e897249b382cd89e88a
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Fri Aug 15 15:34:13 2025 -0700

    Harmonize conditional test variable handling

diff --git a/regress/core/tests.mk.in b/regress/core/tests.mk.in
index 3dd65b92f..a959ad92f 100644
--- a/regress/core/tests.mk.in
+++ b/regress/core/tests.mk.in
@@ -17,6 +17,7 @@ POSTGIS_GEOS_VERSION=@POSTGIS_GEOS_VERSION@
 HAVE_JSON=@HAVE_JSON@
 HAVE_SPGIST=@HAVE_SPGIST@
 INTERRUPTTESTS=@INTERRUPTTESTS@
+HAVE_PROTOBUF=@HAVE_PROTOBUF@
 
 current_dir := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
 
@@ -199,14 +200,13 @@ endif
 
 ifeq ($(HAVE_SPGIST),yes)
 	TESTS += \
-	$(top_srcdir)/regress/core/regress_spgist_index_2d \
-	$(top_srcdir)/regress/core/regress_spgist_index_3d \
-	$(top_srcdir)/regress/core/regress_spgist_index_nd
+		$(top_srcdir)/regress/core/regress_spgist_index_2d \
+		$(top_srcdir)/regress/core/regress_spgist_index_3d \
+		$(top_srcdir)/regress/core/regress_spgist_index_nd
 endif
 
-ifeq (@HAVE_PROTOBUF@,yes)
-	# protobuf-c adds:
-	# ST_AsMVT, ST_AsGeobuf
+ifeq ($(HAVE_PROTOBUF),yes)
+	# protobuf-c adds: ST_AsMVT, ST_AsGeobuf
 	TESTS += \
 		$(top_srcdir)/regress/core/mvt \
 		$(top_srcdir)/regress/core/mvt_jsonb \

commit 1b0336578b0f1107d472e2c85f35b9cfa29edf58
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Fri Aug 15 15:24:32 2025 -0700

    Repair (hopefully) docbook xml

diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml
index 7b03d5588..724761662 100644
--- a/doc/reference_raster.xml
+++ b/doc/reference_raster.xml
@@ -13829,8 +13829,6 @@ GROUP BY t1.rast;
                 </refsection>
             </refentry>
 
-            <refentry xml:id="RT_ST_Contour">
-
             <refentry xml:id="RT_ST_InterpolateRaster">
       <refnamediv>
         <refname>ST_InterpolateRaster</refname>
@@ -13923,6 +13921,7 @@ GROUP BY t1.rast;
 
 </refentry>
 
+            <refentry xml:id="RT_ST_Contour">
       <refnamediv>
         <refname>ST_Contour</refname>
 

commit 55aa87c653fefad2a23e4cce48b58a580a9027db
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Fri Aug 15 11:32:47 2025 -0700

    Move contour and interpolate into the DEM processing section

diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml
index 46efd0632..7b03d5588 100644
--- a/doc/reference_raster.xml
+++ b/doc/reference_raster.xml
@@ -1326,197 +1326,6 @@ WHERE short_name = 'GTiff') As g;
       </refsection>
     </refentry>
 
-    <refentry xml:id="RT_ST_Contour">
-      <refnamediv>
-        <refname>ST_Contour</refname>
-
-        <refpurpose>Generates a set of vector contours from the provided raster
-            band, using the <link xlink:href="https://gdal.org/api/gdal_alg.html?highlight=contour#_CPPv421GDALContourGenerateEx15GDALRasterBandHPv12CSLConstList16GDALProgressFuncPv">GDAL contouring algorithm</link>.</refpurpose>
-      </refnamediv>
-
-      <refsynopsisdiv>
-        <funcsynopsis>
-          <funcprototype>
-            <funcdef>setof record <function>ST_Contour</function></funcdef>
-            <paramdef><type>raster </type><parameter>rast</parameter></paramdef>
-            <paramdef choice="opt"><type>integer </type><parameter>bandnumber=1</parameter></paramdef>
-            <paramdef choice="opt"><type>double precision </type><parameter>level_interval=100.0</parameter></paramdef>
-            <paramdef choice="opt"><type>double precision </type><parameter>level_base=0.0</parameter></paramdef>
-            <paramdef choice="opt"><type>double precision[] </type><parameter>fixed_levels=ARRAY[]</parameter></paramdef>
-            <paramdef choice="opt"><type>boolean </type><parameter>polygonize=false</parameter></paramdef>
-          </funcprototype>
-        </funcsynopsis>
-      </refsynopsisdiv>
-
-      <refsection>
-        <title>Description</title>
-
-        <para>
-            Generates a set of vector contours from the provided raster
-            band, using the <link xlink:href="https://gdal.org/api/gdal_alg.html?highlight=contour#_CPPv421GDALContourGenerateEx15GDALRasterBandHPv12CSLConstList16GDALProgressFuncPv">GDAL contouring algorithm</link>.</para>
-        <para>
-            When the <varname>fixed_levels</varname> parameter is a non-empty
-            array, the <varname>level_interval</varname> and <varname>level_base</varname> parameters are ignored.
-        </para>
-
-        <para>
-            Input parameters are:
-        </para>
-        <variablelist>
-            <varlistentry><term><varname>rast</varname></term>
-                <listitem><para>The raster to generate the contour of</para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>bandnumber</varname></term>
-                <listitem><para>The band to generate the contour of</para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>level_interval</varname></term>
-                <listitem><para>The elevation interval between contours generated</para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>level_base</varname></term>
-                <listitem><para>The "base" relative to which contour intervals are applied,
-                this is normally zero, but could be different.
-                To generate 10m contours at 5, 15, 25, ... the LEVEL_BASE would be 5.</para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>fixed_levels</varname></term>
-                <listitem><para>The elevation interval between contours generated</para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>polygonize</varname></term>
-                <listitem><para>If <literal>true</literal>, contour polygons will be created, rather than polygon lines.</para></listitem>
-            </varlistentry>
-        </variablelist>
-
-        <para>
-            Return values are a set of records with the following attributes:
-        </para>
-        <variablelist>
-            <varlistentry><term><varname>geom</varname></term>
-                <listitem><para>The geometry of the contour line.</para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>id</varname></term>
-                <listitem><para>A unique identifier given to the contour line by GDAL.</para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>value</varname></term>
-                <listitem><para>The raster value the line represents. For an elevation DEM input, this would be the elevation of the output contour.</para></listitem>
-            </varlistentry>
-        </variablelist>
-        <para role="availability" conformance="3.2.0">Availability: 3.2.0</para>
-
-      </refsection>
-
-      <refsection>
-        <title>Example</title>
-
-        <programlisting>WITH c AS (
-SELECT (ST_Contour(rast, 1, fixed_levels => ARRAY[100.0, 200.0, 300.0])).*
-FROM dem_grid WHERE rid = 1
-)
-SELECT st_astext(geom), id, value
-FROM c;</programlisting>
-
-    </refsection>
-    <refsection>
-        <title>See Also</title>
-        <para>
-        <xref linkend="RT_ST_InterpolateRaster"/>
-        </para>
-    </refsection>
-
-</refentry>
-
-
-    <refentry xml:id="RT_ST_InterpolateRaster">
-      <refnamediv>
-        <refname>ST_InterpolateRaster</refname>
-
-        <refpurpose>Interpolates a gridded surface based on an input set
-        of 3-d points, using the X- and Y-values to position the points on
-        the grid and the Z-value of the points as the surface elevation.
-        </refpurpose>
-      </refnamediv>
-
-      <refsynopsisdiv>
-        <funcsynopsis>
-          <funcprototype>
-            <funcdef>raster <function>ST_InterpolateRaster</function></funcdef>
-            <paramdef><type>geometry </type><parameter>input_points</parameter></paramdef>
-            <paramdef><type>text </type><parameter>algorithm_options</parameter></paramdef>
-            <paramdef><type>raster </type><parameter>template</parameter></paramdef>
-            <paramdef choice="opt"><type>integer </type><parameter>template_band_num=1</parameter></paramdef>
-          </funcprototype>
-        </funcsynopsis>
-      </refsynopsisdiv>
-
-      <refsection>
-        <title>Description</title>
-
-        <para>
-            Interpolates a gridded surface based on an input set
-            of 3-d points, using the X- and Y-values to position the points on
-            the grid and the Z-value of the points as the surface elevation.
-
-            There are five interpolation algorithms available: inverse distance,
-            inverse distance nearest-neighbor, moving average, nearest neighbor,
-            and linear interpolation.
-            See the <link xlink:href="https://gdal.org/programs/gdal_grid.html#interpolation-algorithms">gdal_grid documentation</link>
-            for more details on the algorithms and their parameters. For more
-            information on how interpolations are calculated, see the
-            <link xlink:href="https://gdal.org/tutorials/gdal_grid_tut.html">GDAL grid
-            tutorial</link>.
-        </para>
-        <para>
-            Input parameters are:
-        </para>
-        <variablelist>
-            <varlistentry><term><varname>input_points</varname></term>
-                <listitem><para>The points to drive the interpolation. Any
-                    geometry with Z-values is acceptable, all points in the
-                    input will be used.</para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>algorithm_options</varname></term>
-                <listitem><para>A string defining the algorithm and
-                    algorithm options, in the format used by <link xlink:href="https://gdal.org/programs/gdal_grid.html#interpolation-algorithms">gdal_grid</link>.
-                    For example, for an inverse-distance interpolation with a
-                    smoothing of 2, you would use "invdist:smoothing=2.0"
-                </para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>template</varname></term>
-                <listitem><para>A raster template to drive the geometry of the
-                    output raster. The width, height, pixel size, spatial extent
-                    and pixel type will be read from this template.
-                </para></listitem>
-            </varlistentry>
-            <varlistentry><term><varname>template_band_num</varname></term>
-                <listitem><para>By default the first band in the template raster
-                    is used to drive the output raster, but that can be adjusted
-                    with this parameter.
-                </para></listitem>
-            </varlistentry>
-        </variablelist>
-        <para role="availability" conformance="3.2.0">Availability: 3.2.0</para>
-
-      </refsection>
-
-      <refsection>
-        <title>Example</title>
-
-<programlisting>SELECT ST_InterpolateRaster(
-    'MULTIPOINT(10.5 9.5 1000, 11.5 8.5 1000, 10.5 8.5 500, 11.5 9.5 500)'::geometry,
-    'invdist:smoothing:2.0',
-    ST_AddBand(ST_MakeEmptyRaster(200, 400, 10, 10, 0.01, -0.005, 0, 0), '16BSI')
-)</programlisting>
-
-        </refsection>
-
-        <refsection>
-            <title>See Also</title>
-            <para>
-            <xref linkend="RT_ST_Contour"/>
-            </para>
-        </refsection>
-
-</refentry>
-
-
         <refentry xml:id="RT_UpdateRasterSRID">
             <refnamediv>
                 <refname>UpdateRasterSRID</refname>
@@ -14020,6 +13829,196 @@ GROUP BY t1.rast;
                 </refsection>
             </refentry>
 
+            <refentry xml:id="RT_ST_Contour">
+
+            <refentry xml:id="RT_ST_InterpolateRaster">
+      <refnamediv>
+        <refname>ST_InterpolateRaster</refname>
+
+        <refpurpose>Interpolates a gridded surface based on an input set
+        of 3-d points, using the X- and Y-values to position the points on
+        the grid and the Z-value of the points as the surface elevation.
+        </refpurpose>
+      </refnamediv>
+
+      <refsynopsisdiv>
+        <funcsynopsis>
+          <funcprototype>
+            <funcdef>raster <function>ST_InterpolateRaster</function></funcdef>
+            <paramdef><type>geometry </type><parameter>input_points</parameter></paramdef>
+            <paramdef><type>text </type><parameter>algorithm_options</parameter></paramdef>
+            <paramdef><type>raster </type><parameter>template</parameter></paramdef>
+            <paramdef choice="opt"><type>integer </type><parameter>template_band_num=1</parameter></paramdef>
+          </funcprototype>
+        </funcsynopsis>
+      </refsynopsisdiv>
+
+      <refsection>
+        <title>Description</title>
+
+        <para>
+            Interpolates a gridded surface based on an input set
+            of 3-d points, using the X- and Y-values to position the points on
+            the grid and the Z-value of the points as the surface elevation.
+
+            There are five interpolation algorithms available: inverse distance,
+            inverse distance nearest-neighbor, moving average, nearest neighbor,
+            and linear interpolation.
+            See the <link xlink:href="https://gdal.org/programs/gdal_grid.html#interpolation-algorithms">gdal_grid documentation</link>
+            for more details on the algorithms and their parameters. For more
+            information on how interpolations are calculated, see the
+            <link xlink:href="https://gdal.org/tutorials/gdal_grid_tut.html">GDAL grid
+            tutorial</link>.
+        </para>
+        <para>
+            Input parameters are:
+        </para>
+        <variablelist>
+            <varlistentry><term><varname>input_points</varname></term>
+                <listitem><para>The points to drive the interpolation. Any
+                    geometry with Z-values is acceptable, all points in the
+                    input will be used.</para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>algorithm_options</varname></term>
+                <listitem><para>A string defining the algorithm and
+                    algorithm options, in the format used by <link xlink:href="https://gdal.org/programs/gdal_grid.html#interpolation-algorithms">gdal_grid</link>.
+                    For example, for an inverse-distance interpolation with a
+                    smoothing of 2, you would use "invdist:smoothing=2.0"
+                </para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>template</varname></term>
+                <listitem><para>A raster template to drive the geometry of the
+                    output raster. The width, height, pixel size, spatial extent
+                    and pixel type will be read from this template.
+                </para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>template_band_num</varname></term>
+                <listitem><para>By default the first band in the template raster
+                    is used to drive the output raster, but that can be adjusted
+                    with this parameter.
+                </para></listitem>
+            </varlistentry>
+        </variablelist>
+        <para role="availability" conformance="3.2.0">Availability: 3.2.0</para>
+
+      </refsection>
+
+      <refsection>
+        <title>Example</title>
+
+<programlisting>SELECT ST_InterpolateRaster(
+    'MULTIPOINT(10.5 9.5 1000, 11.5 8.5 1000, 10.5 8.5 500, 11.5 9.5 500)'::geometry,
+    'invdist:smoothing:2.0',
+    ST_AddBand(ST_MakeEmptyRaster(200, 400, 10, 10, 0.01, -0.005, 0, 0), '16BSI')
+)</programlisting>
+
+        </refsection>
+
+        <refsection>
+            <title>See Also</title>
+            <para>
+            <xref linkend="RT_ST_Contour"/>
+            </para>
+        </refsection>
+
+</refentry>
+
+      <refnamediv>
+        <refname>ST_Contour</refname>
+
+        <refpurpose>Generates a set of vector contours from the provided raster
+            band, using the <link xlink:href="https://gdal.org/api/gdal_alg.html?highlight=contour#_CPPv421GDALContourGenerateEx15GDALRasterBandHPv12CSLConstList16GDALProgressFuncPv">GDAL contouring algorithm</link>.</refpurpose>
+      </refnamediv>
+
+      <refsynopsisdiv>
+        <funcsynopsis>
+          <funcprototype>
+            <funcdef>setof record <function>ST_Contour</function></funcdef>
+            <paramdef><type>raster </type><parameter>rast</parameter></paramdef>
+            <paramdef choice="opt"><type>integer </type><parameter>bandnumber=1</parameter></paramdef>
+            <paramdef choice="opt"><type>double precision </type><parameter>level_interval=100.0</parameter></paramdef>
+            <paramdef choice="opt"><type>double precision </type><parameter>level_base=0.0</parameter></paramdef>
+            <paramdef choice="opt"><type>double precision[] </type><parameter>fixed_levels=ARRAY[]</parameter></paramdef>
+            <paramdef choice="opt"><type>boolean </type><parameter>polygonize=false</parameter></paramdef>
+          </funcprototype>
+        </funcsynopsis>
+      </refsynopsisdiv>
+
+      <refsection>
+        <title>Description</title>
+
+        <para>
+            Generates a set of vector contours from the provided raster
+            band, using the <link xlink:href="https://gdal.org/api/gdal_alg.html?highlight=contour#_CPPv421GDALContourGenerateEx15GDALRasterBandHPv12CSLConstList16GDALProgressFuncPv">GDAL contouring algorithm</link>.</para>
+        <para>
+            When the <varname>fixed_levels</varname> parameter is a non-empty
+            array, the <varname>level_interval</varname> and <varname>level_base</varname> parameters are ignored.
+        </para>
+
+        <para>
+            Input parameters are:
+        </para>
+        <variablelist>
+            <varlistentry><term><varname>rast</varname></term>
+                <listitem><para>The raster to generate the contour of</para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>bandnumber</varname></term>
+                <listitem><para>The band to generate the contour of</para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>level_interval</varname></term>
+                <listitem><para>The elevation interval between contours generated</para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>level_base</varname></term>
+                <listitem><para>The "base" relative to which contour intervals are applied,
+                this is normally zero, but could be different.
+                To generate 10m contours at 5, 15, 25, ... the LEVEL_BASE would be 5.</para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>fixed_levels</varname></term>
+                <listitem><para>The elevation interval between contours generated</para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>polygonize</varname></term>
+                <listitem><para>If <literal>true</literal>, contour polygons will be created, rather than polygon lines.</para></listitem>
+            </varlistentry>
+        </variablelist>
+
+        <para>
+            Return values are a set of records with the following attributes:
+        </para>
+        <variablelist>
+            <varlistentry><term><varname>geom</varname></term>
+                <listitem><para>The geometry of the contour line.</para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>id</varname></term>
+                <listitem><para>A unique identifier given to the contour line by GDAL.</para></listitem>
+            </varlistentry>
+            <varlistentry><term><varname>value</varname></term>
+                <listitem><para>The raster value the line represents. For an elevation DEM input, this would be the elevation of the output contour.</para></listitem>
+            </varlistentry>
+        </variablelist>
+        <para role="availability" conformance="3.2.0">Availability: 3.2.0</para>
+
+      </refsection>
+
+      <refsection>
+        <title>Example</title>
+
+        <programlisting>WITH c AS (
+SELECT (ST_Contour(rast, 1, fixed_levels => ARRAY[100.0, 200.0, 300.0])).*
+FROM dem_grid WHERE rid = 1
+)
+SELECT st_astext(geom), id, value
+FROM c;</programlisting>
+
+    </refsection>
+    <refsection>
+        <title>See Also</title>
+        <para>
+        <xref linkend="RT_ST_InterpolateRaster"/>
+        </para>
+    </refsection>
+
+</refentry>
+
         </section> <!-- /DEM -->
 
         <section xml:id="Raster_Processing_Geometry">

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

Summary of changes:
 NEWS                                               |   1 +
 configure.ac                                       |   1 +
 doc/reference_raster.xml                           | 446 ++++++++++++---------
 raster/rt_core/librtcore.h                         |  19 +
 raster/rt_core/rt_spatial_relationship.c           |  96 +++++
 raster/rt_pg/rtpg_spatial_relationship.c           |  46 +++
 raster/rt_pg/rtpostgis.sql.in                      |   9 +
 raster/test/regress/rt_intersection_fractions.sql  |  75 ++++
 .../regress/rt_intersection_fractions_expected     |  16 +
 raster/test/regress/tests.mk                       |   7 +
 raster/test/regress/{tests.mk => tests.mk.in}      |   7 +
 regress/core/tests.mk.in                           |  12 +-
 ...opology.sql.in => load_large_topology-4326.sql} |  97 ++---
 ...rge_topology.sql.in => load_large_topology.sql} |  97 ++---
 14 files changed, 636 insertions(+), 293 deletions(-)
 create mode 100644 raster/test/regress/rt_intersection_fractions.sql
 create mode 100644 raster/test/regress/rt_intersection_fractions_expected
 copy raster/test/regress/{tests.mk => tests.mk.in} (97%)
 copy topology/test/{load_large_topology.sql.in => load_large_topology-4326.sql} (73%)
 copy topology/test/{load_large_topology.sql.in => load_large_topology.sql} (73%)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list