admin 管理员组

文章数量: 887021


2024年1月17日发(作者:servlet多线程还是单线程)

7.2 在Java中操作几何体

如你所见,空间对象以SDO_GEOMETRY类型存储在数据库表中。要在Java中对其进行处理,必须首先使用JDBC从数据库中读取它们,然后把它们映射为Java类。

把SDO_GEOMETRY类型映射为Java类是比较简单的,这都归功于Oracle Spatial提供的API接口。API本身是比较简单的:它有一个主要的包(ry),该包中包含两个主要的 类(JGeometry和J3D_Geometry)。这个API在Oracle Database 11g中被显著地加强了。它现在包含许多几何体的处理函数,同时也包含可以在一些标准格式(GML、WKT和ESRI等格式)间进行几何体格式转换的工具 函数。这些工具都位于oracle.

包中。

Oracle Spatial的Java API由位于Oracle安装目录($ORACLE_HOME/md/jlib )下的两个JAR文件(和)组成。要在应用中使用API,须记得要在类路径(classpath)中包含上述目 录。你也需要JDBC驱动和XML解析器(仅用于处理GML)。下面是在Windows环境中你的类路径的设置:

1. C:>set classpath=.;%ORACLE_HOME%;

2. %ORACLE_HOME%;%ORACLE_HOME%;

3. %ORACLE_HOME%;

Oracle Spatial Java API 的文档 (Javadoc) 在Oracle文档全集中有,也可以在你的Oracle安装目录中的两个文档$ORACLE_HOME/md/doc/和中找到。

在Apress站点中可以找到大量的关于如何读、写和处理几何体的Java完整示例。表7-1中列出了这些程序。

表7-1 程序示例

程 序

返 回 信 息

打印出任意表中几何体的结构

把表中一部分或全部的几何体导

出到格式为WKT、WKB、

GML或其他的文件

把先前导出的几何体导入到表中

7.2.1 使用JGeometry类

Java中操作几何体的主要工具就是JGeometry类。它使你可以对数据库中的几何体进行读写,同时也可以对几何体进行检查,创建新的几何体和对这些几何体进行一系列的转换。

1. 几何体的读写

当你用SQL的SELECT语句读取对象类型(如SDO_GEOMETRY类型)时,JDBC返回一个Java结构-- 更精确地说,是对象。在写一个对象的时候(用INSERT或UPDATE语句),你仍需要通过 对象来传递。对STRUCT的解码和构造都是相当复杂的,而Oracle Spatial Java

API(JGeometry类)的主要目标就是使得这种操作变得简单。

JGeometry类提供了两种把STRUCT转换成JGeometry对象的方法:

load()方法用于读入STRUCT,返回JGeometry对象。当你对由SELECT语句返回的几何体进行转换的时候,用该方法。

store()方法与load()方法相反。它把JGeometry对象转换成可以通过INSERT或UPDATE语句写回数据库中的STRUCT对象。

图7-3展示了转换的过程。

(点击查看大图)图7-3 在Java中对几何体的读写

在Oracle Database 11g中,load()和store()方法得到了强化,以提供自己的为几何体对象而优化的序列化(pickling) 及反序列化(unpickling)方法。因此它们应当执行得更好。对于读取一个几何体,首先要把这个对象以字节数组的形式读入,然后把这个数组传递给 load()方法。图7-4展示了这个过程。

图7-4 用优化的"序列化"工具在Java中对几何体进行读写

下面的这个例子,首先用结果集的getObject()方法,把各行的几何体对象提取为STRUCT类型,然后使用JGeometry的静态方法load()把它转换成JGeometry对象。

1. STRUCT dbObject = (STRUCT) ect(1);

2. JGeometry geom = (dbObject);

要使用优化的反序列化工具(unpickler),首先使用结果集的getBytes( )方法提取几何体到一个字节数组中。然后再次使用JGeometry的静态方法load()把它转换成JGeometry对象。

1. byte[] image = rs. getBytes (1);

2. JGeometry geom = (image);

2. 检查几何体

你现在可以用多个get()方法从几何对象中提取信息。表7-2中总结了主要的方法。在表7-3中附加的is()方法对几何体的特性进行了细化。

表7-2 JGeometry中主要的get()方法

方 法

getType()

getDimensions()

getSRID()

getNumPoints()

getPoint()

getFirstPoint()

getLastPoint()

getMBR()

getElemInfo()

返 回 信 息

