Top 5 Recent Articles
ARTICLES CATEGORIES
- Algorithms (22)
- All (399)
- Biography (1)
- Blog (44)
- Business Requirements (1)
- Commentary (1)
- Conversion (2)
- Customers (2)
- Data Models (1)
- Education (2)
- GeoRaptor (13)
- GPS (1)
- Image Processing (2)
- Import Export (8)
- Licensing (2)
- LiDAR (1)
- Linear Referencing (4)
- Manifold GIS (3)
- Mapping (1)
- MySQL Spatial (7)
- Networking and Routing (including Optimization) (5)
- Open Source (18)
- Oracle Spatial and Locator (194)
- Partitioning (1)
- PostGIS (36)
- Projections (1)
- Published Articles (1)
- qGIS (1)
- Recommendations (1)
- Services (1)
- Software Change Log (1)
- Source Code (37)
- Space Curves (9)
- Spatial Database Functions (109)
- Spatial DB comparison (1)
- Spatial XML Processing (11)
- SQL Server Spatial (92)
- Standards (3)
- Stored Procedure (17)
- Tessellation or Gridding (10)
- Tools (2)
- Topological Relationships (1)
- Training (2)
- Triangulation (2)
Optimized Rectangle to 5 Point Polygon
Here is a function that takes a any polygon whose elements are defined by an optimized rectangle and converts all elements to 5 vertex polygons. Polygon may be multi; may have non-optimized rectangle components. Does NOT process geometries with compound elements.
CREATE OR REPLACE FUNCTION Rectangle2Polygon(p_geometry IN mdsys.sdo_geometry) RETURN mdsys.sdo_geometry deterministic AS v_dims pls_integer; v_vertices mdsys.vertex_set_type; v_ordinates mdsys.sdo_ordinate_array := NEW mdsys.sdo_ordinate_array(NULL); v_elem_info mdsys.sdo_elem_info_array := NEW mdsys.sdo_elem_info_array(NULL); v_ord_count pls_integer; v_num_elements pls_integer; v_num_sub_elements pls_integer; v_element sdo_geometry; -- A common utility function that allows for a single vertex -- to be added to the end of an sdo_ordinate_array -- All inserts are before existing vertex in same position -- PROCEDURE appendVertex(p_ordinates IN OUT nocopy mdsys.sdo_ordinate_array, p_dims IN INTEGER, p_x IN NUMBER, p_y IN NUMBER, p_z IN NUMBER, p_w IN NUMBER) IS v_ord pls_integer := 0; BEGIN IF ( p_ordinates IS NULL ) THEN p_ordinates := NEW sdo_ordinate_array(0); p_ordinates.DELETE; END IF; v_ord := p_ordinates.COUNT + 1; p_ordinates.EXTEND(p_dims); -- Insert first vertex p_ordinates(v_ord) := p_X; p_ordinates(v_ord+1) := p_Y; IF (p_dims>=3) THEN p_ordinates(v_ord+2) := p_z; IF ( p_dims > 3 ) THEN p_ordinates(v_ord+3) := p_w; END IF; END IF; END appendVertex; FUNCTION hasOptimizedRectangles(p_geometry IN sdo_geometry) RETURN INTEGER IS v_etype pls_integer; v_interpretation pls_integer; v_elements pls_integer := 0; v_rectangle_count NUMBER := 0; BEGIN IF ( p_geometry.get_gtype() IN (1,2,5,6) ) THEN RETURN 0; END IF; v_elements := ( ( p_geometry.sdo_elem_info.COUNT / 3 ) - 1 ); <<element_extraction>> FOR v_i IN 0 .. v_elements LOOP v_etype := p_geometry.sdo_elem_info(v_i * 3 + 2); v_interpretation := p_geometry.sdo_elem_info(v_i * 3 + 3); IF ( ( v_etype IN (1003,2003) AND v_interpretation = 3 ) ) THEN v_rectangle_count := v_rectangle_count + 1; END IF; END LOOP element_extraction; RETURN v_rectangle_count; END hasOptimizedRectangles; FUNCTION hasCompoundElements(p_geometry IN sdo_geometry) RETURN INTEGER IS v_etype pls_integer; v_elements pls_integer := 0; BEGIN IF ( p_geometry.get_gtype() IN (1,2,5,6) ) THEN RETURN 0; END IF; v_elements := ( ( p_geometry.sdo_elem_info.COUNT / 3 ) - 1 ); <<element_extraction>> FOR v_i IN 0 .. v_elements LOOP v_etype := p_geometry.sdo_elem_info(v_i * 3 + 2); IF ( v_etype IN (1005,2005) ) THEN RETURN 1; END IF; END LOOP element_extraction; RETURN 0; END hasCompoundElements; PROCEDURE processElement(p_geometry IN sdo_geometry, p_ordinates IN OUT nocopy mdsys.sdo_ordinate_array, p_elem_info IN OUT nocopy mdsys.sdo_elem_info_array) AS v_vertices mdsys.vertex_set_type; v_interpretation pls_integer; v_etype pls_integer; v_start_coord mdsys.vertex_type; v_end_coord mdsys.vertex_type; v_temp_coord mdsys.vertex_type; v_num_sub_elements pls_integer; v_sub_element sdo_geometry; v_dims pls_integer; BEGIN -- Can only be called if p_geometry contains at least one optimized rectangle. -- Count sub elements v_dims := p_geometry.get_dims(); v_num_sub_elements := (p_geometry.sdo_elem_info.COUNT / 3); FOR i IN 1..v_num_sub_elements loop dbms_output.put_line('...... Processing Sub Element ' || i || ' of ' || v_num_sub_elements); v_sub_element := sdo_util.EXTRACT(p_geometry,1,i); v_vertices := sdo_util.getVertices(v_sub_element); v_interpretation := v_sub_element.sdo_elem_info(3); v_etype := p_geometry.sdo_elem_info((i-1)*3+2); /* Use Original etype 2003 for holes as extract turns holes into 1003 */ -- Update elem_info_array p_elem_info.EXTEND(3); p_elem_info(p_elem_info.COUNT-2) := v_ordinates.COUNT + 1; -- If sub element not an optimized rectangle copy it through IF ( v_interpretation <> 3 ) THEN dbms_output.put_line('........ Skipping ' || i || ' sub element as is not optimized rectangle.'); -- Copy through elem_info and ordinates p_elem_info(p_elem_info.COUNT-1) := v_sub_element.sdo_elem_info(2); p_elem_info(p_elem_info.COUNT) := v_sub_element.sdo_elem_info(3); -- Now copy all ordinates FOR i IN 1..v_vertices.COUNT Loop appendVertex( p_ordinates, v_dims, v_vertices(i).x, v_vertices(i).y, v_vertices(i).z, v_vertices(i).w ); END Loop; Continue; END IF; dbms_output.put_line('........ Converting Processing Found Optimized Rectangle.'); -- Change to optimized rectangle in to a 5 vertex polygon p_elem_info(p_elem_info.COUNT-1) := CASE WHEN i=1 THEN v_sub_element.sdo_elem_info(2) ELSE 2003 END; p_elem_info(p_elem_info.COUNT) := 1; v_start_coord := v_vertices(1); v_end_coord := v_vertices(2); -- Check inverted only if first element IF ( i = 1 ) THEN v_temp_coord := v_vertices(1); v_temp_coord.x := least(v_start_coord.x,v_end_coord.x); v_temp_coord.y := least(v_start_coord.y,v_end_coord.y); v_end_coord.x := greatest(v_start_coord.x,v_end_coord.x); v_end_coord.y := greatest(v_start_coord.y,v_end_coord.y); v_start_coord.x := v_temp_coord.x; v_start_coord.y := v_temp_coord.y; END IF; -- First coordinate appendVertex( p_ordinates, v_dims, v_start_coord.x, v_start_coord.y, v_start_coord.z, v_start_coord.w ); -- Second coordinate IF ( v_etype = 1003 ) THEN appendVertex(p_ordinates,v_dims,v_end_coord.x,v_start_coord.y,(v_start_coord.z + v_end_coord.z) /2, v_start_coord.w); ELSE appendVertex(p_ordinates,v_dims, v_start_coord.x, v_end_coord.y, (v_start_coord.z + v_end_coord.z) /2, (v_end_coord.w - v_start_coord.w) * ((v_end_coord.x - v_start_coord.x) / ((v_end_coord.x - v_start_coord.x) + (v_end_coord.y - v_start_coord.y)) )); END IF; -- 3rd or middle coordinate appendVertex(p_ordinates,v_dims,v_end_coord.x,v_end_coord.y,v_end_coord.z,v_end_coord.w); -- 4th coordinate IF ( v_etype = 1003 ) THEN appendVertex(p_ordinates,v_dims,v_start_coord.x,v_end_coord.y,(v_start_coord.z + v_end_coord.z) /2,v_start_coord.w); ELSE appendVertex(p_ordinates,v_dims,v_end_coord.x,v_start_coord.y,(v_start_coord.z + v_end_coord.z) /2, (v_end_coord.w - v_start_coord.w) * ((v_end_coord.x - v_start_coord.x) / ((v_end_coord.x - v_start_coord.x) + (v_end_coord.y - v_start_coord.y)) )); END IF; -- Last coordinate appendVertex(p_ordinates,v_dims,v_start_coord.x,v_start_coord.y,v_start_coord.z,v_start_coord.w); dbms_output.put_line('........ Finishing Converting Optimized Rectangle.'); END Loop; END processElement; BEGIN IF ( p_geometry IS NULL ) THEN RETURN p_geometry; END IF; IF ( p_geometry.get_gtype() NOT IN (3,7) ) THEN RETURN p_geometry; END IF; IF ( hasOptimizedRectangles(p_geometry) = 0 ) THEN dbms_output.put_line('p_geometry does not have optimized rectangles.'); RETURN p_geometry; END IF; IF ( hasCompoundElements(p_geometry) <> 0 ) THEN dbms_output.put_line('p_geometry has compound elements.'); RETURN p_geometry; END IF; v_ordinates.DELETE; v_elem_info.DELETE; v_dims := p_geometry.get_dims(); v_num_elements := mdsys.sdo_util.getNumElem(p_geometry); dbms_output.put_line('p_geometry has ' || v_num_elements || ' parts/elements.'); FOR elem IN 1..v_num_elements loop v_element := mdsys.sdo_util.EXTRACT(p_geometry,elem,0); dbms_output.put_line('... Extracted part/element ' || elem || ' has ' || hasOptimizedRectangles(v_element) || ' optimized rectangles.'); IF ( hasOptimizedRectangles(v_element) <> 0 ) THEN processElement(v_element,v_ordinates,v_elem_info); ELSE -- Append dbms_output.put_line('........ Appending non-optimized rectangle geometry to output geometry '); v_num_sub_elements := (v_element.sdo_elem_info.COUNT / 3); v_ord_count := v_ordinates.COUNT; FOR i IN 1..v_num_sub_elements loop v_elem_info.EXTEND(3); v_elem_info(v_elem_info.COUNT-2) := CASE WHEN i=1 THEN v_ord_count+1 ELSE v_element.sdo_elem_info(i) + v_ord_count END; v_ord_count := v_elem_info(v_elem_info.COUNT-2); v_elem_info(v_elem_info.COUNT-1) := v_element.sdo_elem_info(i+1); v_elem_info(v_elem_info.COUNT ) := v_element.sdo_elem_info(i+2); END Loop; v_vertices := sdo_util.getVertices(v_element); -- Now copy all ordinates FOR i IN 1..v_vertices.COUNT Loop appendVertex( v_ordinates, v_dims, v_vertices(i).x, v_vertices(i).y, v_vertices(i).z, v_vertices(i).w ); END Loop; END IF; END LOOP; RETURN mdsys.sdo_geometry(p_geometry.sdo_gtype,p_geometry.sdo_srid,NULL,v_elem_info,v_ordinates); END Rectangle2Polygon; / SHOW errors
Now let’s test the function:
-- Test SET serveroutput ON SIZE unlimited WITH tGeoms AS ( SELECT 'Single rectangle no hole' AS description, sdo_geometry(2003,NULL,NULL,sdo_elem_info_array(1,1003,3),sdo_ordinate_array(0,0,1,1)) AS geom FROM dual UNION ALL SELECT 'Inverted single rectangle' AS description, sdo_geometry(2003,NULL,NULL,sdo_elem_info_array(1,1003,3),sdo_ordinate_array(1,1,0,0)) AS geom FROM dual UNION ALL SELECT 'Rectangle with rectangle hole' AS description, SDO_GEOMETRY(2003,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,3, 5,2003,3),SDO_ORDINATE_ARRAY(0,0, 100,100, 40,40, 60,60)) AS geom FROM dual UNION ALL SELECT 'Compound polygon' AS description, SDO_GEOMETRY(2003,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1005,2, 1,2,1, 7,2,2),SDO_ORDINATE_ARRAY(10.0,128.0, 10.0,125.0, 20.0,125.0, 20.0,128.0, 15.0,130.0, 10.0,128.0)) AS geom FROM dual UNION ALL SELECT 'Single Circle element' AS description, SDO_GEOMETRY(2003,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,4),SDO_ORDINATE_ARRAY(15.0,145.0, 10.0,150.0, 20.0,150.0)) AS geom FROM dual UNION ALL SELECT 'No rectangle elements' AS description, SDO_GEOMETRY(2007,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1, 11,2003,1, 31,1003,1),SDO_ORDINATE_ARRAY(50.0,168.0, 50.0,160.0, 55.0,160.0, 55.0,168.0, 50.0,168.0, 51.0,167.0, 54.0,167.0, 54.0,161.0, 51.0,161.0, 51.0,162.0, 52.0,163.0, 51.0,164.0, 51.0,165.0, 51.0,166.0, 51.0,167.0, 52.0,166.0, 52.0,162.0, 53.0,162.0, 53.0,166.0, 52.0,166.0)) AS geom FROM dual UNION ALL SELECT 'Multi-Polygon one rectangle' AS description, SDO_GEOMETRY(2007,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1, 11,1003,3),SDO_ORDINATE_ARRAY(50.0,105.0, 55.0,105.0, 60.0,110.0, 50.0,110.0, 50.0,105.0, 62.0,108.0, 65.0,112.0)) AS geom FROM dual UNION ALL SELECT 'MultiPolygon All Rectangles' AS description, SDO_GEOMETRY(2007,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,3, 5,2003,3, 9,1003,3),SDO_ORDINATE_ARRAY(0,0,100,100, 40,40,60,60, 200,200,300,300)) AS geom FROM dual ) SELECT f.description, f.validOriginalGeom, sdo_geom.validate_geometry(f.poly,0.005) AS vGeom, f.poly FROM (SELECT a.description, sdo_geom.validate_geometry(a.geom,0.005) AS validOriginalGeom, rectangle2polygon(a.geom) AS poly FROM tGeoms a ) f;
Results are as follows:
DESCRIPTION | VALIDORIGINALGEOM | VGEOM | POLY |
---|---|---|---|
Single rectangle no hole | TRUE | TRUE | SDO_GEOMETRY(2003,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1),SDO_ORDINATE_ARRAY(0,0,1,0,1,1,0,1,0,0)) |
Inverted single rectangle | TRUE | TRUE | SDO_GEOMETRY(2003,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1),SDO_ORDINATE_ARRAY(0,0,1,0,1,1,0,1,0,0)) |
Rectangle with rectangle hole | TRUE | TRUE | SDO_GEOMETRY(2003,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1,11,2003,1),SDO_ORDINATE_ARRAY(0,0,100,0,100,100,0,100,0,0,40,40,40,60,60,60,60,40,40,40)) |
Compound polygon | TRUE | TRUE | SDO_GEOMETRY(2003,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1005,2,1,2,1,7,2,2),SDO_ORDINATE_ARRAY(10,128,10,125,20,125,20,128,15,130,10,128)) |
Single Circle element | TRUE | TRUE | SDO_GEOMETRY(2003,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,4),SDO_ORDINATE_ARRAY(15,145,10,150,20,150)) |
No rectangle elements | TRUE | TRUE | SDO_GEOMETRY(2007,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1,11,2003,1,31,1003,1),SDO_ORDINATE_ARRAY(50,168,50,160,55,160,55,168,50,168,51,167,54,167,54,161,51,161,51,162,52,163,51,164,51,165,51,166,51,167,52,166,52,162,53,162,53,166,52,166)) |
Multi-Polygon one rectangle | TRUE | TRUE | SDO_GEOMETRY(2007,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1,11,1003,1),SDO_ORDINATE_ARRAY(50,105,55,105,60,110,50,110,50,105,62,108,65,108,65,112,62,112,62,108)) |
MultiPolygon All Rectangles | TRUE | TRUE | SDO_GEOMETRY(2007,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1,11,2003,1,21,1003,1),SDO_ORDINATE_ARRAY(0,0,100,0,100,100,0,100,0,0,40,40,40,60,60,60,60,40,40,40,200,200,300,200,300,300,200,300,200,200)) |
I hope this is of use to someone.
Documentation
- MySQL Spatial General Functions
- Oracle LRS Objects
- Oracle Spatial Exporter (Java + pl/SQL)
- Oracle Spatial Object Functions
- Oracle Spatial Object Functions (Multi Page)
- PostGIS pl/pgSQL Functions
- SC4O Oracle Java Topology Suite (Java + pl/SQL)
- SQL Server Spatial General TSQL Functions
- SQL Server Spatial LRS TSQL Functions