[postgis-tickets] r17587 - Add ST_TileEnvelope utility function

Paul Ramsey pramsey at cleverelephant.ca
Wed Jul 10 01:44:52 PDT 2019


Author: pramsey
Date: 2019-07-10 13:44:52 -0700 (Wed, 10 Jul 2019)
New Revision: 17587

Modified:
   trunk/NEWS
   trunk/doc/reference_constructor.xml
   trunk/doc/reference_output.xml
   trunk/postgis/lwgeom_functions_basic.c
   trunk/postgis/postgis.sql.in
   trunk/regress/core/regress.sql
   trunk/regress/core/regress_expected
Log:
Add ST_TileEnvelope utility function
Closes #4452


Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2019-07-09 15:43:19 UTC (rev 17586)
+++ trunk/NEWS	2019-07-10 20:44:52 UTC (rev 17587)
@@ -9,6 +9,7 @@
   - #4445, Fix a bug in geometry_le (Raúl Marín)
   - #4451, Fix the calculation of gserialized_max_header_size (Raúl Marín)
   - #4450, Speed up ST_GeometryType (Raúl Marín)
+  - #4452, Add ST_TileEnvelope() (Paul Ramsey)
 
 
 PostGIS 3.0.0alpha3

Modified: trunk/doc/reference_constructor.xml
===================================================================
--- trunk/doc/reference_constructor.xml	2019-07-09 15:43:19 UTC (rev 17586)
+++ trunk/doc/reference_constructor.xml	2019-07-10 20:44:52 UTC (rev 17587)
@@ -216,7 +216,7 @@
 		</refsection>
 		<refsection>
 			<title>See Also</title>
-			<para><xref linkend="ST_MakePoint" />, <xref linkend="ST_MakeLine" />, <xref linkend="ST_MakePolygon" /></para>
+			<para><xref linkend="ST_MakePoint" />, <xref linkend="ST_MakeLine" />, <xref linkend="ST_MakePolygon" />, <xref linkend="ST_TileEnvelope" /></para>
 		</refsection>
 	</refentry>
 
@@ -756,4 +756,47 @@
 	  </refsection>
 	</refentry>
 
+
+	<refentry id="ST_TileEnvelope">
+		<refnamediv>
+		<refname>ST_TileEnvelope</refname>
+		<refpurpose>Creates a rectangular Polygon in <ulink url="https://en.wikipedia.org/wiki/Web_Mercator_projection">Web Mercator</ulink> (SRID:3857) using the <ulink url="https://en.wikipedia.org/wiki/Tiled_web_map">XYZ tile system</ulink>.</refpurpose>
+		</refnamediv>
+
+		<refsynopsisdiv>
+		<funcsynopsis>
+		  <funcprototype>
+			<funcdef>geometry <function>ST_TileEnvelope</function></funcdef>
+			<paramdef><type>integer</type> <parameter>tileZoom</parameter></paramdef>
+			<paramdef><type>integer</type> <parameter>tileX</parameter></paramdef>
+			<paramdef><type>integer</type> <parameter>tileY</parameter></paramdef>
+			<paramdef choice="opt"><type>geometry</type> <parameter>bounds=SRID=3857;LINESTRING(-20037508.342789 -20037508.342789,20037508.342789 20037508.342789)</parameter></paramdef>
+		  </funcprototype>
+		 </funcsynopsis>
+		</refsynopsisdiv>
+
+		<refsection>
+			<title>Description</title>
+
+			<para>Creates a rectangular Polygon in <ulink url="https://en.wikipedia.org/wiki/Web_Mercator_projection">Web Mercator</ulink> (SRID:3857) using the <ulink url="https://en.wikipedia.org/wiki/Tiled_web_map">XYZ tile system</ulink>. By default, the bounds are the in EPSG:3857 using the standard range of the Web Mercator system (-20037508.342789, 20037508.342789). The optional bounds parameter can be used to generate envelopes for any tiling scheme: provide a geometry that has the SRID and extent of the initial "zoom level zero" square within which the tile system is to be inscribed.</para>
+
+			<para>Availability: 3.0</para>
+
+		</refsection>
+
+		<refsection>
+		<title>Example: Building a tile envelope</title>
+		 <programlisting>SELECT ST_AsText( ST_TileEnvelope(2, 1, 1) );
+
+st_astext
+------------------------------
+POLYGON((-10018754.1713945 0,-10018754.1713945 10018754.1713945,0 10018754.1713945,0 0,-10018754.1713945 0))
+</programlisting>
+		</refsection>
+		<refsection>
+			<title>See Also</title>
+			<para><xref linkend="ST_MakeEnvelope" /></para>
+		</refsection>
+	</refentry>
+
   </sect1>

Modified: trunk/doc/reference_output.xml
===================================================================
--- trunk/doc/reference_output.xml	2019-07-09 15:43:19 UTC (rev 17586)
+++ trunk/doc/reference_output.xml	2019-07-10 20:44:52 UTC (rev 17587)
@@ -1093,6 +1093,7 @@
         <title>See Also</title>
             <para>
                 <xref linkend="ST_AsMVT" />,
