以下文档是我对Geotools的翻译和理解,因为JTS的文档并不是那么全,希望大家受益。
JTS拓扑套件是一个GeoTools的外部套件来提供一个地理信息数据结构的实现。主要的好处就是经过多年的努力在数值上是稳定的。
GooToots都是关于实现空间解决方案的,我们尽最大努力遵循一个不自己疯狂造轮子的主旨。优秀的JTS拓扑套件项目提供了我们在整个库中使用的几何实现。
GeoTools中提供了一些组件辅助JTS
1 gt-api 提供了帮助类和扩展JTS的CurvedGeometryFactory工厂来处理弧线。
2 gt-main 提供了帮助类来讲Geometry转换为Java形态展示。
jts-topo-suite
http://tsusiatsoftware.net/jts/main.html
http://www.vividsolutions.com/jts/bin/JTS%20Developer%20Guide.pdf
http://www.vividsolutions.com/jts/bin/JTS%20Technical%20Specs.pdf
<dependency>
<groupId>com.vividsolutionsgroupId>
<artifactId>jtsartifactId>
<version>1.13version>
dependency>
我们使用JTS的GeometryFactory来创建Geometry对象。GeometryFactory有许多的创建方法能够让坐标实例被包裹进合适的Geometry中。
用于检索SQL标准的简单OGC要素是由Point, LineString and Polygon实现的。
每一个Geometry可以被一个Envelope(外包框)包围。OGC简单要素的对于SQL标准的实现同样也收GeometryCollections的支持。GeometryCollections其中自己包含着Geometry。
你可以实现你自己的GeometryFactory通过一个指定的PrecisionModel和一个CoordinateSequenceFactory。
如果您需要考虑坐标的存储方式(可能是floats而不是doubles),这些“高级”配置选项您应该会感兴趣。这两个概念一起工作:如果将坐标存储在浮点数数组中,那么JTS只需要在计算过程中考虑浮动精度。(此处不理解,我的理解是用什么存储系统下面的就是用什么,可能还需要看看源码)
GeometryFactory 工作的很好(可能意思是可以少考虑点底层)。
GeoTools扩展了这些Geometry类来支持曲线。这些实现生成坐标能够让他们像正常的JTS实现一样。
用于生成坐标的线性化过程利用了定义曲线的控制点和CurvedGetimeyFactory提供的容差。
下面我们使用JTS GeometryFactory来建立一个点,下面是使用FactoryFinder的一个标准实例。
如果您对精度有要求可以自己搞起。
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
Coordinate coord = new Coordinate(1, 1);
Point point = geometryFactory.createPoint(coord);
这里也支持WKT字符串创建 ,WKT字符串是SQL标准定义的一个实现。
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);
Point point = (Point) reader.read("POINT (1 1)");
如果您需要多个点,可以使用MultiPoint
下面的代码用于创造线:
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
Coordinate[] coords =
new Coordinate[] {new Coordinate(0, 2), new Coordinate(2, 0), new Coordinate(8, 6) };
LineString line = geometryFactory.createLineString(coordinates);
或者是使用WKT字符串搞定这个问题。
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
WKTReader reader = new WKTReader( geometryFactory );
LineString line = (LineString) reader.read("LINESTRING(0 2, 2 0, 8 6)");
如果线不连续可以考虑MultiLineString。
示例代码如下:
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
Coordinate[] coords =
new Coordinate[] {new Coordinate(4, 0), new Coordinate(2, 2),
new Coordinate(4, 4), new Coordinate(6, 2), new Coordinate(4, 0) };
LinearRing ring = geometryFactory.createLinearRing( coords );
LinearRing holes[] = null; // use LinearRing[] to represent holes
Polygon polygon = geometryFactory.createPolygon(ring, holes );
或者是从WKT字符串解决:
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory( null );
WKTReader reader = new WKTReader( geometryFactory );
Polygon polygon = (Polygon) reader.read("POLYGON((20 10, 30 0, 40 10, 30 20, 20 10))");
您可以使用MultiPolygon 表达洞,另外可以运用它表达您想要的形状。
When setting up a CurvedGeometryFactory the provided tolerance will be used during linearization:
请使用GeoTools CurvedGeometryFactory来创建环。在线性化过程中将会使用提供的容差。
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
CurvedGeometryFactory curvedFactory = new CurvedGeometryFactory(geometryFactory,Double.MAX_VALUE);
PackedCoordinateSequence coords = new PackedCoordinateSequence.Double(
new double[]{10,14,6,10,14,10}, 2 );
CircularString arc = (CircularString) curvedFactory.createCurvedGeometry(coords);
上面的圆弧应该看做10,14 6,10 14,10(参照WKT字符串)。上面的例子使用PackedCoordinateSequence来压入一串坐标数据。曲线(Curve)支持二维坐标。如果两个或者多个curves从一个闭环中被提供那么就会返回一个CircularLineString。
从WKT字符串中读取圆环:
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
CurvedGeometryFactory curvedfactory = new CurvedGeometryFactory(Double.MAX_VALUE);
WKTReader2 reader = new WKTReader2(curvedfactory);
CircularString arc = (CircularString) reader.read("CIRCULARSTRING(10 14,6 10,14 10)");
一个CompoundCurve(或者闭合的CompoundRing)包括一个CircularString的融合或者一个普通的LineString组件。
JTS拓扑套件没有一个结构用来表示曲线(curve)和圆(circle)。GeoTools添加了一个扩展。JTS所使用的数学严格限于由直线(即直线)组成的几何学。
GeoTools的曲线实现依靠着使用控制点来定义曲线,并在最后可能的时刻( the last possible moment)将其转换为直线。(这里需要看代码)
Curves也可以使用一些数学方法手工达成。
private static Geometry createCircle(double x, double y, final double RADIUS) {
GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
shapeFactory.setNumPoints(32);
shapeFactory.setCentre(new Coordinate(x, y));
shapeFactory.setSize(RADIUS * 2);
return shapeFactory.createCircle();
}
上面的代码其实没有特殊之处,他的想法就是创建一系列坐标逼近圆形。
另外一种方法是用java的handy Shape classes创建一个曲线或者shape对象然后从该对象中提取坐标来创建你想要的geometry。
不使用数学方法创建Arcs:
private static Geometry createBezierCurve(Coordinate start,
Coordinate end,
Coordinate ctrlPoint1,
Coordinate ctrlPoint2
double smooth) {
Shape curve = new CubicCurve2D.Double(
start.x, start.y,
ctrlPoint1.x, ctrlPoint1.y,
ctrlPoint2.x, ctrlPoint2.y,
end.x, end.y);
// the value of the smooth arg determines how closely the line
// segments between points approximate the smooth curve
// (see javadocs for Shape.getPathIterator method)
PathIterator iter = curve.getPathIterator(null, smooth);
// a length 6 array is required for the iterator
double[] iterBuf = new double[6];
List coords = new ArrayList();
while (!iter.isDone()) {
iter.currentSegment(iterBuf);
coords.add(new Coordinate(buf[0], buf[1]);
iter.next();
}
GeometryFactory gf = new GeometryFactory();
return gf.createLineString(coords.toArray(new Coordinate[coords.size()]));
}
下面是一群随机生成的弧线:
有时候你想建立一个曲线并且是能够穿过某些指定点的支线。这里建议您使用样条函数。这就产生了一组多项式(三次)曲线,每一条曲线都符合数据的一部分,并且平滑地连接到它的相邻曲线。
Splines(样条函数):
public Geometry splineInterpolatePoints(double[] xPoints, double[] yPoints) {
/*
* First we create a LineString of segments with the
* input points as vertices.
*/
final int N = xPoints.length;
Coordinate[] coords = new Coordinate[N];
for (int i = 0; i < N; i++) {
coords[i] = new Coordinate(xPoints[i], yPoints[i]);
}
GeometryFactory gf = new GeometryFactory();
LineString line = gf.createLineString(coords);
/*
* Now we use the GeoTools JTS utility class to smooth the
* line. The returned Geometry will have all of the vertices
* of the input line plus extra vertices tracing a spline
* curve. The second argument is the 'fit' parameter which
* can be in the range 0 (loose fit) to 1 (tightest fit).
*/
return JTS.smooth(line, 0.0);
}
对于多边形的平滑:
WKTReader reader = new WKTReader();
Geometry tShape = reader.read(
"POLYGON((10 0, 10 20, 0 20, 0 30, 30 30, 30 20, 20 20, 20 0, 10 0))");
Geometry tLoose = JTS.smooth(tShape, 0.0);
Geometry tTighter = JTS.smooth(tShape, 0.75);
结果:
使用Geometry很直接但是,当方法很多了也不太好。
Some summary information is available:
getArea() - area returned in the same units as the coordinates (be careful of lat/lon data!)
getCentroid() - the centre of the geometry
getEnvelope() - returns a geometry which is probably not what you wanted
getEnvelopeInternal() - this returns a useful Envelope
getInteriorPoint() - the centre of the geometry (that is actually on the geometry)
getDimension()
Geometry relationships are represented by the following functions returning true or false:
disjoint(Geometry) - same as “not” intersects
touches(Geometry) - geometry have to just touch, crossing or overlap will not work
intersects(Geometry)
crosses(Geometry)
within(Geometry) - geometry has to be full inside
contains(Geometry)
overlaps(Geometry) - has to actually overlap the edge, being within or touching will not work
covers(Geometry)
coveredBy(Geometry)
relate(Geometry, String) - allows general check of relationship see dim9 page
relate(Geometry)
To actually determine a shape based on two geometry:
intersection(Geometry)
union(Geometry)
difference(Geometry)
symDifference(Geometry)
Some of the most helpful functions are:
distance( Geometry )
buffer(double) - used to buffer the edge of a geometry to produce a polygon
union() - used on a geometry collection to produce a single geometry
The three most difficult methods are here (they will be discussed in detail):
equals( Object ) - normal Java equals which checks that the two objects are the same instance
equals( Geometry ) - checks if the geometry is the same shape
equalsExact( Geometry ) - check if the data structure is the same
There are some book keeping methods to help discovery how the geometry was constructed:
getGeometryFactory()
getPreceisionModel()
toText() - the WKT representation of the Geometry
getGeoemtryType() - factory method called (i.e. “point”, “linestring”, etc..)
A couple of methods are there to store your developer information:
getSRID() - stores the “spatial reference id”, used as an external key when working with databases
getUserData() - intended to be used by developers, a best practice is to store a java.util.Map
GeoTools will occasionally use this field to store a “srsName” or full CoordinateReferenceSystem.
用几何枚举来判断几何类型。
public boolean hit(Point point, Geometry geometry) {
final double MAX_DISTANCE = 0.001;
switch (Geometries.get(geometry)) {
case POINT:
case MULTIPOINT:
case LINESTRING:
case MULTILINESTRING:
// Test if p is within a threshold distance
return geometry.isWithinDistance(point, MAX_DISTANCE);
case POLYGON:
case MULTIPOLYGON:
// Test if the polygonal geometry contains p
return geometry.contains(point);
default:
// For simplicity we just assume distance check will work for other
// types (e.g. GeometryCollection) in this example
return geometry.isWithinDistance(point, MAX_DISTANCE);
}
}
“开箱即用”JTS适用于默认的双精度模型。通过配置GeometryFactory的PrecisionModel可以允许你使用一个不同的分辨率。
精度模型是数值计算的核心。当使用较大的值时,Java中内置的数学并不是非常精确。通过显式捕获PrecisionModel JTS中的”round-off”过程,JTS允许管理这类错误,并对工作的速度和准确性进行适当的权衡。
Round-off(例如 :6.0000001)经常发生即使是双精度模型。尤其是你的工作坐标系数字很大并且离着原点很远。这里面其实也是根据需要,如果你的使用精度要求不那么高那么可以减小精度模型。
下面的代码
The following code example takes into account that the location being passed in was only supplied as a floating point value x/y.
我们通过GeometryFactory创建一个带有PrecisionModel参数的测试点来解决这个问题。测试点将被标记为具有一定的有限精度,所有JTS操作都将考虑到这一点。
有多种方式来指定精度模型,但是这里我们基于十进制数的位置个数来为坐标比较提供服务。
private boolean polyContains(Polygon poly, float x, float y, int numDecPlaces) {
double scale = Math.pow(10, numDecPlaces);
PrecisionModel pm = new PrecisionModel(scale);
GeometryFactory gf = new GeometryFactory(pm);
Geometry testPoint = gf.createPoint(new Coordinate(x, y));
return poly.contains(testPoint);
}
你也可以使用其他的许多限定来建立精度模型。
pm = new PrecisionModel( PrecisionModel.Type.FIXED ); // fixed decimal point
pm = new PrecisionModel( PrecisionModel.Type.FLOATING ); // for Java double
pm = new PrecisionModel( PrecisionModel.Type.FLOATING_SINGLE ); // for Java float
你也许希望提供一个自定义的CoordinateSequenceFactory来减小内存使用使得程序更有效率。
程序内部的几何通常用Coordinate[]工作,但是许多被JTS所用的空间格式会将值存储在double[] or float[]这样的一维数组中,这样会效率更高。通过实现一个CoordinateSequenceFactorygeotools可以教会JTS如何来直接处理一些文件数据例如shapefile。
在JTS中还有些源码示例,可以用做学习。