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
- GeoRaptor 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