+                <xref linkend="ST_TileEnvelope" />,
                 <xref linkend="PostGIS_Wagyu_Version" />
             </para>
         </refsection>
@@ -1181,24 +1182,22 @@
 
 	  <refsection>
 		<title>Examples</title>
-		<programlisting><![CDATA[SELECT ST_AsMVT(q, 'test', 4096, 'geom')
-  FROM (SELECT 1 AS c1,
-    ST_AsMVTGeom(
-      ST_GeomFromText('POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))'),
-      ST_MakeBox2D(ST_Point(0, 0), ST_Point(4096, 4096)),
-      4096, 0, false) AS geom) AS q;
+		<programlisting><![CDATA[WITH mvtgeom AS
+(
+  SELECT ST_AsMVTGeom(geom, ST_TileEnvelope(12,513,412)) AS geom, name, description
+  FROM points_of_interest
+  WHERE ST_Intersects(geom, ST_TileEnvelope(12,513,412)
+)
+SELECT ST_AsMVT(mvtgeom.*)
+FROM mvtgeom;
+]]></programlisting>
 
-                              st_asmvt
---------------------------------------------------------------------
- \x1a320a0474657374121d1202000018032215095aa63f1a134631130a270f09280a121d0a14140f1a026331220228012880207802
-]]>
-		</programlisting>
 	  </refsection>
 
 		<refsection>
 			<title>See Also</title>
 				<para>
-					<xref linkend="ST_AsMVTGeom" />
+					<xref linkend="ST_AsMVTGeom" />, <xref linkend="ST_TileEnvelope" />
 				</para>
 		  </refsection>
 	</refentry>

Modified: trunk/postgis/lwgeom_functions_basic.c
===================================================================
--- trunk/postgis/lwgeom_functions_basic.c	2019-07-09 15:43:19 UTC (rev 17586)
+++ trunk/postgis/lwgeom_functions_basic.c	2019-07-10 20:44:52 UTC (rev 17587)
@@ -106,6 +106,7 @@
 Datum optimistic_overlap(PG_FUNCTION_ARGS);
 Datum ST_GeoHash(PG_FUNCTION_ARGS);
 Datum ST_MakeEnvelope(PG_FUNCTION_ARGS);
+Datum ST_TileEnvelope(PG_FUNCTION_ARGS);
 Datum ST_CollectionExtract(PG_FUNCTION_ARGS);
 Datum ST_CollectionHomogenize(PG_FUNCTION_ARGS);
 Datum ST_IsCollection(PG_FUNCTION_ARGS);
@@ -2064,6 +2065,67 @@
 	PG_RETURN_POINTER(result);
 }
 
