]> granicus.if.org Git - postgis/commitdiff
Finished BOX3D functions porting.
authorSandro Santilli <strk@keybit.net>
Mon, 6 Sep 2004 10:37:09 +0000 (10:37 +0000)
committerSandro Santilli <strk@keybit.net>
Mon, 6 Sep 2004 10:37:09 +0000 (10:37 +0000)
Added envelope() and extent3d().

git-svn-id: http://svn.osgeo.org/postgis/trunk@769 b70326c6-7e19-0410-871a-916f4a2858ee

lwgeom/MISSING_OBJECTS
lwgeom/lwgeom.h
lwgeom/lwgeom_api.c
lwgeom/lwgeom_box2dfloat4.c
lwgeom/lwgeom_box3d.c
lwgeom/lwgeom_functions_basic.c
lwgeom/lwpostgis.sql.in

index 34d7aafb4a1eff6511d6e25dbdd02817754c3cdc..0c6bd4556b2a534bb7c7318d31e1a932d078e99b 100644 (file)
@@ -6,11 +6,7 @@ FUNC: KEEPING FUNCTION: [simplify(geometry, double precision)]
 FUNC: KEEPING FUNCTION: [asbinary(geometry)]
 FUNC: KEEPING FUNCTION: [asbinary(geometry, text)]
 FUNC: KEEPING FUNCTION: [boundary(geometry)]
-FUNC: KEEPING FUNCTION: [box(geometry)]
-
-FUNC: KEEPING FUNCTION: [envelope(geometry)]
 FUNC: KEEPING FUNCTION: [equals(geometry, geometry)]
-
 FUNC: KEEPING FUNCTION: [isempty(geometry)]
 FUNC: KEEPING FUNCTION: [segmentize(geometry, double precision)]
 FUNC: KEEPING FUNCTION: [unite_garray(geometry[])]
@@ -60,10 +56,3 @@ FUNC: KEEPING FUNCTION: [datatype(chip)]
 FUNC: KEEPING FUNCTION: [compression(chip)]
 FNCAST: KEEPING FNCAST geometry(chip) (see CAST)
 
---- BOX3D
-FNCAST: KEEPING FNCAST box3dtobox(box3d) (see CAST)
-FNCAST: KEEPING FNCAST geometry(box3d) (see CAST)
-FUNC: KEEPING FUNCTION: [box3d(geometry)]
-FUNC: KEEPING FUNCTION: [box3dtobox(box3d)]
-FUNC: KEEPING FUNCTION: [combine_bbox(box3d, geometry)]
-FUNC: KEEPING FUNCTION: [geometry(box3d)]
index ad3704ac27b6250b5696466c230549ad13f35e5a..d73a69434f86766761af1acd837ab8203ffb2cc5 100644 (file)
@@ -494,8 +494,9 @@ extern BOX2DFLOAT4 *box_to_box2df(BOX *box);  // postgresql standard type
 
 extern BOX3D box2df_to_box3d(BOX2DFLOAT4 *box);
 extern void box2df_to_box3d_p(BOX2DFLOAT4 *box, BOX3D *box3d);
+extern BOX box2df_to_box(BOX2DFLOAT4 *box);  // postgresql standard type
+extern void box2df_to_box_p(BOX2DFLOAT4 *box, BOX *out); // postgresql standard type
 
-extern BOX   box2df_to_box(BOX2DFLOAT4 *box);  // postgresql standard type
 extern BOX3D *combine_boxes(BOX3D *b1, BOX3D *b2);
 
 
@@ -681,6 +682,8 @@ Datum lwgeom_gbox_picksplit(PG_FUNCTION_ARGS);
 
 extern float LWGEOM_Minf(float a, float b);
 extern float LWGEOM_Maxf(float a, float b);
+extern double LWGEOM_Mind(double a, double b);
+extern double LWGEOM_Maxd(double a, double b);
 
 
 
index 85a26497a2ae194ad0ce2096e5a525d7ad35a9cd..f2fd1ecf6c038510f97788368196223a792b2e3a 100644 (file)
@@ -265,6 +265,19 @@ BOX   box2df_to_box(BOX2DFLOAT4 *box)
        return result;
 }
 