几何体类型 (1表示点,2表示线,依次类推)

维度

空间参考系ID

几何体中的点数

点对象的坐标(如果几何体是点)

几何体中的第一点

几何体中的最后一点

几何体的MBR

SDO_ELEM_INFO数组的内容

返回SDO_POINT结构的坐标。当用来填充线

或多边形几何体时,这通常被用来当标记点

对一个单点对象来说,以.

getOrdinatesArray()

SDO_ORDINATES数组的内容

getLabelPoint()

getJavaPoint()

2D对象的形式返回点坐标

getJavaPoints()

getElements()

getElementAt()

createShape()

对多点对象来说,返回一个.

2D 对象数组

得到一个JGeometry对象数组,每个对

象都表示几何体的一个元素

以JGeometry格式提取几何体的一个元素

把几何体转换成对象,

为绘制和使用包中的工具做准备

表7-3 主要的JGeometry is()方法

方 法

isPoint()

isOrientedPoint()

isCircle()

isGeodeticMBR()

isMultiPoint()

isRectangle()

hasCircularArcs()

isLRSGeometry()

返 回 信 息

是不是点

是不是有向点

是不是圆

是不是大地测量学的MBR

是不是多点

是不是矩形

几何体中是否包含弧

是不是一个线性参照的几何体

表7-2中的两个方法(getElements()和getElementAt())可以对复杂几何体的结构进行检查:你可以提取独立的元素作为单 独的JGeometry对象。第一个方法返回所有元素到独立的JGeometry对象数组中。第二个方法返回一个指定元素,通过其在几何体中的位置来标 识。

警告:

对element术语的理解应参见OGC Simple Features for SQL规范。例如,一个有孔多边形被看作是一个单元素,尽管它由多个环(在Oracle概念中,每个都是一个单元素)组成。在第5章中讨论的验证函数

(VALIDATE_GEOMETRY_WITH_CONTEXT()和EXTRACT())表现出同样的方式。这就意味着getElements()方 法不允许你从一个有孔多边形中提取孔。

警告:

元素的编号方式是从1开始的,而不是0。

3. 创建几何体

向数据库中写入几何体(用INSERT或UPDATE语句)要求先创建一个新的JGeometry对象,并用()方 法把它转换成STRUCT。然后把STRUCT传递给INSERT或UPDATE语句。同load()方法一样,也可以使用速度更快的空间序列化工具。图 7-3和7-4展示了这两个方法。

下面是这两个方法的例子。首先用JGeometry的静态方法store()把它转换为STRUCT,然后用setObject()方法把它插入到准备好的SQL语句中。

1. STRUCT dbObject = (geom, dbConnection);

2. ect (1,dbObject);

使用优化的序列化工具除了与store()方法的参数顺序相反以外,其他的都一样:先是定义数据库连接对象,然后是创建JGeometry对象。

1. STRUCT dbObject = (dbConnection, geom);

2. ect (1,dbObject);

有两种方法可以构造新的JGeometry对象。一种是使用表7-4中的构造函数。另一种方法是用静态的方法来创建不同的几何体。表7-5列出了这些方法。

表7-4 JGeometry转换器

构 造 函 数

JGeometry (double x, double y, int srid)

JGeometry (double x, double y, double z, int srid)

JGeometry (double minX, double minY, double maxX,

double maxY, int srid)

JGeometry (int gtype, int srid, int[]

elemInfo, double[] ordinates)

目 的

构造点

构造三维点

创建矩形

构造一般几何体

表7-5 静态JGeometry创建方法

创 建 方 法

createPoint(double[] coord, int dim, int srid)

createLinearLineString(double[] coords, int dim, int srid)

createLinearPolygon(double[] coords, int dim, int srid)

createMultiPoint([] coords, int dim, int

srid)

createLinearMultiLineString([]

coords, int dim, int srid)

createLinearPolygon([] coords, int dim, int

srid)

createCircle(double x1, double y1, double x2,

double y2, double x3, double y3, int srid)

用圆上三点创建圆

创建多重线串对象

创建多重多边形

创建多重点对目 的

创建点

创建简单线串

创建简单多边

createCircle(double x, double y, double radius, int srid)

用中心和半径创建圆

4. 修改存在的几何体

JGeometry类没有提供任何用于修改几何体的方法。例如,没有方法用来从一个线上删除或添加一点。为了执行那些更新,你需要用某个方法,如