+
+PG_FUNCTION_INFO_V1(ST_TileEnvelope);
+Datum ST_TileEnvelope(PG_FUNCTION_ARGS)
+{
+	GSERIALIZED *bounds;
+	uint32_t zoomu;
+	int32_t x, y, zoom;
+	uint32_t worldTileSize;
+	double tileGeoSizeX, tileGeoSizeY;
+	double boundsWidth, boundsHeight;
+	double x1, y1, x2, y2;
+	/* This is broken, since 3857 doesn't mean "web mercator", it means
+	   the contents of the row in spatial_ref_sys with srid = 3857.
+	   For practical purposes this will work, but in good implementation
+	   we should de-reference in spatial ref sys to confirm that the
+	   srid of the object is EPSG:3857. */
+	int32_t srid;
+	GBOX bbox;
+
+	POSTGIS_DEBUG(2, "ST_TileEnvelope called");
+
+	zoom = PG_GETARG_INT32(0);
+	x = PG_GETARG_INT32(1);
+	y = PG_GETARG_INT32(2);
+
+	bounds = PG_GETARG_GSERIALIZED_P(3);
+	if(gserialized_get_gbox_p(bounds, &bbox) != LW_SUCCESS)
+		elog(ERROR, "%s: Empty bounds", __func__);
+	srid = gserialized_get_srid(bounds);
+
+	boundsWidth  = bbox.xmax - bbox.xmin;
+	boundsHeight = bbox.ymax - bbox.ymin;
+	if (boundsWidth <= 0 || boundsHeight <= 0)
+		elog(ERROR, "%s: Geometric bounds are too small", __func__);
+
+	if (zoom < 0 || zoom >= 32)
+		elog(ERROR, "%s: Invalid tile zoom value, %d", __func__, zoom);
+
+	zoomu = (uint32_t)zoom;
+	worldTileSize = 0x01u << (zoomu > 31 ? 31 : zoomu);
+
+	if (x < 0 || (uint32_t)x >= worldTileSize)
+		elog(ERROR, "%s: Invalid tile x value, %d", __func__, x);
+	if (y < 0 || (uint32_t)y >= worldTileSize)
+		elog(ERROR, "%s: Invalid tile y value, %d", __func__, y);
+
+	tileGeoSizeX = boundsWidth / worldTileSize;
+	tileGeoSizeY = boundsHeight / worldTileSize;
+	x1 = bbox.xmin + tileGeoSizeX * (x);
+	x2 = bbox.xmin + tileGeoSizeX * (x+1);
+	y1 = bbox.ymax - tileGeoSizeY * (y+1);
+	y2 = bbox.ymax - tileGeoSizeY * (y);
+
+	PG_RETURN_POINTER(
+		geometry_serialize(
+		lwpoly_as_lwgeom(
+		lwpoly_construct_envelope(
+			srid, x1, y1, x2, y2))));
+}
+
+
 PG_FUNCTION_INFO_V1(ST_IsCollection);
 Datum ST_IsCollection(PG_FUNCTION_ARGS)
 {

Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in	2019-07-09 15:43:19 UTC (rev 17586)
+++ trunk/postgis/postgis.sql.in	2019-07-10 20:44:52 UTC (rev 17587)
@@ -1679,6 +1679,13 @@
 	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
 	_COST_LOW;
 
+-- Availability: 3.0.0
+CREATE OR REPLACE FUNCTION ST_TileEnvelope(zoom integer, x integer, y integer, bounds geometry DEFAULT 'SRID=3857;LINESTRING(-20037508.342789 -20037508.342789, 20037508.342789 20037508.342789)'::geometry)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME', 'ST_TileEnvelope'
+	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
+	_COST_LOW;
+
 -- Availability: 1.2.2
 CREATE OR REPLACE FUNCTION ST_MakePolygon(geometry, geometry[])
 	RETURNS geometry

Modified: trunk/regress/core/regress.sql
===================================================================
--- trunk/regress/core/regress.sql	2019-07-09 15:43:19 UTC (rev 17586)
+++ trunk/regress/core/regress.sql	2019-07-10 20:44:52 UTC (rev 17587)
@@ -264,6 +264,18 @@
 select '225', ST_Expand('BOX(-2 3, -1 6'::BOX2D, 4, 2);
 select '226', ST_SRID(ST_Expand('SRID=4326;POINT (0 0)'::geometry, 1))=4326;
 
+-- ST_TileEnvelope()
+select '227', ST_AsEWKT(ST_TileEnvelope(-1, 0, 0));
+select '228', ST_AsEWKT(ST_TileEnvelope(0, 0, 1));
+select '229', ST_AsEWKT(ST_TileEnvelope(0, 0, 0));
+select '230', ST_AsEWKT(ST_TileEnvelope(4, 8, 8));
+select '231', ST_AsEWKT(ST_TileEnvelope(4, 15, 15));
+select '232', ST_AsEWKT(ST_TileEnvelope(4, 8, 8, ST_MakeEnvelope(-100, -100, 100, 100, 0)));
+select '233', ST_AsEWKT(ST_TileEnvelope(4, 15, 15, ST_MakeEnvelope(-100, -100, 100, 100, 0)));
+select '234', ST_AsEWKT(ST_TileEnvelope(4, 0, 0, ST_MakeEnvelope(-100, -100, 100, 100, 0)));
+select '235', ST_AsEWKT(ST_TileEnvelope(4, 8, 8, ST_MakeEnvelope(-200, -100, 200, 100, 0)));
+
+
 -- Drop test table
 DROP table test;
 

Modified: trunk/regress/core/regress_expected
===================================================================
--- trunk/regress/core/regress_expected	2019-07-09 15:43:19 UTC (rev 17586)
+++ trunk/regress/core/regress_expected	2019-07-10 20:44:52 UTC (rev 17587)
@@ -194,3 +194,12 @@
 224|
 225|BOX(-6 1,3 8)
 226|t
+ERROR:  ST_TileEnvelope: Invalid tile zoom value, -1
+ERROR:  ST_TileEnvelope: Invalid tile y value, 1
+229|SRID=3857;POLYGON((-20037510 -20037510,-20037510 20037510,20037510 20037510,20037510 -20037510,-20037510 -20037510))
+230|SRID=3857;POLYGON((0 -2504688.75,0 0,2504688.75 0,2504688.75 -2504688.75,0 -2504688.75))
+231|SRID=3857;POLYGON((17532821.25 -20037510,17532821.25 -17532821.25,20037510 -17532821.25,20037510 -20037510,17532821.25 -20037510))
+232|POLYGON((0 -12.5,0 0,12.5 0,12.5 -12.5,0 -12.5))
+233|POLYGON((87.5 -100,87.5 -87.5,100 -87.5,100 -100,87.5 -100))
+234|POLYGON((-100 87.5,-100 100,-87.5 100,-87.5 87.5,-100 87.5))
+235|POLYGON((0 -12.5,0 0,25 0,25 -12.5,0 -12.5))



More information about the postgis-tickets mailing list