* New Features *
+ - ST_SwapOrdinates (Sandro Santilli / Boundless)
- #3040, KNN GiST index based centroid (<<->>) and box (<<#>>)
n-D distance operators (Sandro Santilli / Boundless)
- Interruptibility API for liblwgeom (Sandro Santilli / CartoDB)
]]></programlisting>
</refsection>
+ <!-- Optionally add a "See Also" section -->
+ <refsection>
+ <title>See Also</title>
+ <para> <xref linkend="ST_SwapOrdinates" /> </para>
+ </refsection>
+
+ </refentry>
+
+ <refentry id="ST_SwapOrdinates">
+ <refnamediv>
+ <refname>ST_SwapOrdinates</refname>
+ <refpurpose>Returns a version of the given geometry with
+ given ordinate values swapped.
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>geometry <function>ST_SwapOrdinates</function></funcdef>
+ <paramdef><type>geometry</type> <parameter>geom</parameter></paramdef>
+ <paramdef><type>cstring</type> <parameter>ords</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsection>
+ <title>Description</title>
+ <para>
+Returns a version of the given geometry with given ordinates swapped.
+ </para>
+ <para>
+The <varname>ords</varname> parameter is a 2-characters string naming
+the ordinates to swap. Valid names are: x,y,z and m.
+ </para>
+ <para>&curve_support;</para>
+ <para>&Z_support;</para>
+ <para>&M_support;</para>
+ <para>Availability: 2.2.0</para>
+ <para>&P_support;</para>
+ <para>&T_support;</para>
+ </refsection>
+
+ <refsection>
+ <title>Example</title>
+ <programlisting><![CDATA[
+-- Scale M value by 2
+SELECT ST_AsText(
+ ST_SwapOrdinates(
+ ST_Scale(
+ ST_SwapOrdinates(g,'xm'),
+ 2, 1
+ ),
+ 'xm')
+) FROM ( SELECT 'POINT ZM (0 0 0 2)'::geometry g ) foo;
+ st_astext
+--------------------
+ POINT ZM (0 0 0 4)
+ ]]></programlisting>
+ </refsection>
+
+ <!-- Optionally add a "See Also" section -->
+ <refsection>
+ <title>See Also</title>
+ <para> <xref linkend="ST_FlipCoordinates" /> </para>
+ </refsection>
+
</refentry>
<refentry id="ST_Intersection">
#define WKBSRIDFLAG 0x20000000
#define WKBBBOXFLAG 0x10000000
+/** Ordinate names */
+typedef enum LWORD_T {
+ LWORD_X = 0,
+ LWORD_Y = 1,
+ LWORD_Z = 2,
+ LWORD_M = 3
+} LWORD;
+
/**********************************************************************
** Spherical radius.
** Moritz, H. (1980). Geodetic Reference System 1980, by resolution of
extern char lwtriangle_is_repeated_points(LWTRIANGLE *triangle);
+/**
+ * Swap ordinate values in every vertex of the geometry.
+ *
+ * Ordinates to swap are specified using an index with meaning:
+ * 0=x, 1=y, 2=z, 3=m
+ *
+ * Swapping an existing ordinate with an unexisting one results
+ * in undefined value being written in the existing ordinate.
+ * Caller should verify and prevent such calls.
+ *
+ * Availability: 2.2.0
+ */
+extern void lwgeom_swap_ordinates(LWGEOM *in, LWORD o1, LWORD o2);
+
/**
* Reverse the X and Y coordinate order. Useful for geometries in lat/lon order
* than need to be converted to lon/lat order.
+*
+* NOTE: uses lwgeom_swap_ordinates internally,
+* kept for backward compatibility
*/
extern LWGEOM* lwgeom_flip_coordinates(LWGEOM *in);
LWCOLLECTION* lwcollection_force_dims(const LWCOLLECTION *lwcol, int hasz, int hasm);
POINTARRAY* ptarray_force_dims(const POINTARRAY *pa, int hasz, int hasm);
+/**
+ * Swap ordinate values o1 and o2 on a given POINTARRAY
+ *
+ * Ordinates semantic is: 0=x 1=y 2=z 3=m
+ */
+void ptarray_swap_ordinates(POINTARRAY *pa, LWORD o1, LWORD o2);
+
/*
* Is Empty?
*/
}
LWGEOM* lwgeom_flip_coordinates(LWGEOM *in)
+{
+ lwgeom_swap_ordinates(in,LWORD_X,LWORD_Y);
+ return in;
+}
+
+void lwgeom_swap_ordinates(LWGEOM *in, LWORD o1, LWORD o2)
{
LWCOLLECTION *col;
LWPOLY *poly;
int i;
- if ( (!in) || lwgeom_is_empty(in) )
- return in;
+#if PARANOIA_LEVEL > 0
+ assert(o1 < 4);
+ assert(o2 < 4);
+#endif
+
+ if ( (!in) || lwgeom_is_empty(in) ) return;
+
+ /* TODO: check for lwgeom NOT having the specified dimension ? */
LWDEBUGF(4, "lwgeom_flip_coordinates, got type: %s",
lwtype_name(in->type));
switch (in->type)
{
case POINTTYPE:
- ptarray_flip_coordinates(lwgeom_as_lwpoint(in)->point);
+ ptarray_swap_ordinates(lwgeom_as_lwpoint(in)->point, o1, o2);
break;
case LINETYPE:
- ptarray_flip_coordinates(lwgeom_as_lwline(in)->points);
+ ptarray_swap_ordinates(lwgeom_as_lwline(in)->points, o1, o2);
break;
case CIRCSTRINGTYPE:
- ptarray_flip_coordinates(lwgeom_as_lwcircstring(in)->points);
+ ptarray_swap_ordinates(lwgeom_as_lwcircstring(in)->points, o1, o2);
break;
case POLYGONTYPE:
poly = (LWPOLY *) in;
for (i=0; i<poly->nrings; i++)
{
- ptarray_flip_coordinates(poly->rings[i]);
+ ptarray_swap_ordinates(poly->rings[i], o1, o2);
}
break;
case TRIANGLETYPE:
- ptarray_flip_coordinates(lwgeom_as_lwtriangle(in)->points);
+ ptarray_swap_ordinates(lwgeom_as_lwtriangle(in)->points, o1, o2);
break;
case MULTIPOINTTYPE:
col = (LWCOLLECTION *) in;
for (i=0; i<col->ngeoms; i++)
{
- lwgeom_flip_coordinates(col->geoms[i]);
+ lwgeom_swap_ordinates(col->geoms[i], o1, o2);
}
break;
default:
- lwerror("lwgeom_flip_coordinates: unsupported geometry type: %s",
+ lwerror("lwgeom_swap_ordinates: unsupported geometry type: %s",
lwtype_name(in->type));
- return NULL;
+ return;
}
- lwgeom_drop_bbox(in);
- lwgeom_add_bbox(in);
- return in;
+ /* only refresh bbox if X or Y changed */
+ if ( o1 < 2 || o2 < 2 ) {
+ lwgeom_drop_bbox(in);
+ lwgeom_add_bbox(in);
+ }
}
void lwgeom_set_srid(LWGEOM *geom, int32_t srid)
return pa;
}
+void
+ptarray_swap_ordinates(POINTARRAY *pa, LWORD o1, LWORD o2)
+{
+ int i;
+ double d, *dp1, *dp2;
+ POINT4D p;
+
+#if PARANOIA_LEVEL > 0
+ assert(o1 < 4);
+ assert(o2 < 4);
+#endif
+
+ dp1 = ((double*)&p)+(unsigned)o1;
+ dp2 = ((double*)&p)+(unsigned)o2;
+ for (i=0 ; i < pa->npoints ; i++)
+ {
+ getPoint4d_p(pa, i, &p);
+ d = *dp2;
+ *dp2 = *dp1;
+ *dp1 = d;
+ ptarray_set_point4d(pa, i, &p);
+ }
+}
+
/**
* @brief Returns a modified #POINTARRAY so that no segment is
PG_FUNCTION_INFO_V1(ST_FlipCoordinates);
Datum ST_FlipCoordinates(PG_FUNCTION_ARGS)
{
- GSERIALIZED *input = (GSERIALIZED *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
- GSERIALIZED *output;
- LWGEOM *lwgeom_in = lwgeom_from_gserialized(input);
- LWGEOM *lwgeom_out;
+ GSERIALIZED *in = (GSERIALIZED *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+ GSERIALIZED *out;
+ LWGEOM *lwgeom = lwgeom_from_gserialized(in);
- lwgeom_out = lwgeom_flip_coordinates(lwgeom_in);
- output = geometry_serialize(lwgeom_out);
+ lwgeom_swap_ordinates(lwgeom, LWORD_X, LWORD_Y);
+ out = geometry_serialize(lwgeom);
- lwgeom_free(lwgeom_in);
- PG_FREE_IF_COPY(input, 0);
+ lwgeom_free(lwgeom);
+ PG_FREE_IF_COPY(in, 0);
- PG_RETURN_POINTER(output);
+ PG_RETURN_POINTER(out);
+}
+
+static LWORD ordname2ordval(char n)
+{
+ if ( n == 'x' || n == 'X' ) return LWORD_X;
+ if ( n == 'y' || n == 'y' ) return LWORD_Y;
+ if ( n == 'z' || n == 'Z' ) return LWORD_Z;
+ if ( n == 'm' || n == 'M' ) return LWORD_M;
+ lwerror("Invalid ordinate name '%c'. Expected x,y,z or m", n);
+ return (LWORD)-1;
+}
+
+Datum ST_SwapOrdinates(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(ST_SwapOrdinates);
+Datum ST_SwapOrdinates(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED *in;
+ GSERIALIZED *out;
+ LWGEOM *lwgeom;
+ const char *ospec;
+ LWORD o1, o2;
+
+ ospec = PG_GETARG_CSTRING(1);
+ if ( strlen(ospec) != 2 )
+ {
+ lwerror("Invalid ordinate specification. "
+ "Need two letters from the set (x,y,z,m). "
+ "Got '%s'", ospec);
+ PG_RETURN_NULL();
+ }
+ o1 = ordname2ordval( ospec[0] );
+ o2 = ordname2ordval( ospec[1] );
+
+ in = (GSERIALIZED *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+
+ /* Check presence of given ordinates */
+ if ( ( o1 == LWORD_M || o2 == LWORD_M ) && ! gserialized_has_m(in) )
+ {
+ lwerror("Geometry does not have an M ordinate");
+ PG_RETURN_NULL();
+ }
+ if ( ( o1 == LWORD_Z || o2 == LWORD_Z ) && ! gserialized_has_z(in) )
+ {
+ lwerror("Geometry does not have a Z ordinate");
+ PG_RETURN_NULL();
+ }
+
+ /* Nothing to do if swapping the same ordinate, pity for the copy... */
+ if ( o1 == o2 ) PG_RETURN_POINTER(in);
+
+ lwgeom = lwgeom_from_gserialized(in);
+ lwgeom_swap_ordinates(lwgeom, o1, o2);
+ out = geometry_serialize(lwgeom);
+ lwgeom_free(lwgeom);
+ PG_FREE_IF_COPY(in, 0);
+ PG_RETURN_POINTER(out);
}
AS 'SELECT $1 && ST_Expand($2,$3) AND $2 && ST_Expand($1,$3) AND _ST_DFullyWithin(ST_ConvexHull($1), ST_ConvexHull($2), $3)'
LANGUAGE 'sql' IMMUTABLE;
+-- Availability: 2.2.0
+CREATE OR REPLACE FUNCTION ST_SwapOrdinates(geom geometry, ords cstring)
+ RETURNS geometry
+ AS 'MODULE_PATHNAME', 'ST_SwapOrdinates'
+ LANGUAGE 'c' IMMUTABLE STRICT;
+
+-- NOTE: same as ST_SwapOrdinates(geometry, 'xy')
+-- but slightly faster in that it doesn't need to parse ordinate
+-- spec strings
CREATE OR REPLACE FUNCTION ST_FlipCoordinates(geometry)
RETURNS geometry
AS 'MODULE_PATHNAME', 'ST_FlipCoordinates'
typmod \
remove_repeated_points \
split \
+ swapordinates \
relate \
bestsrid \
concave_hull\
--- /dev/null
+SELECT 'flip1', ST_AsText(ST_FlipCoordinates('POINT(0 1)'));
+SELECT 'flip2', ST_AsText(ST_FlipCoordinates('GEOMETRYCOLLECTION(POINT(1 2),MULTIPOLYGON(((0 1,1 2,2 1,0 1)),((10 0,20 0,20 10,10 0),(2 2,4 2,4 4,2 2))),LINESTRING(0 1,1 0))'));
+
+-- Bogus calls (swapping unavailable ordinate)
+SELECT ST_AsText(ST_SwapOrdinates('POINTZ(0 1 2)','xm'));
+SELECT ST_AsText(ST_SwapOrdinates('POINTM(0 1 2)','zy'));
+-- Bogus calls (short spec)
+SELECT ST_AsText(ST_SwapOrdinates('POINTZ(0 1 2)','x'));
+-- Bogus calls (invalid ordinate names)
+SELECT ST_AsText(ST_SwapOrdinates('POINTZ(0 1 2)','pq'));
+
+SELECT 'swap1', ST_AsText(ST_SwapOrdinates('POINTZ(0 1 2)','xz'));
+SELECT 'swap2', ST_AsText(ST_SwapOrdinates('POINTM(0 1 2)','my'));
+SELECT 'swap3', ST_AsText(ST_SwapOrdinates('POINTZM(0 1 2 3)','mz'));
+SELECT 'swap4', ST_AsText(ST_SwapOrdinates('MULTICURVE ZM ((5 5 1 3, 3 5 2 2, 3 3 3 1, 0 3 1 1), CIRCULARSTRING ZM (0 0 0 0, 0.2 1 3 -2, 0.5 1.4 1 2), COMPOUNDCURVE ZM (CIRCULARSTRING ZM (0 0 0 0,1 1 1 2,1 0 0 1),(1 0 0 1,0 1 5 4)))','my'));
--- /dev/null
+flip1|POINT(1 0)
+flip2|GEOMETRYCOLLECTION(POINT(2 1),MULTIPOLYGON(((1 0,2 1,1 2,1 0)),((0 10,0 20,10 20,0 10),(2 2,2 4,4 4,2 2))),LINESTRING(1 0,0 1))
+ERROR: Geometry does not have an M ordinate
+ERROR: Geometry does not have a Z ordinate
+ERROR: Invalid ordinate specification. Need two letters from the set (x,y,z,m). Got 'x'
+ERROR: Invalid ordinate name 'p'. Expected x,y,z or m
+swap1|POINT Z (2 1 0)
+swap2|POINT M (0 2 1)
+swap3|POINT ZM (0 1 3 2)
+swap4|MULTICURVE ZM ((5 3 1 5,3 2 2 5,3 1 3 3,0 1 1 3),CIRCULARSTRING ZM (0 0 0 0,0.2 -2 3 1,0.5 2 1 1.4),COMPOUNDCURVE ZM (CIRCULARSTRING ZM (0 0 0 0,1 2 1 1,1 1 0 0),(1 1 0 0,0 4 5 1)))