getOrdinatesArray(),来提取点序列,然后更新由此产生的Java数组,最后用其结果来创建一个新的JGeometry对象。

像先前讨论的那样把修改后的几何体写入到数据库中:用store()方法把JGeometry对象转换为STRUCT,然后把STRUCT传给SQL中的INSERT或UPDATE语句。

5. 处理几何体

Java API也提供了大量的用于对几何体执行各种转换的方法。表7-6中列出了主要的方法。它们以JGeometry对象作为输入而以生成的新几何体作为输出。注意,这些函数大部分都由数据库提供,并通过PL/SQL来对其进行调用,这一内容将在第9章中详述。

表7-6 几何体处理函数

方 法

buffer(double bufferWidth)

simplify(double threshold)

densifyArcs(double arc_tolerance)

clone()

affineTransforms(...)

projectToLTP(double smax, double

flat)

projectFromLTP()

把一个几何体从一个本地切面投影到经度/纬度上

目 的

在几何体周围形成一个缓冲区

简化几何体

在几何体中增加所有弧的密度

复制一个几何体

基于提供的参数(平移、缩放、旋转、切变、

反射)在输入几何体上应用仿射变换

把一个几何体从经度/纬度投影到一个切面表7-7总结了API提供的一些辅助方法。这些函数都不涉及JGeometry对象(除了equals),用来辅助处理一定的任务。

equals()函数用来比较两个JGeometry对象,判断它们是否相同。然而,这种比较是基于几何体的内部编码的:如果两个几何体的所有点的坐标都相同且顺序相同,那么就认为这两个几何体相同。该方法不真正进行涉及容差的几何体的比较。

表7-7 几何体辅助函数

方 法

目 的

equals()

computeArc(double x1, double y1, double x2,

double y2, double x3, double y3)

linearizeArc(double x1, double y1, double

x2,double y2, double x3, double y3)

reFormulateArc(double[] d)

expandCircle(double x1, double y1, double

x2,

double y2, double x3, double y3)

monoMeasure(double[] coords, int dim )

判断两个几何体是否相同

从三个坐标点来计算这

个弧的中心、半径和角度

把一个弧转换成一组二维线段

通过重新计算角度重构一条弧

通过把圆转换成一组二维

线段来实现对其的线性化

判断一条线是否有变

长或缩短的方法

7.2.2 使用3D几何体:J3D_Geometry类

Oracle Database 11g的一个主要的新功能就是它能够对复杂的3D对象(表面和立方体)进行建模。新的J3D_Geometry类能够帮助你操作那些结构。注意,它是JGeometry的子类,所以到目前为止,你已经了解的所有方法都是适用的。

像处理JGeometry一样,从数据库中读取J3D_Geometry对象,然后由JGeometry对象构造一个J3D_Geometry对象。例如:

1. byte[] image = rs. getBytes (1);

2. JGeometry geom = (image);

3. J3D_Geometry geom3D = new J3D_Geometry (

4. e(), D(),

5. mInfo(), inatesArray()

6. );

仅通过使用规则的()函数向数据库中写入J3D_Geometry对象:

1. STRUCT dbObject = (dbConnection, geom3d);

2. ect (1,dbObject);

与JGeometry相似,它提供给你大量的方法用来对几何体进行不同的操作。表7-8总结了这些方法。

表7-8 3D几何体处理函数

方 法

anyInteract(J3D_Geometry A, double

tolerance)

目 的

判断两个三维几何体是否相交

extrusion(JGeometry polygon, double

通过对一个二维多边形

grdHeight,

进行拉伸返回一个三维几double height, Connection conn, boolean cond,

何体

double tolerance)

closestPoints(J3D_Geometry A, double

tolerance)

getMBH(J3D_Geometry geom)

validate(double tolerance)

area(double tolerance)

length(int count_shared_edges, double

tolerance)

volume(double tolerance)

distance(J3D_Geometry A, double tolerance)

计算出两个三维几何体

中离得最近的点

对一个三维几何体返

回一个三维的边界框

验证一个三维图像的有效性

计算一个面或立方体

中一面的面积

计算三维形状的长度

计算三维立方体的体积

计算两个三维几何体之间的距离

7.2.3 从3D几何体中提取元素:ElementExtractor类

3D对象可能是复杂的。立方体尤其符合这种情况。一个复杂的立方体由多个简单的立方体组成。这些简单的立方体又由表面组成,它们中的某些几何体构成了立方体中的孔。表面本身由各种元素构成,而这些元素又由环组成。

