如何使用 Oracle Spatial Java API 确定一个点是否在多边形内?
How to determine if a point is inside a polygon using Oracle Spatial Java API?
首先我尝试了:
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);
}
这没有用,因为 JGeometry.createShape()
returns 一个 java.awt.geom.GeneralPath
对象,它没有双精度,因此 shape.contains(...)
在多边形。
然后我尝试使用 JGeometry.createDoubleShape()
代替 returns 一个 oracle.spatial.util.SDODoubleGeneralPath
对象:
// ...
JGeometry geo = JGeometry.load(geoStruct);
Shape shape = geo.createDoubleShape();
return shape.contains(point);
调用contains
方法时,出现以下错误:
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)
如果 contains
方法抛出此类错误,显然 SDODoubleGeneralPath
存在一些实施问题。
有其他选择吗?
我不想自己实现一个算法来确定一个点是否在多边形内,我宁愿相信已经从信誉良好的来源实现的东西。
由于许可限制,我也不能直接在数据库中使用 Oracle Spatial 函数。
Obs.: 我的 isInPolygon
方法使用 Point2D 作为参数,但如果我必须使用 JGeometry
对象形式的点,以防解决方案需要它,那也没关系。
根据要求提供更多详细信息:
Oracle 版本是 11g,我正在开发一个应该验证数据库中的几何数据的 Web 应用程序。特别是,其中一项验证涉及检查某些点和多边形是否在其他多边形内部。我引用的文档是这样的:https://docs.oracle.com/cd/E18283_01/appdev.112/e11829/toc.htm
这些功能的架构仍在进行中,因此是否应该由 Web 应用程序本身或在数据库内部或其他一些方法来决定是否仍然开放。由于许可限制,第二个选项被搁置了,所需的功能,即 SDO_RELATE
,是 Oracle 11 中的 Oracle Spatial 所独有的,显然与 Oracle 12 相比,它们仅适用于 Oracle Locator。
编辑:这是不正确的,如下面的@Albert Godfrind 所指出的。 SDO_RELATE
和其他空间运算符 受 Oracle Locator 11 支持,如 Oracle 文档中所示,在第 1 版和第 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
另外,Java API for Oracle 11的JGeometry
class不包含isInside()
或anyInteract()
等方法就像它在 Oracle 12 对应物上所做的那样。 Oracle 12 Java API 文档:https://docs.oracle.com/en/database/oracle/oracle-database/12.2/spajv/index.html?oracle/spatial/geometry/JGeometry.html
你说
I also cannot use the Oracle Spatial functions directly in the database due to a license restriction.
这是不正确的。 2D 的所有矢量处理函数都随您的数据库许可证免费提供。它称为 Oracle 定位器。请参阅用户指南的附录 B,了解 Locator 涵盖的详细信息 (https://docs.oracle.com/en/database/oracle/oracle-database/18/spatl/oracle-locator.html#GUID-EC6DEA23-8FD7-4109-A0C1-93C0CE3D6FF2)。请注意,这是针对 12.2 的。旧版本限制更多,但都具有执行所需几何过滤的基本功能。
Oracle Spatial & Graph 许可补充了基本功能,支持 3D、光栅、网络、地理编码、可视化以及语义和 属性 图形。
除此之外,您可以自由使用JAVA API(Oracle Locator 自带)。但请注意,它只适用于 JGeometry
个对象。特别是方法 isInside()
会告诉您一个 JGeometry 对象(您的点)是否完全包含在另一个 JGeometry 对象(您的多边形)中。如果您希望落在多边形边界上的点也被 returned,请改用 anyInteract()
方法。
但是,如果您的主要处理是在数据库中,那么只需使用 SQL 和 SDO_INSIDE
(或 SDO_ANYINTERACT
谓词来查找落在多边形内的所有点。
你能多说说你的实际用例吗?
编辑
这里有一个使用SQLAPI的例子。这对所有数据库版本都有效,一直到 11gR1 并且包含在 Oracle Locator 中(即不需要任何 Spatial 许可)。
在此示例中,我使用了以下两个 table:
包含美国主要城市位置的table:
Name Null? Type
---------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
CITY VARCHAR2(42 CHAR)
POP90 NUMBER
RANK90 NUMBER
LOCATION MDSYS.SDO_GEOMETRY
一个 table 包含美国各州的形状:
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
下面查询returns纽约州的所有城市:
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';
其中 return 个:
CITY POP90
------------------------------ ----------
New York 7322564
Buffalo 328123
Rochester 231636
Yonkers 188082
Syracuse 163860
Albany 101082
6 rows selected.
您只需通过 JDBC 提交此声明并获取结果。如果您还想 return 点的位置(以在地图上突出显示它们),则还包括 location
列。您可以通过空间 Java API 在您的 Java 代码中解释这一点。或者你也可以从点中提取坐标:
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.
首先我尝试了:
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);
}
这没有用,因为 JGeometry.createShape()
returns 一个 java.awt.geom.GeneralPath
对象,它没有双精度,因此 shape.contains(...)
在多边形。
然后我尝试使用 JGeometry.createDoubleShape()
代替 returns 一个 oracle.spatial.util.SDODoubleGeneralPath
对象:
// ...
JGeometry geo = JGeometry.load(geoStruct);
Shape shape = geo.createDoubleShape();
return shape.contains(point);
调用contains
方法时,出现以下错误:
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)
如果 contains
方法抛出此类错误,显然 SDODoubleGeneralPath
存在一些实施问题。
有其他选择吗?
我不想自己实现一个算法来确定一个点是否在多边形内,我宁愿相信已经从信誉良好的来源实现的东西。 由于许可限制,我也不能直接在数据库中使用 Oracle Spatial 函数。
Obs.: 我的 isInPolygon
方法使用 Point2D 作为参数,但如果我必须使用 JGeometry
对象形式的点,以防解决方案需要它,那也没关系。
根据要求提供更多详细信息:
Oracle 版本是 11g,我正在开发一个应该验证数据库中的几何数据的 Web 应用程序。特别是,其中一项验证涉及检查某些点和多边形是否在其他多边形内部。我引用的文档是这样的:https://docs.oracle.com/cd/E18283_01/appdev.112/e11829/toc.htm
这些功能的架构仍在进行中,因此是否应该由 Web 应用程序本身或在数据库内部或其他一些方法来决定是否仍然开放。由于许可限制,第二个选项被搁置了,所需的功能,即 SDO_RELATE
,是 Oracle 11 中的 Oracle Spatial 所独有的,显然与 Oracle 12 相比,它们仅适用于 Oracle Locator。
编辑:这是不正确的,如下面的@Albert Godfrind 所指出的。 SDO_RELATE
和其他空间运算符 受 Oracle Locator 11 支持,如 Oracle 文档中所示,在第 1 版和第 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
另外,Java API for Oracle 11的JGeometry
class不包含isInside()
或anyInteract()
等方法就像它在 Oracle 12 对应物上所做的那样。 Oracle 12 Java API 文档:https://docs.oracle.com/en/database/oracle/oracle-database/12.2/spajv/index.html?oracle/spatial/geometry/JGeometry.html
你说
I also cannot use the Oracle Spatial functions directly in the database due to a license restriction.
这是不正确的。 2D 的所有矢量处理函数都随您的数据库许可证免费提供。它称为 Oracle 定位器。请参阅用户指南的附录 B,了解 Locator 涵盖的详细信息 (https://docs.oracle.com/en/database/oracle/oracle-database/18/spatl/oracle-locator.html#GUID-EC6DEA23-8FD7-4109-A0C1-93C0CE3D6FF2)。请注意,这是针对 12.2 的。旧版本限制更多,但都具有执行所需几何过滤的基本功能。
Oracle Spatial & Graph 许可补充了基本功能,支持 3D、光栅、网络、地理编码、可视化以及语义和 属性 图形。
除此之外,您可以自由使用JAVA API(Oracle Locator 自带)。但请注意,它只适用于 JGeometry
个对象。特别是方法 isInside()
会告诉您一个 JGeometry 对象(您的点)是否完全包含在另一个 JGeometry 对象(您的多边形)中。如果您希望落在多边形边界上的点也被 returned,请改用 anyInteract()
方法。
但是,如果您的主要处理是在数据库中,那么只需使用 SQL 和 SDO_INSIDE
(或 SDO_ANYINTERACT
谓词来查找落在多边形内的所有点。
你能多说说你的实际用例吗?
编辑
这里有一个使用SQLAPI的例子。这对所有数据库版本都有效,一直到 11gR1 并且包含在 Oracle Locator 中(即不需要任何 Spatial 许可)。
在此示例中,我使用了以下两个 table:
包含美国主要城市位置的table:
Name Null? Type
---------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
CITY VARCHAR2(42 CHAR)
POP90 NUMBER
RANK90 NUMBER
LOCATION MDSYS.SDO_GEOMETRY
一个 table 包含美国各州的形状:
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
下面查询returns纽约州的所有城市:
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';
其中 return 个:
CITY POP90
------------------------------ ----------
New York 7322564
Buffalo 328123
Rochester 231636
Yonkers 188082
Syracuse 163860
Albany 101082
6 rows selected.
您只需通过 JDBC 提交此声明并获取结果。如果您还想 return 点的位置(以在地图上突出显示它们),则还包括 location
列。您可以通过空间 Java API 在您的 Java 代码中解释这一点。或者你也可以从点中提取坐标:
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.