Playing around with Centroids by using different seed values

The CENTROID package that I make available for free exposes a function for generating centroids of polygon (or area) objects.

Here is the function:

   /* ----------------------------------------------------------------------------------------
   * @function   : centroid_a
   * @precis     : Generates centroid for a polygon.
   * @version    : 1.3
   * @description: The standard mdsys.sdo_geom.sdO_centroid function does not guarantee
   *               that the centroid it generates falls inside the polygon.
   *               This function ensures that the centroid of any arbitrary polygon falls within the polygon.
   * @param      : p_geometry     : MDSYS.SDO_GEOMETRY : The geometry object.
   * @param      : P_Method       : pls_integer
   *                              : 0 = Use average of all Area's vertices for starting X centroid calculation
   *                                1 = Use centre X of MBR
   *                                2 = User supplied starting seed X
   *                                3 = Use Standard Oracle centroid function
   *                                4 = Use Oracle implementation of PointOnSurface
   * @param      : p_seed_x       : Number : Starting X ordinate for which a Y that is inside the polygon is returned.
   * @param      : P_Dec_Places   : pls_integer : Ordinate rounding precision for X, Y ordinates.
   * @param      : P_Tolerance    : number      : Tolerance for Oracle functions.
   * @param      : p_loops        : pls_integer : Number of attempts to find centroid based on small changes to seed
   * @return     : centroid       : MDSYS.SDO_GEOMETRY : The centroid.
   * @requires   : GetVector()
   * @history    : Simon Greener - Jul 2008 - Original coding of centroid_a as internal function
   * @history    : Simon Greener - Jan 2012 - Exposed internal function. Added p_seed_x support.
   * @copyright  : Free for public use
   **/
   FUNCTION Centroid_A(P_Geometry   IN Mdsys.Sdo_Geometry,
                       P_Method     IN Pls_Integer DEFAULT 1,
                       P_Seed_X     IN NUMBER      DEFAULT NULL,
                       P_Dec_Places IN Pls_Integer DEFAULT 3,
                       P_Tolerance  IN Pls_Integer DEFAULT 0.05,
                       p_loops      IN pls_integer DEFAULT 10)
     RETURN MDSYS.SDO_GEOMETRY DETERMINISTIC;

If you call the function with its defaults, the p_method parameter will be 1. For this value, the code determines the X ordinate extent of the polygon’s MBR, and selects the middle value as a starting point. If the centroid can be determined with this value, the function returns that value. If it cannot it will randomly generate a new X ordinate value between the minimum and maximum value. It will do this for as many times as it needs to up to the p_loop parameter value.