ElementExtractor类使得对复杂对象的检查和独立组件的提取变得容易,如组成立方体的一个或多个表面。注意,你也可以对一些规则的二维几何体使用提取器。例如,在一个复杂的多边形中,它对提取一个环的线性等高线是很有用的。

可以通过两种方式使用提取器:一个是提取指定的元素,另一个是对所有有效的元素进行迭代。

提示:

ElementExtractor类并不只局限于3D几何体,对2D图形也适用。但是因为其输入必须是J3D_Geometry对象,所以首先要把JGeometry对象转换成一个J3D_Geometry对象。

1. 提取独立元素

你需要做的就是调用静态的getElementByLabel()方法,传入一个用于唯一标记待提取几何体元素的标签。这个标签是一个由逗号分隔的 ID号码字符串,该号码指定了待返回的几何体的子集。根据需要可以对以下列出的元素进行指定(对在最后一个指定的元素前的任何一个null元素,为其输入 一个逗号)。

点ID

边ID

环ID

多边形ID

表面ID

立方体ID

多重立方体ID

这些ID号是每个元素在每一级上的序列号。序列号从1开始:在多重多边形中第一个多边形是多边形1。它的环从1到N编号。第二个多边形是多边形2,其中的环仍从1到N编号。

例如,下面的代码将从简单立方体的表面2中返回3号多边形:

1. J3D_Geometry ring=ElementExtractor.

getElementByLabel(solid,"0,0,0,3,2");

注意,当有错误发生时,J3D_Geometry中的validate()方法将返回这样一个标记。你可以通过向mentByLabel()传递这个标记来分离发生错误的元素。

2. 对元素进行迭代

创建一个ElementExtractor对象,向它传递你的查询的参数。这实际上将创建一个 在一定详细程度上初始化的迭代器。接着,你可以通过其nextElement()方法来逐个提取对象的组件。然后还可以用另一个

ElementExtractor对象进一步地检查第一次循环所得到的每个元素。

表7-9详细介绍了创建一个ElementExtractor对象所要用到的参数。这些参数控制提取器的行为方式。

表7-9 ElementExtractor的参数

参 数

geometry

firstElement

extractionLevel

目 的

待分析的3D几何体

开始提取的位置,在几何体的SDO_ELEM_INFO

数组中被定义为偏移量。默认值为0

这个参数决定迭代器处理几何体中元素的方法。

请参见“提取级别”一节获取详细信息

指定为true(默认值)或false。在

MULTICOMP_TOSIMPLE提取级别上,

用户可以通过把这个参数设置为FALSE,

allow_comp_sub_elements

从多个或复合几何体中直接提取

简单几何体。默认值为TRUE,

这意味着用户将先从多几何体中提取复合几何体

3. 提取级别

extractionLevel参数控制提取器的操作方式。以下面的值对其进行指定。名称为在类中所定义的常量。

Level 0 = MULTICOMP_TOSIMPLE:它返回一个在多重几何体中的连续元素。例如,它将返回多立方体中的每个立方体或多重多边形中的每个多边形。可以把它想象成对元素的水平扫描。

Level 1 = INNER_OUTER:如果存在,它将返回内部/外部元素。对于多边形,它先返回外环,接着返回内环。

Level 2 = LOWER_LEVEL:它返回子元素;也就是,它返回下一个层次中的元素(例如,对立方体而言它的下一级是表面,而对于多边形而言是边,以此类推)。

让我们来看看一个有多个孔的多重多边形的简单例子。我们首先用MULTICOMP_TOSIMPLE对其进行充分的扫描。这将把这个多重多边形分解为单个简单的多边形。

下一步是从这些多边形中提取环。首先要用INNER_OUTER扫描由第一步返回的多边形。

对于无孔的多边形,结果不会变。但是对于那些有孔的多边形,你首先得到的是表示外环的多边形,然后是一个或多个表示内环的多边形。

最后,为了进一步分解几何体,用LOWER_LEVEL对每一个环进行扫描。这种扫描将把每个环分解成一个边集--有两个顶点的简单的线串集。

下面是用提取器把一个多重几何体分解为元素的一个例子:

1. // Create new extractor

2. ElementExtractor e = new ElementExtractor (

3. geom3d, 0, OMP_TOSIMPLE);

4. // Geometry to receive extracted element(s)

5. J3D_Geometry g;

