Skip to Main Content

Database Software

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

Idea: Add a SDO_ORDINATES member function (for extracting collection elements by index)

User_1871Jun 19 2022 — edited Jul 1 2022

Oracle 18c:
When we try to extract SDO_ORDINATES elements by index...such as ordinate (1):

select sdo_geometry('linestring(1 2, 3 4, 5 6)').sdo_ordinates(1)
from   dual

Error:
ORA-00904: "MDSYS"."SDO_GEOMETRY"."SDO_ORDINATES": invalid identifier

...the SQL engine processes the syntax as a call to a member function in the form of object_type.member_function(argument) .
There is no SDO_ORDINATES member function of the SDO_GEOMETRY data type, so the result is an error:

ORA-00904: "MDSYS"."SDO_GEOMETRY"."SDO_ORDINATES": invalid identifier

Could an SDO_ORDINATES member function be added to SDO_GEOMETRY to make it easier to extract SDO_ORDINATES elements by index?

Source: https://stackoverflow.com/a/72672702/5576771

Comments

David Lapp-Oracle

The member functions return scalars. Sdo_ordinates is a varray so it needs table function. However you can accomplish what you want like this:

select json_value(x.geom.Get_GeoJson(),'$.coordinates[1][1]') ordinate4
from (
  select
  SDO_GEOMETRY(2002, 4326, NULL,
   SDO_ELEM_INFO_ARRAY(1, 2, 1),
   SDO_ORDINATE_ARRAY(-93.0275, 42.0829, -92.8717, 42.0452, -92.0, 42.0)) as geom
  from dual) x;

ORDINATE4
--------
42.0452

so in general

'$.coordinates[<index-of-coordinate>][<index-of-ordinate-in-coordinate>]') 

with index starting with 0.

User_1871

Thanks! That's interesting.

Side note:
That technique doesn't seem to work for LRS geometries:

--get the X coordinate of the second vertex
with data as (select sdo_lrs.convert_to_lrs_geom(sdo_geometry('linestring(10 20, 30 40, 50 60)')) as shape from dual)

select 
    json_value((shape).Get_GeoJson(),'$.coordinates[1][0]' returning number) as x
from 
    data

Error:

ORA-13199: LRS is not supported
ORA-06512: at "MDSYS.SDO_UTIL", line 6410
ORA-06512: at "MDSYS.SDO_UTIL", line 6431
ORA-06512: at "MDSYS.SDO_GEOMETRY", line 156
13199. 00000 -  "%s"
*Cause:    This is an internal error.
*Action:   Contact Oracle Support Services.

But I think we already knew that. The GeoJSON standard doesn't support LRS lines (not Oracle Spatial's fault): SDO_UTIL.TO_GEOJSON() - Support LRS geometries

User_1871

[David Lapp-Oracle](/ords/forums/user/David Lapp-Oracle)
If member functions can only return scalars, do you know why this query works?

select
 (shape).sdo_ordinates as ordinates 
from
 (select sdo_geometry('linestring(1 2, 3 4, 5 6)') as shape from dual)

image.pngIn that query, is .sdo_ordinates a member function? And is it returning a varray (a kind of table collection)? If so, why wouldn't it be possible to create a member function for extracting collection elements by index, instead of returning the entire collection?
I've likely misunderstood something there. Just trying to learn.
Thanks.

David Lapp-Oracle

Member functions are object type methods . They are functions and end with ( )

select a.geom.Get_Gtype() from mydata a;

Object attribute queries return attributes of an object as-is. They are not functions so have no ( )

select a.geom.sdo_gtype from mydata a;

In the case of an object attribute query for sdo_ordinates, the result is varray.

User_1871

[David Lapp-Oracle](/ords/forums/user/David Lapp-Oracle)
What do you think about this test?

CREATE TYPE my_sdo_geom_type AS OBJECT( 
 shape SDO_GEOMETRY, 

 MEMBER FUNCTION GetOrdinates(
  self IN my_sdo_geom_type,
  idx IN NUMBER
 ) RETURN NUMBER
) 
/ 
CREATE TYPE BODY my_sdo_geom_type AS
  MEMBER FUNCTION GetOrdinates(
    self IN my_sdo_geom_type,
    idx  IN NUMBER
  ) RETURN NUMBER
  IS
  BEGIN 
    return shape.sdo_ordinates(idx);
  END;
END;
/ 
create table lines (my_sdo_geom_col my_sdo_geom_type);
insert into lines (my_sdo_geom_col) 
  values (my_sdo_geom_type(sdo_geometry('linestring(10 20, 30 40, 50 60)')));

select (my_sdo_geom_col).GetOrdinates(1)
from   lines

Result:
10

That seems to work as expected. The custom GetOrdinates() member function returns a specific ordinate.
Would it be possible for Oracle to implement something like that in the sdo_geometry object? That might make it easier to extract ordinates in some cases.

David Lapp-Oracle

This is a good idea and I will create an enhancement request.

User_1871

[David Lapp-Oracle](/ords/forums/user/David Lapp-Oracle)
Could we make it so the member function would accept negative numbers? For example, a -1 would return the last ordinate. And a -2 would return the second last ordinate, etc..
Like this:

CREATE TYPE BODY my_sdo_geom_type AS
  MEMBER FUNCTION GetOrdinates(
    self IN my_sdo_geom_type,
    idx  IN NUMBER
  ) RETURN NUMBER
  IS
  BEGIN 
  IF idx BETWEEN 1 AND shape.sdo_ordinates.COUNT THEN
    return shape.sdo_ordinates(idx);
  ELSIF -idx BETWEEN 1 AND shape.sdo_ordinates.COUNT THEN
    RETURN shape.sdo_ordinates(shape.sdo_ordinates.COUNT + 1 + idx);
  ELSE
    RETURN NULL;
  END IF;
  END;
END;
/ 

Source: Use negative number to extract element at end of varray list (in a custom member function)
That's similar to how sdo_util.get_coordinate() works, as demonstrated here: https://gis.stackexchange.com/a/425057/62572.

User_1871

I'm looking forward to eventually using that member function. I actually think it might result in a performance improvement in some cases.
For example, when it comes to getting the startpoint X and Y of SDO_GEOMETRY lines, this is the fastest technique I've found:

with 
function 
sdo_startpoint_x(shape sdo_geometry) return number is
begin
  return shape.sdo_ordinates(1);
end;   

function sdo_startpoint_y(shape sdo_geometry) return number is
begin
  return shape.sdo_ordinates(2);
end;

select
  sdo_startpoint_x(shape) as startpoint_x,
  sdo_startpoint_y(shape) as startpoint_y
from
  bc_atn_sdo_geom

I imagine the GetOrdinate member function will be equally as fast.

User_1871

@david-lapp-oracle
I wonder if a member function should be created for SDO_ELEM_INFO too, so that we can easily access multi-part geometry information. I.e., determine where the ordinates for part 1 stop and part 2's ordinates start, etc.
Related info in this answer: How is multi-part SDO_GEOMETRY information stored?

1 - 9

Post Details

Added on Jun 19 2022
9 comments
474 views