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.