+// convert BOX2D to postgresql BOX
+void
+box2df_to_box_p(BOX2DFLOAT4 *box, BOX *out)
+{
+       if (box == NULL) return;
+
+       out->low.x = nextDown_d(box->xmin);
+       out->low.y = nextDown_d(box->ymin);
+
+       out->high.x = nextUp_d(box->xmax);
+       out->high.y = nextUp_d(box->ymax);
+}
+
 
 // returns a BOX3D that encloses b1 and b2
 // combine_boxes(NULL,A) --> A
index 9f6be59dafbfc0a00e31ce9035e556cc81c5ea5d..829682ff06fbd4014d8205623a40ee6192cf6e83 100644 (file)
@@ -33,6 +33,7 @@ Datum BOX2DFLOAT4_xmin(PG_FUNCTION_ARGS);
 Datum BOX2DFLOAT4_ymin(PG_FUNCTION_ARGS);
 Datum BOX2DFLOAT4_xmax(PG_FUNCTION_ARGS);
 Datum BOX2DFLOAT4_ymax(PG_FUNCTION_ARGS);
+Datum BOX2DFLOAT4_combine(PG_FUNCTION_ARGS);
 
 
 
@@ -99,12 +100,11 @@ Datum BOX2DFLOAT4_out(PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1(LWGEOM_to_BOX2DFLOAT4);
 Datum LWGEOM_to_BOX2DFLOAT4(PG_FUNCTION_ARGS)
 {
-       char *lwgeom = (char *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-       BOX2DFLOAT4 box, *result;
+       LWGEOM *lwgeom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       BOX2DFLOAT4 *result;
 
-       box = getbox2d(lwgeom+4);
        result = palloc(sizeof(BOX2DFLOAT4));
-       memcpy(result,&box, sizeof(BOX2DFLOAT4));
+       getbox2d_p(SERIALIZED_FORM(lwgeom), result);
        PG_RETURN_POINTER(result);
 }
 
@@ -358,3 +358,52 @@ Datum BOX2DFLOAT4_ymax(PG_FUNCTION_ARGS)
        BOX2DFLOAT4 *box = (BOX2DFLOAT4 *)PG_GETARG_POINTER(0);
        PG_RETURN_FLOAT4(box->ymax);
 }
+
+PG_FUNCTION_INFO_V1(BOX2DFLOAT4_combine);
+Datum BOX2DFLOAT4_combine(PG_FUNCTION_ARGS)
+{
+       Pointer box2d_ptr = PG_GETARG_POINTER(0);
+       Pointer geom_ptr = PG_GETARG_POINTER(1);
+       BOX2DFLOAT4 *a,*b;
+       char *lwgeom;
+       BOX2DFLOAT4 box, *result;
+
+       if  ( (box2d_ptr == NULL) && (geom_ptr == NULL) )
+       {
+               PG_RETURN_NULL(); // combine_box2d(null,null) => null
+       }
+
+       result = (BOX2DFLOAT4 *)palloc(sizeof(BOX2DFLOAT4));
+
+       if (box2d_ptr == NULL)
+       {
+               lwgeom = (char *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+               box = getbox2d(lwgeom+4);
+               memcpy(result, &box, sizeof(BOX2DFLOAT4));
+               PG_RETURN_POINTER(result);
+       }
+
+       // combine_bbox(BOX3D, null) => BOX3D
+       if (geom_ptr == NULL)
+       {
+               memcpy(result, (char *)PG_GETARG_DATUM(0), sizeof(BOX2DFLOAT4));
+               PG_RETURN_POINTER(result);
+       }
+
+       //combine_bbox(BOX3D, geometry) => union(BOX3D, geometry->bvol)
+
+       lwgeom = (char *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       box = getbox2d(lwgeom+4);
+
+       a = (BOX2DFLOAT4 *)PG_GETARG_DATUM(0);
+       b = &box;
+
+       result->xmax = LWGEOM_Maxf(a->xmax, b->xmax);
+       result->ymax = LWGEOM_Maxf(a->ymax, b->ymax);
+       result->xmin = LWGEOM_Minf(a->xmin, b->xmin);
+       result->ymin = LWGEOM_Minf(a->ymin, b->ymin);
+
+       PG_RETURN_POINTER(result);
+}
+
index d0e52689b372f79ac1a2656c85600255ac92327a..29291e367bcc177374a190a18acf7318704c79e3 100644 (file)
 Datum BOX3D_in(PG_FUNCTION_ARGS);
 Datum BOX3D_out(PG_FUNCTION_ARGS);
 Datum LWGEOM_to_BOX3D(PG_FUNCTION_ARGS);
+Datum BOX3D_to_LWGEOM(PG_FUNCTION_ARGS);
 Datum BOX3D_expand(PG_FUNCTION_ARGS);
 Datum BOX3D_to_BOX2DFLOAT4(PG_FUNCTION_ARGS);
-
+Datum BOX3D_to_BOX(PG_FUNCTION_ARGS);
 Datum BOX3D_xmin(PG_FUNCTION_ARGS);
 Datum BOX3D_ymin(PG_FUNCTION_ARGS);
 Datum BOX3D_zmin(PG_FUNCTION_ARGS);
 Datum BOX3D_xmax(PG_FUNCTION_ARGS);
 Datum BOX3D_ymax(PG_FUNCTION_ARGS);
 Datum BOX3D_zmax(PG_FUNCTION_ARGS);
+Datum BOX3D_combine(PG_FUNCTION_ARGS);
 
 /*
  *  BOX3D_in - takes a string rep of BOX3D and returns internal rep
@@ -141,6 +143,53 @@ Datum BOX3D_to_BOX2DFLOAT4(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(out);
 }
 
+PG_FUNCTION_INFO_V1(BOX3D_to_BOX);
+Datum BOX3D_to_BOX(PG_FUNCTION_ARGS)
+{
+       BOX3D *in = (BOX3D *)PG_GETARG_POINTER(0);
+       BOX2DFLOAT4 *box2d = box3d_to_box2df(in);
+       BOX *box = palloc(sizeof(BOX));
+
+       box2df_to_box_p(box2d, box);
+       PG_RETURN_POINTER(box);
+}
+
+PG_FUNCTION_INFO_V1(BOX3D_to_LWGEOM);
+Datum BOX3D_to_LWGEOM(PG_FUNCTION_ARGS)
+{
+       BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
+       POINT2D *pts = palloc(sizeof(POINT2D)*5);
+       POINTARRAY *pa[1];
+       LWPOLY *poly;
+       int wantbbox = 0;
+       LWGEOM *result;
+       char *ser;
+
+       // Assign coordinates to POINT2D array
+       pts[0].x = box->xmin; pts[0].y = box->ymin;
+       pts[1].x = box->xmin; pts[1].y = box->ymax;
+       pts[2].x = box->xmax; pts[2].y = box->ymax;
+       pts[3].x = box->xmax; pts[3].y = box->ymin;
+       pts[4].x = box->xmin; pts[4].y = box->ymin;
+
+       // Construct point array
+       pa[0] = palloc(sizeof(POINTARRAY));
+       pa[0]->serialized_pointlist = (char *)pts;
+       pa[0]->ndims = 2;
+       pa[0]->npoints = 5;
+
+       // Construct polygon
+       poly = lwpoly_construct(2, -1, 1, pa);
+
+       // Serialize polygon
+       ser = lwpoly_serialize(poly);
+
+       // Construct LWGEOM 
+       result = LWGEOM_construct(ser, -1, wantbbox);
+       
+       PG_RETURN_POINTER(result);
+}
+
 /* Expand given box of 'd' units in all directions */
 void
 expand_box3d(BOX3D *box, double d)
@@ -172,7 +221,6 @@ PG_FUNCTION_INFO_V1(LWGEOM_to_BOX3D);
 Datum LWGEOM_to_BOX3D(PG_FUNCTION_ARGS)
 {
        LWGEOM *lwgeom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-       BOX2DFLOAT4 box;
        BOX3D *result;
 
        result = lw_geom_getBB(SERIALIZED_FORM(lwgeom));
@@ -221,3 +269,67 @@ Datum BOX3D_zmax(PG_FUNCTION_ARGS)
        BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
        PG_RETURN_FLOAT8(box->zmax);
 }
+
+
+PG_FUNCTION_INFO_V1(BOX3D_combine);
+Datum BOX3D_combine(PG_FUNCTION_ARGS)
+{
+       Pointer box3d_ptr = PG_GETARG_POINTER(0);
+       Pointer geom_ptr = PG_GETARG_POINTER(1);
+       BOX3D *a,*b;
+       LWGEOM *lwgeom;
+       BOX3D *box, *result;
+
+       if  ( (box3d_ptr == NULL) && (geom_ptr == NULL) )
+       {
+               PG_RETURN_NULL(); 
+       }
+
+       result = (BOX3D *)palloc(sizeof(BOX3D));
+
+       if (box3d_ptr == NULL)
+       {
+               lwgeom = (LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+               box = lw_geom_getBB(SERIALIZED_FORM(lwgeom));
+               memcpy(result, box, sizeof(BOX3D));
+               PG_RETURN_POINTER(result);
+       }
+
+       // combine_bbox(BOX3D, null) => BOX3D
+       if (geom_ptr == NULL)
+       {
+               memcpy(result, (char *)PG_GETARG_DATUM(0), sizeof(BOX3D));
+               PG_RETURN_POINTER(result);
+       }
+
+       lwgeom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       box = lw_geom_getBB(SERIALIZED_FORM(lwgeom));
+
+       a = (BOX3D *)PG_GETARG_DATUM(0);
+       b = box;
+
+       result->xmax = LWGEOM_Maxd(a->xmax, b->xmax);
+       result->ymax = LWGEOM_Maxd(a->ymax, b->ymax);
+       result->zmax = LWGEOM_Maxd(a->zmax, b->zmax);
+       result->xmin = LWGEOM_Mind(a->xmin, b->xmin);
+       result->ymin = LWGEOM_Mind(a->ymin, b->ymin);
+       result->zmin = LWGEOM_Mind(a->zmin, b->zmin);
+
+       PG_RETURN_POINTER(result);
+}
+
+//min(a,b)
+double LWGEOM_Mind(double a, double b)
+{
+       if (a<b)
+               return a;
+       return b;
+}
+
+//max(a,b)
+double LWGEOM_Maxd(double a, double b)
+{
+       if (b>a)
+               return b;
+       return a;
+}
index 9193475f4db0c9e0955a84e1d7dd450ec069fe1c..32930638f0c6027e6598e0a1ea0e6703146afae9 100644 (file)
@@ -14,7 +14,6 @@
 
 //#define DEBUG
 
-Datum combine_box2d(PG_FUNCTION_ARGS);
 Datum LWGEOM_mem_size(PG_FUNCTION_ARGS);
 Datum LWGEOM_summary(PG_FUNCTION_ARGS);
 Datum LWGEOM_npoints(PG_FUNCTION_ARGS);
@@ -39,6 +38,8 @@ Datum LWGEOM_collect(PG_FUNCTION_ARGS);
 Datum LWGEOM_accum(PG_FUNCTION_ARGS);
 Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS);
 Datum LWGEOM_expand(PG_FUNCTION_ARGS);
+Datum LWGEOM_to_BOX(PG_FUNCTION_ARGS);
+Datum LWGEOM_envelope(PG_FUNCTION_ARGS);
 
 // internal
 char * lwgeom_summary_recursive(char *serialized, int offset);
@@ -777,54 +778,6 @@ lwgeom_pt_inside_circle(POINT2D *p, double cx, double cy, double rad)
 
 /*------------------------------------------------------------------*/
 
-PG_FUNCTION_INFO_V1(combine_box2d);
-Datum combine_box2d(PG_FUNCTION_ARGS)
-{
-       Pointer box2d_ptr = PG_GETARG_POINTER(0);
-       Pointer geom_ptr = PG_GETARG_POINTER(1);
-       BOX2DFLOAT4 *a,*b;
-       char *lwgeom;
-       BOX2DFLOAT4 box, *result;
-
-       if  ( (box2d_ptr == NULL) && (geom_ptr == NULL) )
-       {
-               PG_RETURN_NULL(); // combine_box2d(null,null) => null
-       }
-
-       result = (BOX2DFLOAT4 *)palloc(sizeof(BOX2DFLOAT4));
-
-       if (box2d_ptr == NULL)
-       {
-               lwgeom = (char *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-
-               box = getbox2d(lwgeom+4);
-               memcpy(result, &box, sizeof(BOX2DFLOAT4));
-               PG_RETURN_POINTER(result);
-       }
-
-       // combine_bbox(BOX3D, null) => BOX3D
-       if (geom_ptr == NULL)
-       {
-               memcpy(result, (char *)PG_GETARG_DATUM(0), sizeof(BOX2DFLOAT4));
-               PG_RETURN_POINTER(result);
-       }
-
-       //combine_bbox(BOX3D, geometry) => union(BOX3D, geometry->bvol)
-
-       lwgeom = (char *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-       box = getbox2d(lwgeom+4);
-
-       a = (BOX2DFLOAT4 *)PG_GETARG_DATUM(0);
-       b = &box;
-
-       result->xmax = LWGEOM_Maxf(a->xmax, b->xmax);
-       result->ymax = LWGEOM_Maxf(a->ymax, b->ymax);
-       result->xmin = LWGEOM_Minf(a->xmin, b->xmin);
-       result->ymin = LWGEOM_Minf(a->ymin, b->ymin);
-
-       PG_RETURN_POINTER(result);
-}
-
 //find the size of geometry
 PG_FUNCTION_INFO_V1(LWGEOM_mem_size);
 Datum LWGEOM_mem_size(PG_FUNCTION_ARGS)
@@ -2325,3 +2278,61 @@ Datum LWGEOM_expand(PG_FUNCTION_ARGS)
        
        PG_RETURN_POINTER(result);
 }
+
+// Convert geometry to BOX (internal postgres type)
+PG_FUNCTION_INFO_V1(LWGEOM_to_BOX);
+Datum LWGEOM_to_BOX(PG_FUNCTION_ARGS)
+{
+       LWGEOM *lwgeom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       BOX2DFLOAT4 box2d;
+       BOX *result = (BOX *)palloc(sizeof(BOX));
+
+       getbox2d_p(SERIALIZED_FORM(lwgeom), &box2d);
+       box2df_to_box_p(&box2d, result);
+
+       PG_RETURN_POINTER(result);
+}
+
+// makes a polygon of the features bvol - 1st point = LL 3rd=UR
+// 2d only. (3d might be worth adding).
+// create new geometry of type polygon, 1 ring, 5 points
+PG_FUNCTION_INFO_V1(LWGEOM_envelope);
+Datum LWGEOM_envelope(PG_FUNCTION_ARGS)
+{
+       LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       BOX2DFLOAT4 box;
+       POINT2D *pts = palloc(sizeof(POINT2D)*5);
+       POINTARRAY *pa[1];
+       LWPOLY *poly;
+       int SRID;
+       LWGEOM *result;
+       char *ser;
+
+       // get geometry box and SRID
+       getbox2d_p(SERIALIZED_FORM(geom), &box);
+       SRID = lwgeom_getsrid(SERIALIZED_FORM(geom));
+
+       // Assign coordinates to POINT2D array
+       pts[0].x = box.xmin; pts[0].y = box.ymin;
+       pts[1].x = box.xmin; pts[1].y = box.ymax;
+       pts[2].x = box.xmax; pts[2].y = box.ymax;
+       pts[3].x = box.xmax; pts[3].y = box.ymin;
+       pts[4].x = box.xmin; pts[4].y = box.ymin;
+
+       // Construct point array
+       pa[0] = palloc(sizeof(POINTARRAY));
+       pa[0]->serialized_pointlist = (char *)pts;
+       pa[0]->ndims = 2;
+       pa[0]->npoints = 5;
+
+       // Construct polygon
+       poly = lwpoly_construct(2, SRID, 1, pa);
+
+       // Serialize polygon
+       ser = lwpoly_serialize(poly);
+
+       // Construct LWGEOM 
+       result = LWGEOM_construct(ser, SRID, lwgeom_hasBBOX(geom->type));
+       
+       PG_RETURN_POINTER(result);
+}
index 5c279de1c797329c7813a296c4320050d38ee0dd..c32396c3f0500480ae9440e1a5d56e8da7bc829b 100644 (file)
@@ -1300,6 +1300,11 @@ CREATEFUNCTION expand(geometry,float8)
        AS '@MODULE_FILENAME@', 'LWGEOM_expand'
        LANGUAGE 'C' WITH (iscachable,isstrict);
 
+CREATEFUNCTION envelope(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@', 'LWGEOM_envelope'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
 ------------------------------------------------------------------------
 
 --
@@ -1308,7 +1313,7 @@ CREATEFUNCTION expand(geometry,float8)
 
 CREATEFUNCTION combine_bbox(box2d,geometry)
        RETURNS box2d
-       AS '@MODULE_FILENAME@', 'combine_box2d'
+       AS '@MODULE_FILENAME@', 'BOX2DFLOAT4_combine'
        LANGUAGE 'C';
 
 CREATE AGGREGATE extent(
@@ -1317,6 +1322,17 @@ CREATE AGGREGATE extent(
        stype = box2d
        );
 
+CREATEFUNCTION combine_bbox(box3d,geometry)
+       RETURNS box3d
+       AS '@MODULE_FILENAME@', 'BOX3D_combine'
+       LANGUAGE 'C';
+
+CREATE AGGREGATE extent3d(
+       sfunc = combine_bbox,
+       basetype = geometry,
+       stype = box3d
+       );
+
 -----------------------------------------------------------------------
 -- CREATE_HISTOGRAM2D( <box2d>, <size> )
 -----------------------------------------------------------------------
@@ -2358,6 +2374,13 @@ CREATEFUNCTION box3d(geometry)
 
 CREATE CAST (geometry as box3d) WITH FUNCTION box3d(geometry) AS IMPLICIT ;
 
+CREATEFUNCTION box(geometry)
+        RETURNS box
+        AS '@MODULE_FILENAME@','LWGEOM_to_BOX'
+        LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATE CAST (geometry as box) WITH FUNCTION box(geometry) AS IMPLICIT ;
+
 CREATEFUNCTION box2d(box3d)
         RETURNS box2d
         AS '@MODULE_FILENAME@','BOX3D_to_BOX2DFLOAT4'
@@ -2372,6 +2395,26 @@ CREATEFUNCTION box3d(box2d)
 
 CREATE CAST (box2d as box3d) WITH FUNCTION box3d(box2d) AS IMPLICIT ;
 
+CREATEFUNCTION box(box3d)
+        RETURNS box
+        AS '@MODULE_FILENAME@','BOX3D_to_BOX'
+        LANGUAGE 'C' WITH (isstrict,iscachable);
+
+-- this is kept for backward-compatibility
+CREATEFUNCTION box3dtobox(box3d)
+        RETURNS box
+        AS 'SELECT box($1)'
+       LANGUAGE 'SQL' WITH (isstrict,iscachable);
+
+CREATE CAST (box3d as box) WITH FUNCTION box(box3d) AS IMPLICIT ;
+
+CREATEFUNCTION geometry(box3d)
+        RETURNS geometry
+        AS '@MODULE_FILENAME@','BOX3D_to_LWGEOM'
+        LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATE CAST (box3d as geometry) WITH FUNCTION geometry(box3d) AS IMPLICIT ;
+
 ---------------------------------------------------------------
 -- END
 ---------------------------------------------------------------