6. // Used to receive the type of element (1=outer, 2=inner)

7. int is_a_hole[] = {0};

8. // Extract the elements

9. while ((g = ement(is_a_hole)) != null) {

10.// Process extracted element

11.}

注意,nextElement()需要一个输出参数来表示其输出的元素是外部元素还是内部元素。这个参数必须以int[]的形式定义,其第一个元素被设置成1(表示外部元素)或2(表示内部元素)。这仅适用于LOWER_LEVEL扫描,但是所有的情况中都需要输出参数。

4. 递归分解

你不需要对extractionLevel参数进行指定。如果忽略了,提取器将自动选择最合适的级别用作对几何体的分析。这使得它很容易在其成分上对几何体进行递归地分解,直到独立的线段。这将在下面的例子中得到展示:

1. void decomposeGeometry (J3D_Geometry geom, int level, int seq)

2. throws Exception

3. {

4. n ("Level: "+ level+" Sequence: "+seq);

5. if ((e() == _CURVE) ||

6. (e() == _POINT))

7. return;

8. MyElementExtractor e = new MyElementExtractor (geom);

9. int i = 0;

10.J3D_Geometry g;

h[] = {0};

((g = ement(h)) != null) {

oseGeometry (g, level + 1, i+1);

14.i++;

15.}

16.}

7.2.4 使用标准的格式:WKT、WKB和GML

在第5章中我们讨论了能够在SDO_GEOMETRY类型和开放地理信息系统联盟(OGC)定义的标准格式之间进行转换的PL/SQL函数和方法。 OGC的标准格式有:WKT(熟知文本)、WKB(熟知二进制)和GML(地理标识语言)。我们将对怎样在Java中执行这些转换做简要解释。

对这些格式进行操作的类都在包中。和程序(可以从Apress站点下载)对WKT、WKB和GML格式的读写提供了功能齐全的例子。

1. 对WKT的读写

WKT格式是对几何体编码的一个结构化文本格式。它最初被设计为在不同环境间交换几何体的一种标准方法。点在WKT中可以被编码为:

1. POINT(-111.870478 33.685992)

一个简单多边形可以编码为:

1. POLYGON ((-119.308006 37.778061,..., -119.308006 37.778061))

我们将使用包中WKT类的fromJGeometry()方法将JGeometry对象转换为WKT格式。注意,这个方法将生成一个字节数组,在将其写入到文件前,需要把它转换成一个字符串。如下所示:

1. // Create a WKT processor

2. WKT wkt = new WKT();

3. ...

4. // Convert the geometry to WKT

5. String s = new String(eometry(geom));

也可以用WKT类中的toJGeometry方法把WKT字符串转回为JGeometry对象。这个方法的输入也是一个字节数组。过程如下所示:

1. // Create a WKT processor

2. WKT wkt = new WKT();

3. ...

4. // Convert the WKT to geometry

5. JGeometry geom = metry(es());

警告:

WKT和WKB格式没有提供任何的机制用来表示几何投影;也就是说,在将几何体转换成另一种格式时,这类信息将丢失。如果想保留这些信息,需要单独对其进行处理,同时用setSRID()方法把它回添到几何体中。

2. 对WKB的读写

顾名思义,WKB编码就是把几何体以二进制的方式编码,是一种比WKT更紧凑的格式。它的使用方法与前面的处理相似。WKB类中的 fromJGeometry()方法也产生一个字节数组,你可以把该数组写到文件中。我们将向你展示怎样将其写入java DataOutputStream(在例子中称为ds)中,以便以便携的方式写入原始Java数据类型。

首先把JGeometry转换为WKB。这将产生一个字节数组。然后把字节数组的大小和几何体的SRID写入输出流。最后以适当的方式写入字节数组。

1. // Create a WKB processor

2. WKB wkb = new WKB(_ENDIAN);

3. ...

4. // Convert JGeometry to WKB

5. byte[] b = eometry(geom);

6. // First write the number of bytes in the array

7. nt();

8. // Then write the SRID of the geometry

9. nt(D());

10.// Then write the binary array

(b);

注意,_ENDIAN参数表明要使用的二进制编码的类型:大尾段(big

endian)或小尾段(litle endian)。默认生成大尾段编码。你需要使其与用来处理WKB工具所接受的编码相适应。注意,其中选择的编码方法本身是在二进制结果的第一个字节被标 记的。

