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)
Reducing 5 Vertex Polygon to Optimized Rectangle
I had occasion to convert a set of 5 vertex polygons that defined grid squares to their optimized rectangle equivalent.
To do this I created a simple function as follows.
create or replace function Polygon2Rectangle( p_geometry in mdsys.sdo_geometry ) return mdsys.sdo_geometry deterministic as v_vertices mdsys.vertex_set_type; v_id pls_integer; v_ords mdsys.sdo_ordinate_array := new mdsys.sdo_ordinate_array(null); begin v_ords.DELETE; v_vertices := mdsys.sdo_util.getVertices(p_geometry); if ( v_vertices.COUNT <> 5 ) Then return p_geometry; End If; <<for_all_vectors>> FOR v_id in v_vertices.FIRST..(v_vertices.LAST-1) LOOP if ( v_id in (1,3) ) then v_ords.EXTEND(2); v_ords(v_ords.COUNT-1) := v_vertices(v_id).x; v_ords(v_ords.COUNT) := v_vertices(v_id).y; end if; END LOOP for_all_vectors; return mdsys.sdo_geometry(p_geometry.sdo_gtype, p_geometry.sdo_srid, p_geometry.sdo_point, mdsys.sdo_elem_info_array(1,1003,3), v_ords); end Polygon2Rectangle;
Which can then used as follows:
select polygon2rectangle(sdo_geometry(2003,null,null, sdo_elem_info_array(1,1003,1), sdo_ordinate_array(1,1,10,1,10,10,1,10,1,1))) as optimised_rectangle from dual; OPTIMISED_RECTANGLE ----------------------------- MDSYS.SDO_GEOMETRY(2003,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,3),MDSYS.SDO_ORDINATE_ARRAY(1,1,10,10))
Update
On the basis of the excellent comments made by Farid Cheraghi, I have included his rectangle check (which could also have been done as a check of the diagonals of the rectangle), I have updated the function adding support for more complex sdo_geometry objects.
Note: The Oracle sdo_util.extract function, when it extracts inner rings (2003) renames them to 1003. Also, a complex element’s sdo_elem_info needs to be processed more carefully than is evident in the Oracle pl/sql functions.
create or replace Function Polygon2Rectangle( p_geometry in mdsys.sdo_geometry, p_tolerance in number default 0.005 ) Return mdsys.sdo_geometry DETERMINISTIC AS v_vertices mdsys.vertex_set_type; v_ords mdsys.sdo_ordinate_array := new mdsys.sdo_ordinate_array(null); v_num_elems pls_integer; v_actual_etype pls_integer; v_ring_elem_count pls_integer := 0; v_ring mdsys.sdo_geometry; v_num_rings pls_integer; v_out_geom mdsys.sdo_geometry; Function GetETypeAt( p_geometry in mdsys.sdo_geometry, p_element in pls_integer) Return pls_integer Is v_num_elems number; Begin If ( p_geometry is not null ) Then v_num_elems := ( ( p_geometry.sdo_elem_info.COUNT / 3 ) - 1 ); <<element_extraction>> for v_i IN 0 .. v_num_elems LOOP if ( (v_i+1) = p_element ) then RETURN p_geometry.sdo_elem_info(v_i * 3 + 2); End If; end loop element_extraction; End If; Return NULL; End GetETypeAt; Function GetNumRings( p_geometry in mdsys.sdo_geometry, p_ring_type in integer /* 0 = ALL; 1 = OUTER; 2 = INNER */ ) Return Number Is v_elements pls_integer := 0; v_ring_count pls_integer := 0; v_etype pls_integer; v_ring_type pls_integer := case when ( p_ring_type is null OR p_ring_type not in (0,1,2) ) Then 0 Else p_ring_type End; Begin If ( p_geometry is not null ) Then 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 (1003,1005,2003,2005) and 0 = v_ring_type ) OR ( v_etype in (1003,1005) and 1 = v_ring_type ) OR ( v_etype in (2003,2005) and 2 = v_ring_type ) ) Then v_ring_count := v_ring_count + 1; End If; END LOOP element_extraction; End If; Return v_ring_count; End GetNumRings; BEGIN IF ( p_geometry is null ) THEN RETURN p_geometry; END IF; -- Is polygon? IF ( p_geometry.get_gtype() not in (3,7) ) THEN RETURN p_geometry; END IF; v_num_elems := MDSYS.SDO_UTIL.GETNUMELEM(p_geometry); <<all_elements>> FOR v_elem_no IN 1..v_num_elems LOOP -- Need to process and check all inner rings -- -- Process all rings in the extracted single - 2003 - polygon v_num_rings := GetNumRings(MDSYS.SDO_UTIL.EXTRACT(p_geometry,v_elem_no),0); <<All_Rings>> FOR v_ring_no IN 1..v_num_rings LOOP v_ring := MDSYS.SDO_UTIL.EXTRACT(p_geometry,v_elem_no,v_ring_no); v_actual_etype := GetEtypeAt(p_geometry,(v_ring_elem_count+1)); v_ring_elem_count := v_ring_elem_count + v_ring.sdo_elem_info.COUNT / 3; IF ( v_ring is not null ) THEN IF ( v_ring.sdo_elem_info(2) = 1003 AND v_ring.sdo_elem_info(2) <> v_actual_etype ) THEN -- Replace etype as Oracle extracts 2003 as 1003 v_ring.sdo_elem_info(2) := v_actual_etype; End If; v_vertices := mdsys.sdo_util.getVertices(v_ring); IF ( v_vertices.COUNT = 5 ) THEN -- Do the five vertices form a rectangle? -- inner product of two consequent vector must equals to zero IF ( (v_vertices(1).x-v_vertices(2).x) * (v_vertices(2).x-v_vertices(3).x) + (v_vertices(1).y-v_vertices(2).y) * (v_vertices(2).y-v_vertices(3).y) = 0 AND (v_vertices(3).x-v_vertices(4).x) * (v_vertices(4).x-v_vertices(5).x) + (v_vertices(3).y-v_vertices(4).y) * (v_vertices(4).y-v_vertices(5).y) = 0 ) THEN v_ring.sdo_elem_info(1) := 1; v_ring.sdo_elem_info(3) := 3; v_ords.DELETE; v_ords.EXTEND(4); v_ords(1) := v_vertices(1).x; v_ords(2) := v_vertices(1).y; v_ords(3) := v_vertices(3).x; v_ords(4) := v_vertices(3).y; v_ring := mdsys.sdo_geometry(v_ring.sdo_gtype, v_ring.sdo_srid, v_ring.sdo_point, v_ring.sdo_elem_info, v_ords); END IF; END IF; IF ( v_out_geom is null ) THEN v_out_geom := v_ring; ELSE v_out_geom := mdsys.sdo_util.APPEND(v_out_geom,v_ring); END IF; END IF; END LOOP All_Rings; END LOOP all_elements; RETURN mdsys.sdo_geometry(p_geometry.sdo_gtype, p_geometry.sdo_srid, p_geometry.sdo_point, v_out_geom.sdo_elem_info, v_out_geom.sdo_ordinates); END Polygon2Rectangle;
Testing…..
select sdo_geom.validate_geometry(a.geometry,0.005) as valid, polygon2rectangle(a.geometry) as optimised from (select sdo_geometry(2003,null,null, sdo_elem_info_array(1,1003,1), sdo_ordinate_array(1,1,10,1,10,10,1,10,1,1)) as geometry from dual union all select sdo_geometry(2007,null,null,sdo_elem_info_array(1,1003,1,11,1003,1,21,2003,3), sdo_ordinate_array(1,1,10,1,10,10,1,10,1,1, 15,15,25,15,25,25,15,25,15,15, 18,18,20,20)) as geometry from dual union all select sdo_geometry(2007,null,null,sdo_elem_info_array(1,1003,1,11,1003,1,21,2003,1), sdo_ordinate_array(1,1,10,1,10,10,1,10,1,1, 15,15,25,15,25,25,15,25,15,15, 16,16,16,24,24,24,24,16,16,16)) as geometry from dual union all select sdo_geometry(2003,null,null,sdo_elem_info_array(1,1005,2, 1,2,1, 5,2,2, 11, 2003, 1), sdo_ordinate_array(6,10, 10,1, 14,10, 10,14, 6,10, 8,8,8,12,12,12,12,8,8,8)) as geometry from dual union all select sdo_geometry(2003,null,null,sdo_elem_info_array(1,1003,2,11,2003,1), sdo_ordinate_array(15.0, 115.0, 20.0, 118.0, 15.0, 120.0, 10.0, 118.0, 15.0, 115.0, 13.6,116.7,13.6,118.3,16.3,118.3,16.3,116.7,13.6,116.7)) as geometry from dual ) a; Valid Optimised ----- --------- TRUE MDSYS.SDO_GEOMETRY(2003,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,3),MDSYS.SDO_ORDINATE_ARRAY(1,1,10,10)) TRUE MDSYS.SDO_GEOMETRY(2007,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,3,5,1003,3,9,2003,3),MDSYS.SDO_ORDINATE_ARRAY(1,1,10,10,15,15,25,25,18,18,20,20)) TRUE MDSYS.SDO_GEOMETRY(2007,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,3,5,1003,3,9,2003,3),MDSYS.SDO_ORDINATE_ARRAY(1,1,10,10,15,15,25,25,16,16,24,24)) TRUE MDSYS.SDO_GEOMETRY(2003,NULL,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1005,2,1,2,1,5,2,2,11,2003,3),MDSYS.SDO_ORDINATE_ARRAY(6,10,10,1,14,10,10,14,6,10,8,8,12,12)) TRUE MDSYS.SDO_GEOMETRY(2003,null,null,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,2,11,2003,3),MDSYS.SDO_ORDINATE_ARRAY(15,115,20,118,15,120,10,118,15,115,13.6,116.7,16.3,118.3))
I hope this function helps 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