- ST_DelaunayTriangles (Sandro Santilli / Vizzuality)
- ST_NearestValue, ST_Neighborhood (Bborie Park / UC Davis)
- - ST_PixelAsPoint, ST_PixelAsPoints (Bborie Park / UC Davis)
- - ST_PixelAsCentroid, ST_PixelAsCentroids (Bborie Park / UC Davis)
+ - ST_PixelAsPoint, ST_PixelAsPoints (Bborie Park / UC Davis)
+ - ST_PixelAsCentroid, ST_PixelAsCentroids (Bborie Park / UC Davis)
- ST_Raster2WorldCoord, ST_World2RasterCoord (Bborie Park / UC Davis)
- - ST_BandSurface (Bborie Park / UC Davis)
- #1643, Tiger Geocoder - Tiger 2011 loader (Regina Obe / Paragon Corporation)
Funded by Hunter Systems Group
- GEOMETRYCOLLECTION support for ST_MakeValid (Sandro Santilli / Vizzuality)
</refsection>
</refentry>
- <refentry id="RT_ST_BandSurface">
- <refnamediv>
- <refname>ST_BandSurface</refname>
- <refpurpose>
- Returns the surface (multipolygon) of the area represented by a raster's band.
- </refpurpose>
- </refnamediv>
- <refsynopsisdiv>
- <funcsynopsis>
- <funcprototype>
- <funcdef>geometry <function>ST_BandSurface</function></funcdef>
- <paramdef><type>raster </type> <parameter>rast</parameter></paramdef>
- <paramdef choice='opt'><type>integer </type> <parameter>band=1</parameter></paramdef>
- </funcprototype>
- </funcsynopsis>
- </refsynopsisdiv>
-
- <refsection>
- <title>Description</title>
- <para>
- Returns the surface (multipolygon) of the area represented by a raster's band. The area represented by a raster's band is built using those pixels whose values are not NODATA.
- </para>
- </refsection>
-
- <refsection>
- <title>Examples</title>
- <programlisting>
-SELECT
- ST_AsText(ST_BandSurface(rast))
-FROM (
- SELECT
- ST_SetValue(
- ST_SetValue(
- ST_SetValue(
- ST_SetValue(
- ST_SetValue(
- ST_SetValue(
- ST_SetValue(
- ST_SetValue(
- ST_SetValue(
- ST_AddBand(
- ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0),
- 1, '32BUI', 1, 0
- ),
- 1, 1, 1, 0
- ),
- 1, 2, 2, 0
- ),
- 1, 3, 3, 0
- ),
- 1, 4, 4, 0
- ),
- 1, 5, 5, 0
- ),
- 1, 5, 1, 0
- ),
- 1, 4, 2, 0
- ),
- 1, 2, 4, 0
- ),
- 1, 1, 5, 0
- ) AS rast
- FROM raster_surface
-) foo;
-
- st_astext
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- MULTIPOLYGON(((1 0,2 0,3 0,4 0,4 -1,3 -1,3 -2,2 -2,2 -1,1 -1,1 0)),((0 -1,1 -1,1 -2,2 -2,2 -3,1 -3,1 -4,0 -4,0 -3,0 -2,0 -1)),((4 -1,5 -1,5 -2,5 -3,5 -4,4 -4,4 -3,3 -3,3 -2,4 -2,4 -1)),((2 -3,3 -3,3 -4,4 -4,4 -5,3 -5,2 -5,1 -5,1 -4,2 -4,2 -3)))
- </programlisting>
- </refsection>
-
- <refsection>
- <title>See Also</title>
- <para>
- <xref linkend="RT_ST_DumpAsPolygons" />,
- <xref linkend="RT_ST_PixelAsPolygon" />,
- <xref linkend="RT_ST_PixelAsPolygons" />,
- <xref linkend="RT_ST_PixelAsPoint" />,
- <xref linkend="RT_ST_PixelAsPoints" />,
- <xref linkend="RT_ST_PixelAsCentroid" />,
- <xref linkend="RT_ST_PixelAsCentroids" />
- </para>
- </refsection>
-
- </refentry>
-
</sect1>
<sect1 id="Raster_Pixel_Accessors">
<refsection>
<title>See Also</title>
<para>
- <xref linkend="RT_ST_BandSurface" />,
<xref linkend="RT_ST_DumpAsPolygons" />,
<xref linkend="RT_ST_PixelAsPolygons" />,
<xref linkend="RT_ST_PixelAsPoint" />,
<title>See Also</title>
<para>
<xref linkend="RT_ST_DumpAsPolygons" />,
- <xref linkend="RT_ST_BandSurface" />,
<xref linkend="RT_ST_PixelAsPolygon" />,
<xref linkend="RT_ST_PixelAsPoint" />,
<xref linkend="RT_ST_PixelAsPoints" />,
<xref linkend="RT_ST_PixelAsPolygons" />,
<xref linkend="RT_ST_PixelAsPoints" />,
<xref linkend="RT_ST_PixelAsCentroid" />,
- <xref linkend="RT_ST_PixelAsCentroids" />,
- <xref linkend="RT_ST_BandSurface" />
+ <xref linkend="RT_ST_PixelAsCentroids" />
</para>
</refsection>
<xref linkend="RT_ST_PixelAsPolygons" />,
<xref linkend="RT_ST_PixelAsPoint" />,
<xref linkend="RT_ST_PixelAsCentroid" />,
- <xref linkend="RT_ST_PixelAsCentroids" />,
- <xref linkend="RT_ST_BandSurface" />
+ <xref linkend="RT_ST_PixelAsCentroids" />
</para>
</refsection>
<xref linkend="RT_ST_PixelAsPolygons" />,
<xref linkend="RT_ST_PixelAsPoint" />,
<xref linkend="RT_ST_PixelAsPoints" />,
- <xref linkend="RT_ST_PixelAsCentroids" />,
- <xref linkend="RT_ST_BandSurface" />
+ <xref linkend="RT_ST_PixelAsCentroids" />
</para>
</refsection>
<xref linkend="RT_ST_PixelAsPolygons" />,
<xref linkend="RT_ST_PixelAsPoint" />,
<xref linkend="RT_ST_PixelAsPoints" />,
- <xref linkend="RT_ST_PixelAsCentroid" />,
- <xref linkend="RT_ST_BandSurface" />
+ <xref linkend="RT_ST_PixelAsCentroid" />
</para>
</refsection>
LWGEOM *lwgeomValid = NULL;
#if POSTGIS_GEOS_VERSION < 33
+/*
int msgValid = 0;
+*/
#endif
uint32_t bandNums[1] = {nband};
rt_band band = NULL;
LWGEOM *mpoly = NULL;
LWGEOM *tmp = NULL;
- LWGEOM *poly = NULL;
- uint16_t width = 0;
- uint16_t height = 0;
- int x = 0;
- int y = 0;
- int err = 0;
- double val = 0;
- double nodata = 0;
+ rt_geomval gv = NULL;
+ int gvcount = 0;
+ GEOSGeometry *gc = NULL;
+ GEOSGeometry *gunion = NULL;
+ GEOSGeometry **geoms = NULL;
+ int geomscount = 0;
+ int i = 0;
/* raster is empty, return NULL */
if (rt_raster_is_empty(raster))
);
}
- /* NODATA value */
- nodata = rt_band_get_nodata(band);
+ /* initialize GEOS */
+ initGEOS(lwnotice, lwgeom_geos_error);
- /* process each pixel of band */
- width = rt_raster_get_width(raster);
- height = rt_raster_get_height(raster);
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++) {
- err = rt_band_get_pixel(band, x, y, &val);
- if (err != 0) {
- rterror("rt_raster_surface: Unable to get pixel value");
- return NULL;
+ /* use gdal polygonize */
+ gv = rt_raster_gdal_polygonize(raster, nband, 1, &gvcount);
+ /* no polygons returned */
+ if (gvcount < 1) {
+ RASTER_DEBUG(3, "All pixels of band are NODATA. Returning NULL");
+ return NULL;
+ }
+ /* more than 1 polygon */
+ else if (gvcount > 1) {
+ /* convert LWPOLY to GEOSGeometry */
+ geomscount = gvcount;
+ geoms = rtalloc(sizeof(GEOSGeometry *) * geomscount);
+ if (geoms == NULL) {
+ rterror("rt_raster_surface: Unable to allocate memory for pixel polygons as GEOSGeometry");
+ for (i = 0; i < gvcount; i++) lwpoly_free(gv[i].geom);
+ rtdealloc(gv);
+ return NULL;
+ }
+ for (i = 0; i < gvcount; i++) {
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ char *wkt = lwgeom_to_wkt(lwpoly_as_lwgeom(gv[i].geom), WKT_ISO, DBL_DIG, NULL);
+ RASTER_DEBUGF(4, "geom %d = %s", i, wkt);
+ rtdealloc(wkt);
}
+#endif
- /* val is NODATA, skip */
- if (
- FLT_EQ(val, nodata) ||
- rt_band_clamped_value_is_nodata(band, val) != 0
- ) {
- continue;
- }
+ geoms[i] = LWGEOM2GEOS(lwpoly_as_lwgeom(gv[i].geom));
+ lwpoly_free(gv[i].geom);
+ }
+ rtdealloc(gv);
- /* convert pixel to polygon */
- poly = lwpoly_as_lwgeom(rt_raster_pixel_as_polygon(raster, x, y));
- if (poly == NULL) {
- rterror("rt_raster_surface: Unable to create polygon of pixel");
- if (mpoly != NULL) lwgeom_free(mpoly);
- return NULL;
- }
+ /* create geometry collection */
+#if POSTGIS_GEOS_VERSION >= 33
+ gc = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, geoms, geomscount);
+#else
+ gc = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, geoms, geomscount);
+#endif
- /* union polygon */
- if (mpoly == NULL)
- mpoly = poly;
- else {
- tmp = mpoly;
- mpoly = lwgeom_union(tmp, poly);
+ if (gc == NULL) {
+#if POSTGIS_GEOS_VERSION >= 33
+ rterror("rt_raster_surface: Unable to create GEOS GEOMETRYCOLLECTION from set of pixel polygons");
+#else
+ rterror("rt_raster_surface: Unable to create GEOS MULTIPOLYGON from set of pixel polygons");
+#endif
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- char *wkt = NULL;
-
- wkt = lwgeom_to_wkt(poly, WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "poly = %s", wkt);
- rtdealloc(wkt);
-
- wkt = lwgeom_to_wkt(tmp, WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "tmp = %s", wkt);
- rtdealloc(wkt);
-
- wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "mpoly = %s", wkt);
- rtdealloc(wkt);
- }
+ for (i = 0; i < geomscount; i++)
+ GEOSGeom_destroy(geoms[i]);
+ rtdealloc(geoms);
+ return NULL;
+ }
+
+ /* run the union */
+#if POSTGIS_GEOS_VERSION >= 33
+ gunion = GEOSUnaryUnion(gc);
+#else
+ gunion = GEOSUnionCascaded(gc);
#endif
+ GEOSGeom_destroy(gc);
+ rtdealloc(geoms);
- lwgeom_free(tmp);
- lwgeom_free(poly);
+ if (gunion == NULL) {
+#if POSTGIS_GEOS_VERSION >= 33
+ rterror("rt_raster_surface: Unable to union the pixel polygons using GEOSUnaryUnion()");
+#else
+ rterror("rt_raster_surface: Unable to union the pixel polygons using GEOSUnionCascaded()");
+#endif
+ return NULL;
+ }
- if (mpoly == NULL) {
- rterror("rt_raster_surface: Unable to union pixel polygons");
- return NULL;
- }
+ /* convert union result from GEOSGeometry to LWGEOM */
+ mpoly = GEOS2LWGEOM(gunion, 0);
+
+ /*
+ is geometry valid?
+ if not, try to make valid
+ */
+ do {
+ LWGEOM *mpolyValid = NULL;
+
+#if POSTGIS_GEOS_VERSION < 33
+ break;
+#endif
+
+ if (GEOSisValid(gunion))
+ break;
+
+ /* make geometry valid */
+ mpolyValid = lwgeom_make_valid(mpoly);
+ if (mpolyValid == NULL) {
+ rtwarn("Cannot fix invalid geometry");
+ break;
}
+
+ lwgeom_free(mpoly);
+ mpoly = mpolyValid;
}
+ while (0);
+
+ GEOSGeom_destroy(gunion);
+ }
+ else {
+ mpoly = lwpoly_as_lwgeom(gv[i].geom);
+ rtdealloc(gv);
}
+ /* specify SRID */
+ lwgeom_set_srid(mpoly, rt_raster_get_srid(raster));
+
if (mpoly != NULL) {
/* convert to multi */
if (!lwgeom_is_collection(mpoly)) {
/* Get pixel geographical shape */
Datum RASTER_getPixelPolygons(PG_FUNCTION_ARGS);
-/* Get raster band's surface */
-Datum RASTER_getBandSurface(PG_FUNCTION_ARGS);
+/* Get raster band's polygon */
+Datum RASTER_getPolygon(PG_FUNCTION_ARGS);
/* Get pixels of value */
Datum RASTER_pixelOfValue(PG_FUNCTION_ARGS);
}
/**
- * Get raster band's surface
+ * Get raster band's polygon
*/
-PG_FUNCTION_INFO_V1(RASTER_getBandSurface);
-Datum RASTER_getBandSurface(PG_FUNCTION_ARGS)
+PG_FUNCTION_INFO_V1(RASTER_getPolygon);
+Datum RASTER_getPolygon(PG_FUNCTION_ARGS)
{
rt_pgraster *pgraster = NULL;
rt_raster raster = NULL;
raster = rt_raster_deserialize(pgraster, FALSE);
if (!raster) {
- elog(ERROR, "RASTER_getBandSurface: Could not deserialize raster");
+ elog(ERROR, "RASTER_getPolygon: Could not deserialize raster");
PG_FREE_IF_COPY(pgraster, 0);
PG_RETURN_NULL();
}
PG_FREE_IF_COPY(pgraster, 0);
if (surface == NULL) {
- elog(ERROR, "RASTER_getBandSurface: Could not get raster band's surface");
+ elog(ERROR, "RASTER_getPolygon: Could not get raster band's surface");
PG_RETURN_NULL();
}
val double precision
);
+-----------------------------------------------------------------------
+-- ST_DumpAsPolygons
+-----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION st_dumpaspolygons(rast raster, band integer DEFAULT 1, exclude_nodata_value boolean DEFAULT TRUE)
RETURNS SETOF geomval
AS 'MODULE_PATHNAME','RASTER_dumpAsPolygons'
LANGUAGE 'c' IMMUTABLE STRICT;
+-----------------------------------------------------------------------
+-- ST_Polygon
+-----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION st_polygon(rast raster, band integer DEFAULT 1)
- RETURNS geometry AS
- $$
- SELECT st_union(f.geom) AS singlegeom
- FROM (SELECT (st_dumpaspolygons($1, $2)).geom AS geom) AS f;
- $$
- LANGUAGE 'sql' IMMUTABLE STRICT;
+ RETURNS geometry AS
+ AS 'MODULE_PATHNAME','RASTER_getPolygon'
+ LANGUAGE 'c' IMMUTABLE STRICT;
-----------------------------------------------------------------------
-- ST_PixelAsPolygons
AS $$ SELECT ST_Centroid(geom) FROM _st_pixelaspolygons($1, NULL, $2, $3) $$
LANGUAGE 'sql' IMMUTABLE STRICT;
------------------------------------------------------------------------
--- ST_BandSurface
------------------------------------------------------------------------
-
-CREATE OR REPLACE FUNCTION st_bandsurface(rast raster, nband integer DEFAULT 1)
- RETURNS geometry
- AS 'MODULE_PATHNAME','RASTER_getBandSurface'
- LANGUAGE 'c' IMMUTABLE STRICT;
-
-----------------------------------------------------------------------
-- Raster Utility Functions
-----------------------------------------------------------------------
RETURN TRUE;
END IF;
- -- get band surface
- surface := ST_BandSurface(rast, nband);
+ -- get band polygon
+ surface := ST_Polygon(rast, nband);
IF surface IS NOT NULL THEN
RETURN ST_Intersects(geom, surface);
mpoly = rt_raster_surface(rast, 0);
CHECK((mpoly != NULL));
wkt = lwgeom_to_text(lwmpoly_as_lwgeom(mpoly));
- CHECK(!strcmp(wkt, "MULTIPOLYGON(((0 0,1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,0 0)))"));
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((0 0,0 -5,5 -5,5 0,0 0)))"));
rtdealloc(wkt);
lwmpoly_free(mpoly);
mpoly = NULL;
mpoly = rt_raster_surface(rast, 0);
CHECK((mpoly != NULL));
wkt = lwgeom_to_text(lwmpoly_as_lwgeom(mpoly));
- CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,1 -1,1 0)))"));
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,1 -1,0 -1,0 -5,4 -5,5 -5,5 0,1 0)))"));
rtdealloc(wkt);
lwmpoly_free(mpoly);
mpoly = NULL;
mpoly = rt_raster_surface(rast, 0);
CHECK((mpoly != NULL));
wkt = lwgeom_to_text(lwmpoly_as_lwgeom(mpoly));
- CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,1 -1,1 0),(1 -1,1 -2,2 -2,2 -1,1 -1)))"));
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,1 -1,0 -1,0 -5,4 -5,5 -5,5 0,1 0),(1 -1,1 -2,2 -2,2 -1,1 -1)))"));
rtdealloc(wkt);
lwmpoly_free(mpoly);
mpoly = NULL;
mpoly = rt_raster_surface(rast, 0);
CHECK((mpoly != NULL));
wkt = lwgeom_to_text(lwmpoly_as_lwgeom(mpoly));
- CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,1 -1,1 0),(1 -1,1 -2,2 -2,2 -1,1 -1),(2 -2,2 -3,3 -3,3 -2,2 -2)))"));
+#if POSTGIS_GEOS_VERSION >= 33
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 -1,1 0,5 0,5 -5,4 -5,0 -5,0 -1,1 -1),(1 -1,1 -2,2 -2,2 -1,1 -1),(2 -2,2 -3,3 -3,3 -2,2 -2)))"));
+#else
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,1 -1,0 -1,0 -5,4 -5,5 -5,5 0,1 0),(1 -1,1 -2,2 -2,2 -3,3 -3,3 -2,2 -2,2 -1,1 -1)))"));
+#endif
rtdealloc(wkt);
lwmpoly_free(mpoly);
mpoly = NULL;
mpoly = rt_raster_surface(rast, 0);
CHECK((mpoly != NULL));
wkt = lwgeom_to_text(lwmpoly_as_lwgeom(mpoly));
- CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,1 -1,1 0),(1 -1,1 -2,2 -2,2 -1,1 -1),(2 -2,2 -3,3 -3,3 -2,2 -2),(3 -3,3 -4,4 -4,4 -3,3 -3)))"));
+#if POSTGIS_GEOS_VERSION >= 33
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 -1,1 0,5 0,5 -5,4 -5,0 -5,0 -1,1 -1),(1 -1,1 -2,2 -2,2 -1,1 -1),(2 -2,2 -3,3 -3,3 -2,2 -2),(3 -3,3 -4,4 -4,4 -3,3 -3)))"));
+#else
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,1 -1,0 -1,0 -5,4 -5,5 -5,5 0,1 0),(1 -1,1 -2,2 -2,2 -3,3 -3,3 -4,4 -4,4 -3,3 -3,3 -2,2 -2,2 -1,1 -1)))"));
+#endif
rtdealloc(wkt);
lwmpoly_free(mpoly);
mpoly = NULL;
mpoly = rt_raster_surface(rast, 0);
CHECK((mpoly != NULL));
wkt = lwgeom_to_text(lwmpoly_as_lwgeom(mpoly));
- CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,4 -4,4 -3,3 -3,3 -2,2 -2,2 -1,1 -1,1 0)),((0 -1,1 -1,1 -2,2 -2,2 -3,3 -3,3 -4,4 -4,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1)))"));
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((4 -4,4 -5,0 -5,0 -1,1 -1,1 -2,2 -2,2 -3,3 -3,3 -4,4 -4)),((1 -1,1 0,5 0,5 -4,4 -4,4 -3,3 -3,3 -2,2 -2,2 -1,1 -1)))"));
rtdealloc(wkt);
lwmpoly_free(mpoly);
mpoly = NULL;
mpoly = rt_raster_surface(rast, 0);
CHECK((mpoly != NULL));
wkt = lwgeom_to_text(lwmpoly_as_lwgeom(mpoly));
- CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 0,2 0,3 0,4 0,4 -1,3 -1,3 -2,2 -2,2 -1,1 -1,1 0)),((0 -1,1 -1,1 -2,2 -2,2 -3,1 -3,1 -4,0 -4,0 -3,0 -2,0 -1)),((4 -1,5 -1,5 -2,5 -3,5 -4,4 -4,4 -3,3 -3,3 -2,4 -2,4 -1)),((2 -3,3 -3,3 -4,4 -4,4 -5,3 -5,2 -5,1 -5,1 -4,2 -4,2 -3)))"));
+ CHECK(!strcmp(wkt, "MULTIPOLYGON(((1 -4,2 -4,2 -3,3 -3,3 -4,4 -4,4 -5,3 -5,1 -5,1 -4)),((1 -4,0 -4,0 -1,1 -1,1 -2,2 -2,2 -3,1 -3,1 -4)),((3 -2,4 -2,4 -1,5 -1,5 -4,4 -4,4 -3,3 -3,3 -2)),((3 -2,2 -2,2 -1,1 -1,1 0,4 0,4 -1,3 -1,3 -2)))"));
rtdealloc(wkt);
lwmpoly_free(mpoly);
mpoly = NULL;
NOTICE: table "raster_surface" does not exist, skipping
-MULTIPOLYGON(((0 0,1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,0 0)))
-MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,1 -1,1 0)))
-MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,1 -1,1 0),(1 -1,1 -2,2 -2,2 -1,1 -1)))
-MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,1 -1,1 0),(1 -1,1 -2,2 -2,2 -1,1 -1),(2 -2,2 -3,3 -3,3 -2,2 -2)))
-MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,5 -5,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1,1 -1,1 0),(1 -1,1 -2,2 -2,2 -1,1 -1),(2 -2,2 -3,3 -3,3 -2,2 -2),(3 -3,3 -4,4 -4,4 -3,3 -3)))
-MULTIPOLYGON(((1 0,2 0,3 0,4 0,5 0,5 -1,5 -2,5 -3,5 -4,4 -4,4 -3,3 -3,3 -2,2 -2,2 -1,1 -1,1 0)),((0 -1,1 -1,1 -2,2 -2,2 -3,3 -3,3 -4,4 -4,4 -5,3 -5,2 -5,1 -5,0 -5,0 -4,0 -3,0 -2,0 -1)))
-MULTIPOLYGON(((1 0,2 0,3 0,4 0,4 -1,3 -1,3 -2,2 -2,2 -1,1 -1,1 0)),((0 -1,1 -1,1 -2,2 -2,2 -3,1 -3,1 -4,0 -4,0 -3,0 -2,0 -1)),((4 -1,5 -1,5 -2,5 -3,5 -4,4 -4,4 -3,3 -3,3 -2,4 -2,4 -1)),((2 -3,3 -3,3 -4,4 -4,4 -5,3 -5,2 -5,1 -5,1 -4,2 -4,2 -3)))
+MULTIPOLYGON(((0 0,0 -5,5 -5,5 0,0 0)))
+MULTIPOLYGON(((1 0,1 -1,0 -1,0 -5,4 -5,5 -5,5 0,1 0)))
+MULTIPOLYGON(((1 0,1 -1,0 -1,0 -5,4 -5,5 -5,5 0,1 0),(1 -1,1 -2,2 -2,2 -1,1 -1)))
+MULTIPOLYGON(((1 0,1 -1,0 -1,0 -5,4 -5,5 -5,5 0,1 0),(1 -1,1 -2,2 -2,2 -3,3 -3,3 -2,2 -2,2 -1,1 -1)))
+MULTIPOLYGON(((1 0,1 -1,0 -1,0 -5,4 -5,5 -5,5 0,1 0),(1 -1,1 -2,2 -2,2 -3,3 -3,3 -4,4 -4,4 -3,3 -3,3 -2,2 -2,2 -1,1 -1)))
+MULTIPOLYGON(((4 -4,4 -5,0 -5,0 -1,1 -1,1 -2,2 -2,2 -3,3 -3,3 -4,4 -4)),((1 -1,1 0,5 0,5 -4,4 -4,4 -3,3 -3,3 -2,2 -2,2 -1,1 -1)))
+MULTIPOLYGON(((1 -4,2 -4,2 -3,3 -3,3 -4,4 -4,4 -5,3 -5,1 -5,1 -4)),((1 -4,0 -4,0 -1,1 -1,1 -2,2 -2,2 -3,1 -3,1 -4)),((3 -2,4 -2,4 -1,5 -1,5 -4,4 -4,4 -3,3 -3,3 -2)),((3 -2,2 -2,2 -1,1 -1,1 0,4 0,4 -1,3 -1,3 -2)))