用WKB类中的toJGeometry()方法可以把WKB转换成JGeometry对象。这个方法仍用字节数组作为输入。下面的例子展示了这个过 程。假设我们从先前的文件中读取数据,首先读取WKB的长度,然后读取几何体的SRID。接下来读取重建WKB所需的字节数。最后把WKB转换为 JGeometry对象并用setSRID方法把SRID写回。

1. // Create a WKB processor

2. WKB wkb = new WKB();

3. ...

4. // Read the size of the byte array

5. int n = t();

6. // Read the SRID of the geometry

7. int srid = t();

8. // Read the byte array that contains the WKB

9. byte[] b = new byte[n];

l = (b, 0, n);

11.// Convert to JGeometry

= metry(b);

13.// Add the SRID

D(srid);

我们不需要指定将要使用的二进制编码的格式(大尾段或小尾段),因为toJGeometry()方法能够自动对编码进行识别并可以对两者进行透明的处理。

3. 对GML的读写

WKT和WKB有许多限制:它们只支持简单2D的形状,而不支持任何3D的形状,以及弧或圆。另外,它们也无法指定对几何体的投影。GML(地理标识语言)则是一个强有力的解决方案,它对地理信息进行XML编码。

为了对GML进行读写,我们将展示怎样使用表7-10中总结的4个类及其方法。存在多个类的原因是GML标准在不断更新中。现在有两个主要的版本:GML2和GML3,其中GML3提供对3D(表面和立方体)和其他高级工具的支持。

表7-10 对GML进行处理的类和方法

GML版本

写GML

GML2

GML3

读GML

_GMLGeometry()

deToGeometry()

_GML3Geometry()

deToGeometry()

其中GML3是GML2的父集,所以任意由GML2编码的几何体都可以被GML3读取。反之则不成立。

把JGeometry对象转换成GML格式很容易,如下所示:

1. //Create a GML2 Processor

2. GML2 gml = new GML2();

3. ---

4. String s = _GMLGeometry(geom);

如你所见,to_GMLGeometry()返回一个可以直接写出的字符串。你也可以把结果(一个XML串)包含在其他XML文档中。上面的例子使用了GML2。而对GML3的使用,只要使用合适的类和方法就可以了。

把GML字符串转换成JGeometry对象就比较麻烦了。转换方法(de

ToGeometry()和deToGeometry())不支持GML字符串直接作为输入,但是支持解析文档。所以在使用这些 方法之前,你自己必须首先对GML字符串进行解析。下面的例子展示了这个过程:

1. // Create a GML3 Processor

2. GML3 gml = new GML3g();

3. ...

4. // Read GML string from input stream

5. String s = ne();

6. // Setup an XML DOM parser

7. DOMParser parser = new DOMParser();

8. // Parse the XML string

9. (new StringReader(s));

10.// Get the parsed document

nt document = ument();

12.// Get the top level node of the document

node = umentElement();

14.// Convert to geometry

= deToGeometry(node);

注意到,这里使用了GML3处理器。要是使用GML2处理器,只需把GML3g转换为GML。

警告:

确保不要弄混在GML处理中包含的类的名称。

7.2.5 使用ESRI shapefile

ESRI shapefile格式是进行地理数据传输的流行格式。在第5章中,我们曾用了一个简单的命令行工具对shapefile进行转换并把它们加载到空间表 中。现在我们要展示的是怎样在Java中对其进行读写。为此,我们将要用到包中的一些类。

如果你仅需把ESRI shapefile加载到数据库表中,那么就能够满足你的需求。调用 SampleShapefileToJGeomFeature类并向它传递合适的参数。如果你不了解这些参数,可以不包含任何参数,该类将告诉你这些参数 的详情。下面是把shp_cities shapefile内容加载到us_cities表中的例子:

1. C:>java Shapefile

ToJGeomFeature -h 127.0.0.1 -p 1521

2. -s orcl111 -u spatial -d spatial -t us_cities -f shp_cities -r 8307

3. host: 127.0.0.1

4. port: 1521

5. sid: orcl111

6. db_username: spatial

7. db_password: spatial

8. db_tablename: us_cities

9. shapefile_name: shp_cities

: 8307

ting to

12.127.0.0.1,1521,orcl111, spatial, spatial,

us_cities, shp_cities, null, 8307

ng

ng

ting record #10 of 195

ting record #20 of 195

ting record #30 of 195

ting record #40 of 195

ting record #50 of 195

ting record #60 of 195

