First I tried:
public static boolean isInPolygon(java.awt.geom.Point2D point) {
// ...
// ... build prepared statement
ResultSet rs = statement.executeQuery();
STRUCT geoStruct = (STRUCT) rs.getObject("SDO_GEOMETRY_COLUMN");
JGeometry geo = JGeometry.load(geoStruct);
Shape shape = geo.createShape();
return shape.contains(point);
}
This did not work, as JGeometry.createShape()
returns a java.awt.geom.GeneralPath
object, which does not have double precision, therefore shape.contains(...)
returned wrong results around the edges of the polygon.
Then I tried using JGeometry.createDoubleShape()
which returns a oracle.spatial.util.SDODoubleGeneralPath
object instead:
// ...
JGeometry geo = JGeometry.load(geoStruct);
Shape shape = geo.createDoubleShape();
return shape.contains(point);
When the contains
method is called, I get the following error:
Exception in thread "main" java.lang.NoSuchMethodError: sun.awt.geom.Curve.crossingsForPath(Ljava/awt/geom/PathIterator;DD)I
at oracle.spatial.util.SDODoubleGeneralPath.contains(SDODoubleGeneralPath.java:498)
at igea_html.test.Test.handleGeo(Test.java:258)
at igea_html.test.Test.jgeometryTest(Test.java:204)
at igea_html.test.Main.main(Main.java:74)
Apparently SDODoubleGeneralPath
has some implementation issues if its contains
method throws such an error.
Is there an alternative?
I would like to avoid implementing myself an algorithm for determining if a point is inside a polygon, I'd rather trust something already implemented from a reputable source. I also cannot use the Oracle Spatial functions directly in the database due to a license restriction.
Obs.: My isInPolygon
method uses Point2D as an argument, but it is ok if I have to use a point in the form of a JGeometry
object in case the solution requires it.
Further details as requested:
Oracle version is 11g and I'm working on a web app that is supposed to validate geometrical data in the database. In particular, one of the validations involve checking if certain points and polygons are inside other polygons. The documentation I'm referencing is this: https://docs.oracle.com/cd/E18283_01/appdev.112/e11829/toc.htm
The architecture of those features is still a work in progress so the decision if this should be made by the web app itself or inside the database or some other method is still open. The second option was thrown aside due to license restrictions, the required functions, i.e. SDO_RELATE
, are exclusive to Oracle Spatial in Oracle 11 apparently in contrast to Oracle 12 where they are available with just Oracle Locator.
EDIT: This is incorrect, as indicated by @Albert Godfrind below. SDO_RELATE
and other spatial operators are supported by Oracle Locator 11 as indicated in Oracle's documentation, both in release 1 and 2:
https://docs.oracle.com/cd/B28359_01/appdev.111/b28400/sdo_locator.htm#CFACCEEG
https://docs.oracle.com/cd/E11882_01/appdev.112/e11830/sdo_locator.htm#CFACCEEG
Also, the JGeometry
class of the Java API for Oracle 11 does not contain methods such as isInside()
or anyInteract()
like it does on its Oracle 12 counterpart. Oracle 12 Java API docs: https://docs.oracle.com/en/database/oracle/oracle-database/12.2/spajv/index.html?oracle/spatial/geometry/JGeometry.html
You say
I also cannot use the Oracle Spatial functions directly in the database due to a license restriction.
That is not correct. All vector processing functions for 2D come for free with your database licenses. It is called Oracle Locator. See appendix B of the users guide for the fine print details of what Locator covers (https://docs.oracle.com/en/database/oracle/oracle-database/18/spatl/oracle-locator.html#GUID-EC6DEA23-8FD7-4109-A0C1-93C0CE3D6FF2). Note that this is for 12.2. Older versions were more restrictive, but all come with the basic ability to do the geometry filtering you need.
The Oracle Spatial & Graph license complements the basic functions with support for things like 3D, rasters, networks, geocoding, visualization as well as semantic and property graphs.
Other than that, you can freely use the JAVA API (it comes with Oracle Locator). Note however that it exclusively works on JGeometry
objects. In particular method isInside()
will tell you whether one JGeometry object (your point) is fully contained inside another JGeometry object (your polygon). If you want points falling on the boundary of the polygon to also be returned, then use the anyInteract()
method instead.
However if your main processing is in the database, then just use plain SQL with the SDO_INSIDE
(or SDO_ANYINTERACT
predicate to find all points that fall inside a polygon.
Can you say more about your actual use case ?
EDIT
Here is an example of using the SQL API. This is valid for all database releases, all the way back to 11gR1 and is covered by Oracle Locator (i.e. no need for any Spatial license).
In this example, I am using the following two tables:
A table containing the location of major US cities:
Name Null? Type
---------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
CITY VARCHAR2(42 CHAR)
POP90 NUMBER
RANK90 NUMBER
LOCATION MDSYS.SDO_GEOMETRY
A table containing the shape of US states:
Name Null? Type
---------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
STATE VARCHAR2(26 CHAR)
STATE_ABRV VARCHAR2(2 CHAR)
FIPSST VARCHAR2(2 CHAR)
LANDSQMI NUMBER
POPPSQMI NUMBER
GEOM MDSYS.SDO_GEOMETRY
The following query returns all cities in New York state:
select c.city, c.pop90
from us_cities c, us_states s
where s.state = 'New York'
and sdo_inside (c.location, s.geom) = 'TRUE';
which returns:
CITY POP90
------------------------------ ----------
New York 7322564
Buffalo 328123
Rochester 231636
Yonkers 188082
Syracuse 163860
Albany 101082
6 rows selected.
All you need is to submit this statement via JDBC and fetch the results. If you want to also return the location of the points (to highlight them on a map) then also include the location
column. This you can interpret in your Java code via the Spatial Java API. Or you could also extract the coordinates from the points:
select c.city, c.pop90, c.location.sdo_point.x longitude, c.location.sdo_point.y latitude
from us_cities c, us_states s
where s.state = 'New York'
and sdo_inside (c.location, s.geom) = 'TRUE';
CITY POP90 LONGITUDE LATITUDE
------------------------------ ---------- ---------- ----------
New York 7322564 -73.943849 40.6698
Buffalo 328123 -78.859684 42.8898
Rochester 231636 -77.615838 43.168651
Yonkers 188082 -73.867514 40.947033
Syracuse 163860 -76.144067 43.041059
Albany 101082 -73.799017 42.66575
6 rows selected.