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