ting record #70 of 195

ting record #80 of 195

ting record #90 of 195

ting record #100 of 195

ting record #110 of 195

ting record #120 of 195

ting record #130 of 195

ting record #140 of 195

ting record #150 of 195

ting record #160 of 195

ting record #170 of 195

ting record #180 of 195

ting record #190 of 195

34.195 record(s) converted.

.

该类将创建表、加载该表并在USER_SDO_GEOM_METADATA中插入合适的元数据。

1. 对shapefile的简单说明

shapefile名字会让人产生误解。每个shapefile至少由3个文件构成,它们的文件名都相同,只是扩展名互不相同。例如,在前面例子中的shp_cities

shapefile实际上是下面文件的集合:

shp_:这个文件中保存了对实际几何体的定义。

shp_:在形状上的空间索引。

shp_:一个dBASE Ⅳ文件,包含每个几何体的属性。

有些shapefile中也带有其他一些文件,如shp_或shp_。这些都会被Oracle Spatial Java 类所忽略。

在为shapefile命名的时候不要包含.shp扩展名。所有组成同一个shapefile的文件都应该被存储在同一个目录下。

2. 在程序中加载shapefile

包中包含了3个可以用来在你的程序中读取和加载shapefile的类。表7-11是对它们的总结。

表7-11 Shapefile处理类

类 名 称

ShapefileReaderJGeom

DBFReaderJGeom

ShapefileFeatureJGeom

目 的

提供函数,用来从shapefile中读取形状

(几何体)并把它们转换成JGeometry对象

提供函数,用来从DBF文件中读取

属性并获取该属性的名称和类型

使用两个读取类提供高端函数,用来

创建数据库表并从shapefile中对其进行加载

下面的例子展示了怎样用shapefile处理类把shapefile加载器简单地合并到你的应用中。首先是打开输入文件并设置辅助类。要注意的是,shapeFileName中包含的是没有扩展名的shapefile名称。

1. // Open SHP and DBF files

2. ShapefileReaderJGeom shpr = new ShapefileReaderJGeom(shapeFileName);

3. DBFReaderJGeom dbfr = new DBFReaderJGeom(shapeFileName);

4. ShapefileFeatureJGeom sf = new ShapefileFeatureJGeom();

然后提取shapefile的维度,也就是在文件中的所有维度上的所有几何体坐标的最大值和最小值。

1. // Get shapefile bounds and dimension

2. double minX = X();

3. double maxX = X();

4. double minY = Y();

5. double maxY = Y();

6. double minZ = Z();

7. double maxZ = Z();

8. double minM = Measure();

9. double maxM = Measure();

shpDims = Dims(shpFileType, maxM);

现在可以构建空间元数据了。getDimArray()方法将根据shapefile中数据的限制和维度来生成合适的元数据定义。注意,在使用该方法时的一个特殊现象:X和Y维上的最大值和最小值必须以字符串的形式传递;Z和M维上的最大值和最小值以数字的形式传递。

1. // Construct the spatial metadata

2. String dimArray = Array(

3. shpDims, f(tolerance),

4. f(minX), f(maxX),

5. f(minY), f(maxY),

6. minZ, maxZ, minM, maxM

7. );

接下来可以在数据库中创建表了。该方法会先删除已经存在的表,然后用合适的Oracle数据类型(与DBF文件中的属性类型相匹配)创建一个新表。 它也会向USER_SDO_ GEOM_METADATA中插入空间元数据。指定SDO_GEOMETRY列的名称(geoColumn)。你也可以有选择地指定某个标识列 (idColumn)的名称。该列将在加载的过程中被自动填充序列号。

1. // Create table before loading

2. eTableForData(

3. dbConnection, dbfr, tableName, geoColumn, idColumn,

4. srid, dimArray

5. );

现在你可以从shapefile中把数据加载到刚刚创建的数据库表中了。insertFeatures方法有很多参数:标识列名 (idColumn)、起始数据值(firstId)、提交的频率(commitFrequency)和载入期间打印进度消息的频率

(printFrequency)。

1. // Load the features

2. Features(dbConnection, dbfr, shpr, tableName,

3. idColumn, firstId,

4. commitFrequency, printFrequency, srid,

5. dimArray

6. );

最后,记得关闭输入文件:

1. // Close input file

2. hapefile();

3. BF();

3. 构建你自己的加载器

