Требуется немного работы, но это выполнимо.
Поскольку Oracle (11.2) не смог предоставить, единственная опция, которую мы имеем, - это разделить линию на сегменты и использовать RELATE
на парах сегментов.
Ниже приведена моя собственная реализация (используется в производственном коде более 3 лет, для миллионов геометрий). Я выбрал конвейерный подход для покрытия слишком больших или сложных геометрий.
Предварительное условие 1, тип базы данных:
CREATE OR REPLACE TYPE ElemGeom as object
(eid integer, egeom mdsys.sdo_geometry, egtype integer, eelemnum integer, evertnum integer, earea number, elength number);
CREATE OR REPLACE TYPE ElemGeomTbl as table of ElemGeom;
Предварительное условие 2, функция разделения:
create or replace FUNCTION LineSegments (igeom in mdsys.sdo_geometry)
RETURN ElemGeomTbl pipelined
is
seg ElemGeom := ElemGeom(null,null,null,null,null,null,null);
cursor c is select T.id, T.X ,T.Y from table(SDO_UTIL.GETVERTICES(iGEOM)) T order by 1;
type ctbl is table of c%rowtype;
carr ctbl;
seg_geom mdsys.sdo_geometry;
cnt integer:=0;
segid integer; x1 number; y1 number; x2 number; y2 number;
begin
--if igeom.sdo_gtype not in (2002,2006)
--then... if you need to catch non-linears here...
--end if;
open c;
loop
fetch c
bulk collect into carr ;
for i in carr.first .. carr.last -1
loop cnt:=cnt+1;
segid := cnt;
x1 := carr(i).X; y1 := carr(i).Y;
x2 := carr(i+1).X; y2 := carr(i+1).Y;
seg_geom:= (mdsys.sdo_geometry(2002,2100,null
,mdsys.sdo_elem_info_array(1,2,1)
,mdsys.sdo_ordinate_array(x1,y1, x2,y2)));
seg.eid:=segid;
seg.egeom:=seg_geom;
seg.egtype:=seg_geom.sdo_gtype;
pipe row(seg);
end loop;
exit when c%notfound;
end loop;
close c;
end LineSegments;
Вы можете проверить его вывод с помощью чего-то вроде (мои GEOM - SRID 2100):
with t1 as (
select
SDO_GEOMETRY(2002,2100,NULL,
SDO_ELEM_INFO_ARRAY(1,2,1),
SDO_ORDINATE_ARRAY(290161.697,4206385.413, 290161.901,4206388.095, 290162.684,4206385.188, 290163.188,4206388.041,
290163.51,4206385.22, 290164.357,4206388.159, 290166.879,4206387.108, 290161.397,4206387.366,
290166.331,4206386.067, 290165.763,4206388.052))
as G from DUAL
)
select * from t1,table(LineSegments(g));
И основная функция:
create or replace FUNCTION validate_Line
(igeom in mdsys.sdo_geometry, itol in number default null)
RETURN varchar2
is
vtol number:= nvl(itol, 1/power(10,6));
verd1 varchar2(256); verd2 varchar2(256); v varchar2(256);
begin
verd1:= sdo_geom.validate_geometry_with_context(igeom,vtol);
for r1 in ( select a.eid seg1, a.egeom geom1, b.eid seg2, b.egeom geom2
from table(LineSegments(igeom)) a, table(LineSegments(igeom)) b
where a.eid < b.eid
order by a.eid, b.eid )
loop
--I hate outputting long words, so:
v:= replace(replace(sdo_geom.relate(r1.geom1,'determine',r1.geom2, vtol)
,'OVERLAPBDYDISJOINT','OVR-BDIS'),'OVERLAPBDYINTERSECT','OVR-BINT');
if instr('EQUAL,TOUCH,DISJOINT',v) = 0 then
verd2:= verd2|| case when verd2 is not null
then ', '||r1.seg1||'-'||r1.seg2||'='||v
else r1.seg1||'-'||r1.seg2||'='||v end;
end if;
end loop;
verd1:= nvl(verd1,'NULL')
|| case when verd1 ='TRUE' and verd2 is null then null
when verd1 ='TRUE' and verd2 is not null then ' *+: '||verd2
end;
return verd1;
end validate_Line;
И ее тест:
with t1 as (
select
SDO_GEOMETRY(2002,2100,NULL,
SDO_ELEM_INFO_ARRAY(1,2,1),
SDO_ORDINATE_ARRAY(290161.697,4206385.413, 290161.901,4206388.095, 290162.684,4206385.188, 290163.188,4206388.041,
290163.51,4206385.22, 290164.357,4206388.159, 290166.879,4206387.108, 290161.397,4206387.366,
290166.331,4206386.067, 290165.763,4206388.052))
as G from DUAL
)
select t1.*,validate_Line(g) from t1;
Возвращает:
*TRUE *+: 1-7=OVR-BDIS, 1-8=OVR-BDIS, 2-7=OVR-BDIS, 2-8=OVR-BDIS, 3-7=OVR-BDIS, 3-8=OVR-BDIS, 4-7=OVR-BDIS, 4-8=OVR-BDIS, 5-7=OVR-BDIS, 5-8=OVR-BDIS, 6-9=OVR-BDIS, 7-9=OVR-BDIS*
Конечно, вы можете изменить вывод, чтобы он был просто флагом или чем-то еще - это как раз то, что соответствовало моим собственным потребностям.
HTH