GeTools 中的 JTS基础--1

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yatsov/article/details/80215278

简介

以下文档是我对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

Maven 坐标

<dependency>
  <groupId>com.vividsolutions</groupId>
  <artifactId>jts</artifactId>
  <version>1.13</version>
</dependency>

Geometry

我们使用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提供的容差。

建立一个点 Creating a Point

下面我们使用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

创造一个线 Creating a LineString

下面的代码用于创造线:

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 表达洞,另外可以运用它表达您想要的形状。

Creating CircularString 创建一个环

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组件。

创建一个自定义曲线 Custom Curves

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<Coordinate> coords = new ArrayList<Coordinate>();
    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

使用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 asnot” 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.

Geometries Enum

用几何枚举来判断几何类型。

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);
    }
}

PrecisionModel 精度模型

“开箱即用”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

CoordinateSequence

你也许希望提供一个自定义的CoordinateSequenceFactory来减小内存使用使得程序更有效率。

程序内部的几何通常用Coordinate[]工作,但是许多被JTS所用的空间格式会将值存储在double[] or float[]这样的一维数组中,这样会效率更高。通过实现一个CoordinateSequenceFactorygeotools可以教会JTS如何来直接处理一些文件数据例如shapefile。

源码示例

在JTS中还有些源码示例,可以用做学习。

猜你喜欢

转载自blog.csdn.net/yatsov/article/details/80215278