If this is not acceptable, you can generate your own starting X value. The following SQL shows how to do this for two polygons.

 WITH POLY AS (
   SELECT 1 AS POLY_ID,
          SDO_GEOMETRY(2003,2154,SDO_POINT_TYPE(302941.0,6669203.45,NULL),SDO_ELEM_INFO_ARRAY(1,1003,1),SDO_ORDINATE_ARRAY(297360.0,6667914.0, 297660.0,6667653.0, 298609.0,6666944.0, 299789.0,6664881.0, 300680.0,6664235.0, 301667.0,6664862.0, 303232.0,6664738.0, 304687.0,6663414.0, 304945.0,6663116.0, 305198.0,6662814.0, 306251.0,6661156.0, 307301.0,6657791.0, 308037.0,6656883.0, 308202.0,6657247.0, 308522.0,6661532.0, 307817.0,6662929.0, 305833.0,6664168.0, 305510.0,6664375.0, 303260.0,6665837.0, 302505.0,6665938.0, 302397.0,6666285.0, 302495.0,6667977.0, 301563.0,6668110.0, 301929.0,6668266.0, 303324.0,6667787.0, 303670.0,6668397.0, 303702.0,6669597.0, 303379.0,6670300.0, 302298.0,6670765.0, 301143.0,6671816.0, 300762.0,6671916.0, 299584.0,6671989.0, 297610.0,6671815.0, 298172.0,6670055.0, 297408.0,6668688.0, 297360.0,6667914.0))
            AS POLYGON
     FROM DUAL UNION ALL
   SELECT 2 AS POLY_ID,
          SDO_GEOMETRY(2003,2154,SDO_POINT_TYPE(360179.0,6575409.45,NULL),SDO_ELEM_INFO_ARRAY(1,1003,1),SDO_ORDINATE_ARRAY(359878.0,6574428.0, 360194.0,6574227.0, 361803.0,6573283.0, 363464.0,6571791.0, 363700.0,6571492.0, 364187.0,6570906.0, 366530.0,6569742.0, 367624.0,6569426.0, 369521.0,6569568.0, 369878.0,6569424.0, 371622.0,6571410.0, 370159.0,6571358.0, 369671.0,6572729.0, 369472.0,6573056.0, 368841.0,6574386.0, 368122.0,6574650.0, 367017.0,6574363.0, 365341.0,6575902.0, 365027.0,6576142.0, 363611.0,6576780.0, 360865.0,6576508.0, 360487.0,6576519.0, 359009.0,6576823.0, 357554.0,6577889.0, 359387.0,6578390.0, 359775.0,6579403.0, 356629.0,6579554.0, 355793.0,6578505.0, 354495.0,6577699.0, 354353.0,6577326.0, 353535.0,6577430.0, 352627.0,6578530.0, 352221.0,6580046.0, 352254.0,6580505.0, 352397.0,6580138.0, 352782.0,6579453.0, 353155.0,6579362.0, 353703.0,6580730.0, 354082.0,6580648.0, 354163.0,6579933.0, 354515.0,6579796.0, 355125.0,6580194.0, 354737.0,6581702.0, 353718.0,6582869.0, 352553.0,6583035.0, 351635.0,6582314.0, 350259.0,6581598.0, 348736.0,6581698.0, 348873.0,6580572.0, 349939.0,6578955.0, 350054.0,6578581.0, 350563.0,6577117.0, 352612.0,6576013.0, 355156.0,6576764.0, 355539.0,6576787.0, 359878.0,6574428.0))
            AS polygon
     FROM dual
 )
 -- select poly_id,a.polygon,CENTROID.CENTROID_A(a.POLYGON,0,null,2,2,2) as cPoly from poly a;
 , X_EXTENT AS (
   SELECT C.POLY_ID,
          D.ROW_ID,
          MINX + ((MAXX-MINX)/50)*ROW_ID AS INC,
          C.POLYGON,
          sdo_geometry(2002,c.polygon.sdo_srid,NULL,sdo_elem_info_array(1,2,1),sdo_ordinate_array(minx,miny,maxx,miny)) AS mbr_baseline
     FROM (SELECT b.POLY_ID,
                  SDO_GEOM.SDO_MIN_MBR_ORDINATE(MBR,1) AS MINX,
                  SDO_GEOM.SDO_MIN_MBR_ORDINATE(MBR,2) AS MINY,
                  SDO_GEOM.SDO_MIN_MBR_ORDINATE(MBR,3) AS MAXX,
                  b.polygon
             FROM (SELECT P.POLY_ID, p.polygon, SDO_GEOM.SDO_MBR(P.POLYGON) AS MBR
                     FROM POLY P
                  ) B
           ) c,
           (SELECT level AS row_id FROM dual CONNECT BY level < 50) d
 )
 SELECT a.POLY_ID, a.row_id, CENTROID.CENTROID_A(a.POLYGON,2,a.INC,2,2,2), mbr_baseline
   FROM X_EXTENT a
   ORDER BY a.poly_id, a.row_id;

The different centroid values are displayed in the following two images. The actual centroid created by the CENTROID_A function with the default p_method value is shown in red.

I hope this is of help to someone out there.

Leave a Reply

Your email address will not be published. Required fields are marked *