0

I have the following 4 geometries in an Oracle Spatial table:

A) POLYGON ((-100 40, -100 44, -96 44, -96 40, -100 40))
B) LINESTRING (-99 37, -99 40)
C) POINT (-100 41)
D) POLYGON ((-96 41.5, -95 42, -95 41, -96 41.5))

So essentially a line B), a point C) and another polygon D) that all touch a rectangular polygon A). I'm using an SRID of 4326, if that makes a difference. Usage of either:

SDO_RELATE(GEOMETRY_COLUMN, <LITERAL REPRESENTING A)>, 'mask=TOUCH')
or
SDO_RELATE(GEOMETRY_COLUMN, <LITERAL REPRESENTING A)>, 'mask=DISJOINT')

Will return the correct results for C) and D) but not B). In short, it reports that the linestring B) does not touch A) and that B) is DISJOINT from A).

I've tried this with other spatial databases using ST_DISJOINT and ST_TOUCHES, and I get the correct results. Why does Oracle think B) does not touch A)?

1
  • Does it touch with a noded polygon POLYGON (( -100 40, -100 44, -96 44, -96 40, -99 40, -100 40 ))?
    – user30184
    Commented Jun 28, 2023 at 4:47

1 Answer 1

1

Let's try this by loading the shapes into a table:

drop table t1 purge;
create table t1 (id char, geom sdo_geometry);
insert into t1 values('A', sdo_geometry('POLYGON ((-100 40, -100 44, -96 44, -96 40, -100 40))',4326));
insert into t1 values('B', sdo_geometry('LINESTRING (-99 37, -99 40)',4326));
insert into t1 values('C', sdo_geometry('POINT (-100 41)',4326));
insert into t1 values('D', sdo_geometry('POLYGON ((-96 41.5, -95 42, -95 41, -96 41.5))',4326));
commit;

The data looks like this:

SQL> select * from t1;

ID   GEOM(SDO_GTYPE, SDO_SRID, SDO_POINT(X, Y, Z), SDO_ELEM_INFO, SDO_ORDINATES)
---- -----------------------------------------------------------------------------------------------------------------------------------
A    SDO_GEOMETRY(2003, 4326, NULL, SDO_ELEM_INFO_ARRAY(1, 1003, 1), SDO_ORDINATE_ARRAY(-100, 40, -100, 44, -96, 44, -96, 40, -100, 40))
B    SDO_GEOMETRY(2002, 4326, NULL, SDO_ELEM_INFO_ARRAY(1, 2, 1), SDO_ORDINATE_ARRAY(-99, 37, -99, 40))
C    SDO_GEOMETRY(2001, 4326, SDO_POINT_TYPE(-100, 41, NULL), NULL, NULL)
D    SDO_GEOMETRY(2003, 4326, NULL, SDO_ELEM_INFO_ARRAY(1, 1003, 1), SDO_ORDINATE_ARRAY(-96, 41.5, -95, 42, -95, 41, -96, 41.5))

4 rows selected.

Then let's do the query to run the spatial comparisons:

select a.id, b.id, sdo_relate(a.geom,b.geom,'mask=TOUCH') as touch, sdo_relate(a.geom,b.geom,'mask=DISJOINT') as disjoint
from t1 a, t1 b
where a.id in ('B','C','D')
and b.id in ('A')
order by a.id, b.id

and check the results:

ID   ID   TOUCH      DISJOINT
---- ---- ---------- ----------
B    A    FALSE      TRUE
C    A    TRUE       FALSE
D    A    TRUE       FALSE

3 rows selected.

Those are the same you have. Line B does not touch rectangle A. Why is that ? Let's see how those shapes look on a map:

enter image description here

It does look like line B does touch rectangle A. But does it really ? Here is where Oracle differs from the other spatial databases (one of them being PostGIS I imagine). And why does it ?

Because in Oracle's geometry system, line segments between geodetic points represent the shortest distance between those points on the WGS84 ellipsoid, i.e they are geodesics - they follow great circles.

That, and Oracle does all geodetic calculations directly on the ellipsoidal representation of the earth.