如果想要更大的灵活性,如选择要加载的属性、对其重命名或在加载前对几何体进行一些处理,就可以使用ShapefileReaderJGeom和DBFReaderJGeom类中较低层次的方法。表7-12和表7-13对其作了总结。

表7-12 ShapefileReaderJGeom方法

方 法

getMinX()

getMaxX()

getMinY()

getMaxY()

getMinZ()

getMaxZ()

getMinMeasure()

getMaxMeasure()

getShpFileType()

getShpDims()

numRecords()

getGeometryBytes(int

nth)

getGeometry(byte[]

recBuffer, int srid)

()

closeShapefile()

目 的

为x维返回最小值

为x维返回最大值

为y维返回最小值

为y维返回最大值

为z维返回最小值

为z维返回最大值

为m维返回最小值

为m维返回最大值

返回该shapefile中包含的几何体的类型:

点为1,线为3,多边形为5

返回几何体的维度

返回shapefile中记录的数目

从shapefile中以字节数组的

形式提取第n个几何体

把一个几何体从形状二进制

编码转换为JGeometry对象

关闭shapefile

表7-13 DBFReaderJGeom方法

方 法

numRecords()

numFields()

getFieldName(int nth)

getFieldType(int nth)

getFieldLength(int nth)

getRecord(int nth)

getFieldData(int nth, byte[]

rec)

closeDBF()

目 的

返回DBF文件中记录的数目。

这应该与shapefile中的记录数相匹配

返回DBF文件中属性的个数(与列数相等)

以字符串的形式返回第n个域的名称

以单独字符编码的形式返回第n个域的类型

返回第n个域的长度(在文件中占据的字节数)

以字节数组的形式返回文件中的第n个记录

从二进制记录中提取第n个域的值

关闭DBF文件

使用这些方法你可以从DBF文件中提取属性的名称、类型和属性大小。根据获得的信息,你在创建数据表时就有了充分的灵活性。以下展示了如何把DBF数据类型等价地映射到Oracle中。

1. int numFields = lds();

2. String[] fieldName = new String[numFields];

3. byte[] fieldType = new byte[numFields];

4. int[] fieldLength = new int[numFields];

5. String[] oracleType = new String[numFields];

6. for (int i=0; i

7. fieldName[i] = ldName(i);

8. fieldType[i] = ldType(i);

9. fieldLength[i] = ldLength(i);

(fieldType[i]) {

'C': // Character

Type[i] = "VARCHAR2(" + fieldLength[i] + ")";

;

'L': // Logical

Type[i] = "CHAR(1)";

;

'D': // Date

Type[i] = "DATE";

;

'I': // Integer

'F': // Float

'N': // Numeric

Type[i] = "NUMBER";

;

t:

new RuntimeException("Unsupported DBF field type " + fieldType[i]);

27.}

28.}

你可以很容易地创建相应的表。仅向CREATE TABLE语句添加属性名称就可以实现。

1. String createTableSql = "CREATE TABLE " + tableName +

"(";

2. for (int i=0; i

3. createTableSqlcreateTableSql = createTableSql +

fieldName[i] + " " + oracleType[i] + ",";

4. createTableSqlcreateTableSql = createTableSql +

geoColumn + " SDO_GEOMETRY)";

最后,通过循环向数据库中读取和插入数据,以便对SHP和DBF记录逐个地进行获取和处理。我们假设你构建并准备了相应的INSERT语句,每个待填充的列都有一个绑定变量。

1. for (int rowNumber = 0; rowNumber < numRows; rowNumber++)

2. {

3. // Extract attributes values from current DBF record

4. byte[] a = ord (rowNumber);

5. for (int i = 0; i< lds(); i++) {

6. ing (i+1,ldData(i, a));

7. }

8. // Extract geometry from current SHP record

9. byte[] s = metryBytes (rowNumber);

try geom = metry (s, srid);

11.// Convert JGeometry object into database object

dbObject = (dbConnection, geom);

ect (numFields+1, dbObject);

14.

15.// Insert row into the database table

e();

17.}

7.3 本章小结

在本章中,你学习了如何把几何体类型映射到数据结构中,然后在PL/SQL和Java中对该结构进行读、写和其他操作。注意,在不少场合下需要执行 这样的操作,不过Oracle Spatial为你提供了丰富的操作符集和函数集(涵盖了大多数的情况)。我们将在接下来的两章中讨论这些操作符和函数。


本文标签: 方法 进行 对象 提取 元素