So the "top" and "bottom" lines of the mauve rectangle do not follow the 40th and 44th parallels as the graphical representation would make us believe. That is because the map is a global mercator projection, and the mapping tool just draws straight line on that projection.

But in reality they follow great circles, i.e. on the projection, they are slightly curved northward.

To observe this, let's create a copy of rectangle A, but densified so that we have one vertex every 10km:

insert into t1
select 'E',sdo_util.DENSIFY_GEOMETRY(geom,10000)
from t1
where id = 'A';
commit;

and add to the map: enter image description here It's hard to see, but the "horizontal" lines are now slightly curved. Let's zoom in to get a better view:

enter image description here enter image description here

This shows that there is a significant distance between the line and the southern border of your rectangle: 1.4km.

enter image description here

And that explains why "Oracle thinks B does not touch A": simply because B does not touch A. It only does if your spatial database first projects geodetic shapes on a plane, such as a mercator projection. But when the calculations are done directly on the ellipsoidal representation of the globe, as Oracle does, then the reality appears: the line does not touch the rectangle.

EDIT Two more important points to note:

First, the SDO_RELATE() function is really not designed to be used in a SELECT list: its primary intent is to be used as a predicate in a where clause. For example to find all shapes that touch a selected shape you would write something like:

SELECT ...
FROM land_parcels lp, pipes p
WHERE sdo_relate (lp.geom.p.geom, 'mask=TOUCH') = 'TRUE'
AND p.pipe_id = ...

or better, use the simplified syntax:

WHERE sdo_touch (lp.geom.p.geom) = 'TRUE'

In the select list, you can use the sdo_geom.relate() function, like this:

select a.id, b.id, sdo_geom.relate(a.geom,'TOUCH',b.geom) as touch, sdo_geom.relate(a.geom,'DISJOINT',b.geom) as disjoint
from t1 a, t1 b
where a.id in ('B','C','D')
and b.id in ('A')
order by a.id, b.id;

The result is the same as before, but the function allows you to specify the tolerance at which the computation is made. When no tolerance is specified, Oracle uses a default of 0.5m. For the TOUCH relationship, it means that Oracle will say that your line touches the polygon as long as the end point is less than 50cm from the polygon border, or up to 50cm inside the polygon.

For example, this uses a tolerance of 1500m:

select a.id, b.id, sdo_geom.relate(a.geom,'TOUCH',b.geom,1500) as touch
from t1 a, t1 b
where a.id in ('B','C','D')
and b.id in ('A')
order by a.id, b.id;

The result:

ID   ID   TOUCH
---- ---- ----------
B    A    TOUCH
C    A    TOUCH
D    A    TOUCH

3 rows selected.

Now line B touches polygon A. That is because the end point of B is some 1400m from the southern border of A, and therefore is within tolerance.

The tolerance used by the SDO_RELATE() operator and its variants (SDO_INSIDE(), SDO_TOUCH() etc) is specified in the metadata for that table (USER_SDO_GEOM_METADATA). For functions, you can pass it as a parameter.

Typical tolerance values are much smaller: 1m, 50cm, 5cm ... depending on the quality and accuracy of the data. The SDO_TOUCH predicate is typically used to find things like land parcels adjacent to another parcel. The tolerance then reflects that the actual parcel boundaries have been measured at different times with different instruments, and the border between two adjacent parcels is not always the exact same line: it compensates for inaccuracies.

For example, two parcels may be adjacent (they TOUCH) at tolerance 0.05 (5cm), but not at tolerance 0.5 (50cm). This would then reflect that there actually exists a narrow path 50cm wide between the parcels. Or that the two parcels actually overlap slightly (up to 50cm). In other words it is very important to understand the accuracy and quality of the data you work with, and to use the proper tolerance especially for spatial relationships like TOUCH, COVERS, INSIDE ...

2
  • What a fantastic explanation, thank you. It has made me rethink some of the logic I was using and I'm super grateful for it. Many many thanks. Commented Jun 28, 2023 at 21:20
  • You're welcome. I've added some comments about tolerance, something very important to consider. Commented Jun 29, 2023 at 10:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.