简介
JTS由加拿大的VividSolutions公司开发,是一个用Java语言描述的几何拓扑套件,遵循OpenGIS的Simple Feature Specification,封装了2D几何类型和非常多的空间分析操作,而且包含了不少常见的计算几何算法实现。
JTS被广泛地应用在开源GIS软件中,作为GeoTools和基于GeoTools的GeoServer和uDig的底层库。
JTS官方文档
WKT 在线查看工具
Maven 依赖
<dependency><groupId>com.vividsolutions</groupId><artifactId>jts</artifactId><version>1.13</version>
</dependency>
几何模型
Point 点
Coordinate coord = new Coordinate(102.6806945800781 24.999259605696068);
Point point = new GeometryFactory().createPoint( coord );
或
WKTReader reader = new WKTReader(new GeometryFactory());
Point point = (Point)reader.read("POINT(102.6806945800781 24.999259605696068)");
MultiPoint 多点
WKTReader reader = new WKTReader(new GeometryFactory());
MultiPoint mpoint = (MultiPoint)reader.read("MULTIPOINT((102.6806945800781 24.999259605696068),(102.6811065673828 24.99851282550918))");
LineString 线
Coordinate[] coords = new Coordinate[]{new Coordinate(102.67990493826801, 24.999349063221004), new Coordinate(102.68184471235142, 24.9966108454865)};
LineString line = new GeometryFactory().createLineString(coords);
或
WKTReader reader = new WKTReader(new GeometryFactory());
LineString line = (LineString)reader.read("LINESTRING(102.67990493826801 24.999349063221004,102.68184471235142 24.9966108454865)");
MultiLineString 多线
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate[] coords1 = new Coordinate[] {new Coordinate(102.67990493826801, 24.999349063221004), new Coordinate(102.68184471235142, 24.9966108454865)};
LineString line1 = geometryFactory.createLineString(coords1);
Coordinate[] coords2 = new Coordinate[] {new Coordinate(102.68017959647113, 24.999504642938945), new Coordinate(102.68335533194477,24.997575452644412)};
LineString line2 = geometryFactory.createLineString(coords2);
LineString[] lineStrings = new LineString[2];
lineStrings[0]= line1;
lineStrings[1] = line2;
MultiLineString ms = geometryFactory.createMultiLineString(lineStrings);
或
WKTReader reader = new WKTReader(new GeometryFactory());
MultiLineString line = (MultiLineString)reader.read("MULTILINESTRING((102.67990493826801 24.999349063221004,102.68184471235142 24.9966108454865),(102.68017959647113 24.999504642938945,102.68335533194477 24.997575452644412))");
Polygon 面
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon = (Polygon)reader.read("POLYGON((102.68002510070801 24.99920904358541,102.68057441790008 24.99844670247988,102.68141555759941 24.99892900051202,102.68071174621583 24.999675779831406,102.68002510070801 24.99920904358541))");
MultiPolygon 多面
WKTReader reader = new WKTReader(new GeometryFactory());
MultiPolygon mpolygon = (MultiPolygon) reader.read("MULTIPOLYGON(((40 10, 30 0, 40 10, 30 20, 40 10),(30 10, 30 0, 40 10, 30 20, 30 10)))");
GeometryCollection 几何集合
GeometryFactory geometryFactory = new GeometryFactory();
LineString line = (LineString)reader.read("LINESTRING(102.67990493826801 24.999349063221004,102.68184471235142 24.9966108454865)");
Polygon polygon = (Polygon)reader.read("POLYGON((102.68002510070801 24.99920904358541,102.68057441790008 24.99844670247988,102.68141555759941 24.99892900051202,102.68071174621583 24.999675779831406,102.68002510070801 24.99920904358541))");
Geometry g1 = geometryFactory.createGeometry(line);
Geometry g2 = geometryFactory.createGeometry(polygon);
GeometryCollection gc = geometryFactory.createGeometryCollection(new Geometry[]{g1,g2});
Circle 圆
/*** create a Circle 创建一个圆,圆心(x,y) 半径RADIUS* @param x* @param y* @param RADIUS* @return*/public Polygon createCircle(double x, double y, final double RADIUS){GeometryFactory geometryFactory = new GeometryFactory();final int SIDES = 32;//圆上面的点个数Coordinate coords[] = new Coordinate[SIDES+1];for( int i = 0; i < SIDES; i++){double angle = ((double) i / (double) SIDES) * Math.PI * 2.0;double dx = Math.cos( angle ) * RADIUS;double dy = Math.sin( angle ) * RADIUS;coords[i] = new Coordinate( (double) x + dx, (double) y + dy );}coords[SIDES] = coords[0];LinearRing ring = geometryFactory.createLinearRing( coords );Polygon polygon = geometryFactory.createPolygon( ring, null );return polygon;}
几何操作
拓扑有效性检查
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon = (Polygon)reader.read("POLYGON((102.68002510070801 24.99920904358541,102.68057441790008 24.99844670247988,102.68141555759941 24.99892900051202,102.68071174621583 24.999675779831406,102.68002510070801 24.99920904358541))");
boolean valid = polygon.isValid();
System.out.println("valid: " + valid);
面积计算
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon = (Polygon)reader.read("POLYGON((102.68002510070801 24.99920904358541,102.68057441790008 24.99844670247988,102.68141555759941 24.99892900051202,102.68071174621583 24.999675779831406,102.68002510070801 24.99920904358541))");
double area = polygon.getArea();
长度/周长计算
WKTReader reader = new WKTReader(new GeometryFactory());
LineString line = (LineString)reader.read("LINESTRING(102.67990493826801 24.999349063221004,102.68184471235142 24.9966108454865)");
double length = line.getLength();
或
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon = (Polygon)reader.read("POLYGON((102.68002510070801 24.99920904358541,102.68057441790008 24.99844670247988,102.68141555759941 24.99892900051202,102.68071174621583 24.999675779831406,102.68002510070801 24.99920904358541))");
double length = polygon.getLength();
距离计算
几何之间的距离
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.6797633172391 24.999400600015505,102.68005084991454 24.99904665764305,102.68069887187445 24.99930336322602,102.68021821982254 24.999626189041066,102.6797633172391 24.999400600015505))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.68460845934166 24.99876272521388,102.68482732785922 24.998509908088764,102.68520498301947 24.998778283233165,102.68496894862618 24.998968867993526,102.68460845934166 24.99876272521388))");
double distance = polygon1.distance(polygon2); // 0.003946791560736727
几何之间的距离是否在指定范围内
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.6797633172391 24.999400600015505,102.68005084991454 24.99904665764305,102.68069887187445 24.99930336322602,102.68021821982254 24.999626189041066,102.6797633172391 24.999400600015505))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.68460845934166 24.99876272521388,102.68482732785922 24.998509908088764,102.68520498301947 24.998778283233165,102.68496894862618 24.998968867993526,102.68460845934166 24.99876272521388))");
double distance = polygon1.distance(polygon2); // 0.003946791560736727
boolean withinDistance = polygon1.isWithinDistance(polygon2, 0.00395); // true
空间关系
contains 包含
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.6804885866877 24.999426853935475,102.68218803458147 24.996984242282693,102.6862392441253 24.999426853935475,102.68407630972796 25.0016204947501,102.6804885866877 24.999426853935475))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.68181037876637 24.999302390976425,102.68265151977538 24.998088869480398,102.68426513829034 24.998866769330263,102.68316650390624 25.00009583951264,102.68181037876637 24.999302390976425))");
boolean contains = polygon1.contains(polygon2); // true
within 内含
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.6804885866877 24.999426853935475,102.68218803458147 24.996984242282693,102.6862392441253 24.999426853935475,102.68407630972796 25.0016204947501,102.6804885866877 24.999426853935475))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.68181037876637 24.999302390976425,102.68265151977538 24.998088869480398,102.68426513829034 24.998866769330263,102.68316650390624 25.00009583951264,102.68181037876637 24.999302390976425))");
boolean within = polygon2.within(polygon1); // true
covers 覆盖
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.6804885866877 24.999426853935475,102.68218803458147 24.996984242282693,102.6862392441253 24.999426853935475,102.68407630972796 25.0016204947501,102.6804885866877 24.999426853935475))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.68181037876637 24.999302390976425,102.68265151977538 24.998088869480398,102.68426513829034 24.998866769330263,102.68316650390624 25.00009583951264,102.68181037876637 24.999302390976425))");
boolean contains = polygon1.contains(polygon2); // true
coveredBy 被掩盖
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.6804885866877 24.999426853935475,102.68218803458147 24.996984242282693,102.6862392441253 24.999426853935475,102.68407630972796 25.0016204947501,102.6804885866877 24.999426853935475))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.68181037876637 24.999302390976425,102.68265151977538 24.998088869480398,102.68426513829034 24.998866769330263,102.68316650390624 25.00009583951264,102.68181037876637 24.999302390976425))");
boolean coveredBy = polygon2.coveredBy(polygon1); // true
intersects 相交
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.6800594337692 24.999333506401584,102.68167304966484 24.9970931497745,102.6822910316696 24.99741987072126,102.68047142002617 24.999582432724026,102.6800594337692 24.999333506401584))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.67961311314139 24.998508934467893,102.6800594337692 24.997871055927746,102.68199920654297 24.998897883678765,102.68134689409635 24.999566874509824,102.67961311314139 24.998508934467893))");
boolean intersects = polygon1.intersects(polygon2); // true
disjoint 脱节(不相交)
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.68004226658377 24.999489085070962,102.68153572134909 24.99757545240702,102.68210220310722 24.997995519624396,102.68066024858854 24.999738011078236,102.68004226658377 24.999489085070962))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.68148422241212 25.00015807208976,102.68198204066721 24.999489085070962,102.68464279331965 25.00098263176963,102.68387031712336 25.001573822062696,102.68148422241212 25.00015807208976))");
boolean disjoint = polygon1.disjoint(polygon2); // true
crosses 交叉
WKTReader reader = new WKTReader(new GeometryFactory());
LineString line1 = (LineString)reader.read("LINESTRING(102.68007659964496 24.999442411573924,102.68392181344099 24.9978243822909)");
LineString line2 = (LineString)reader.read("LINESTRING(102.68076324515278 25.00000249414873,102.68273735098774 24.997310964706003)");
boolean crosses = line1.crosses(line2); // true
overlaps 重叠
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67993927001955 24.99931794744367,102.6806774144643 24.99833779773074,102.68199920654297 24.999022348115815,102.6811065679067 24.99995582025325,102.67993927001955 24.99931794744367))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.68047142002615 24.998384470460365,102.68235969517264 24.99933350628288,102.68282318167621 24.998648957631303,102.68095207240549 24.9978088238541,102.68047142002615 24.998384470460365))");
boolean overlaps = polygon1.overlaps(polygon2); // true
touches 接触
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((1 0,6 0,6 5,1 5,1 0))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((6 0,6 1,7 1,7 0,6 0))");
boolean touches = polygon1.touches(polygon2); // true
equals 等于
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.68002510070801 24.99920904358541,102.68057441790008 24.99844670247988,102.68141555759941 24.99892900051202,102.68071174621583 24.999675779831406,102.68002510070801 24.99920904358541))");
boolean equals = polygon1.equals(polygon1); // true
relate 关系
测试两个几何体是否与 DE-9IM表达式(官方文档) 中的关系一致。
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.6800594337692 24.999333506401584,102.68167304966484 24.9970931497745,102.6822910316696 24.99741987072126,102.68047142002617 24.999582432724026,102.6800594337692 24.999333506401584))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.67961311314139 24.998508934467893,102.6800594337692 24.997871055927746,102.68199920654297 24.998897883678765,102.68134689409635 24.999566874509824,102.67961311314139 24.998508934467893))");
boolean relate = polygon1.relate(polygon2, "T*T***T**"); // true
空间分析
intersection 交叉分析
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67981910705566 24.999370456440957,102.68139839250942 24.99718455412153,102.68242836077113 24.997830216333284,102.68060016684466 24.999860529046316,102.67981910705566 24.999370456440957))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.67986202239989 24.999043740680463,102.68028259355924 24.99845253791706,102.68199920732876 24.999292666996425,102.6815443044179 25.00003166473097,102.67986202239989 24.999043740680463))");
Geometry intersection = polygon1.intersection(polygon2);
difference 差异分析
,
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67981910705566 24.999370456440957,102.68139839250942 24.99718455412153,102.68242836077113 24.997830216333284,102.68060016684466 24.999860529046316,102.67981910705566 24.999370456440957))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.67986202239989 24.999043740680463,102.68028259355924 24.99845253791706,102.68199920732876 24.999292666996425,102.6815443044179 25.00003166473097,102.67986202239989 24.999043740680463))");
Geometry difference = polygon1.difference(polygon2);
union 联合分析
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67981910705566 24.999370456440957,102.68139839250942 24.99718455412153,102.68242836077113 24.997830216333284,102.68060016684466 24.999860529046316,102.67981910705566 24.999370456440957))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.67986202239989 24.999043740680463,102.68028259355924 24.99845253791706,102.68199920732876 24.999292666996425,102.6815443044179 25.00003166473097,102.67986202239989 24.999043740680463))");
Geometry union = polygon1.union(polygon2);
symmetric difference 对称差异分析
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67981910705566 24.999370456440957,102.68139839250942 24.99718455412153,102.68242836077113 24.997830216333284,102.68060016684466 24.999860529046316,102.67981910705566 24.999370456440957))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.67986202239989 24.999043740680463,102.68028259355924 24.99845253791706,102.68199920732876 24.999292666996425,102.6815443044179 25.00003166473097,102.67986202239989 24.999043740680463))");
Geometry symDifference = polygon1.symDifference(polygon2);
Buffer 缓冲区分析
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67981910705566 24.999370456440957,102.68139839250942 24.99718455412153,102.68242836077113 24.997830216333284,102.68060016684466 24.999860529046316,102.67981910705566 24.999370456440957))");
Geometry buffer = polygon1.buffer(0.0005);
Convex hull 凸包分析
WKTReader reader = new WKTReader(new GeometryFactory());
MultiPoint multiPoint = (MultiPoint)reader.read("MULTIPOINT((102.6818618774414 24.999333506639005),(102.68241119463345 24.998571167492628),(102.68380165152485 24.998446703666872),(102.68666839599608 25.00062480449344),(102.68536377110284 25.00120043909439),(102.68320083670551 25.000095840462194))");
Geometry convexHull = multiPoint.convexHull();
精度操作
精度模型
精度模型是数值计算的核心,JTS适用于默认的双精度
模型。当使用较大的值时,Java中内置的数学并不是非常精确,可以通过配置 GeometryFactory
的 PrecisionModel
使用不同的精度。
通过显式捕获PrecisionModel JTS中的”round-off”过程,JTS允许管理这类错误,并对工作的速度和准确性进行适当的权衡。Round-off(例如 :6.0000001)经常发生即使是双精度模型。尤其是你的工作坐标系数字很大并且离着原点很远。这里面其实也是根据需要,如果你的使用精度要求不那么高那么可以减小精度模型。
精度操作(降低精度)
精度模型常量:
FIXED
固定精度表示坐标有固定的小数位数,小数点的位数由比例因子的对数10决定;FLOATING
浮动精度对应于标准的Java双精度
浮点表示,它是基于IEEE-754标准的;FLOATING_SINGLE
浮动单精度对应于标准的Java单精度
浮点表示,它是基于IEEE-754标准的。
PrecisionModel precisionModel = new PrecisionModel(PrecisionModel.FLOATING_SINGLE);
WKTReader reader = new WKTReader(new GeometryFactory(precisionModel));
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67981910705566 24.999370456440957,102.68139839250942 24.99718455412153,102.68242836077113 24.997830216333284,102.68060016684466 24.999860529046316,102.67981910705566 24.999370456440957))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.67986202239989 24.999043740680463,102.68028259355924 24.99845253791706,102.68199920732876 24.999292666996425,102.6815443044179 25.00003166473097,102.67986202239989 24.999043740680463))");
Geometry intersection = polygon1.intersection(polygon2);
System.out.println(intersection.toText());
使用 precisionModel 前:
POLYGON ((102.6804301249854 24.998524741369838, 102.67999762227275 24.99912337202302, 102.68082593508016 24.999609800656877, 102.68138303750645 24.99899110691649, 102.6804301249854 24.998524741369838))
使用 precisionModel 后:
POLYGON ((102.680428 24.998524, 102.679993 24.999123, 102.680824 24.999611, 102.681381 24.998991, 102.680428 24.998524))
几何结构
Delaunay三角剖分
从点的集合中创建三角剖分,并提取产生的三角形边缘 / 三角形作为几何图形。
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);
MultiPoint multiPoint = (MultiPoint)reader.read("MULTIPOINT((102.68194556236264 24.99925377243376), (102.6823854446411 24.998563388737196), (102.68381237983702 24.998495322528882), (102.68659114837645 25.000615081033544), (102.68534660339354 25.00118877085464))");DelaunayTriangulationBuilder builder = new DelaunayTriangulationBuilder();
builder.setSites(multiPoint);// 实际为GeometryCollection(组成的geometry紧密相连)
Geometry geometry = builder.getTriangles(geometryFactory);
System.out.println(geometry.toText());// 以0的距离进行缓冲(因为各多边形两两共边),生成一个多边形
// 此时则将点云构造成了多边形
Geometry buffer = geometry.buffer(0);
System.out.println(buffer.toText());
限定Delaunay三角剖分
从点的集合和线性约束中创建限定三角剖分,并提取产生的三角形边缘 / 三角形作为几何图形。
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);
MultiPoint multiPoint = (MultiPoint)reader.read("MULTIPOINT((102.68194556236264 24.99925377243376), (102.6823854446411 24.998563388737196), (102.68381237983702 24.998495322528882), (102.68659114837645 25.000615081033544), (102.68534660339354 25.00118877085464))");ConformingDelaunayTriangulationBuilder builder = new ConformingDelaunayTriangulationBuilder();
builder.setSites(multiPoint);// 实际为GeometryCollection(组成的geometry紧密相连)
Geometry geometry = builder.getTriangles(geometryFactory);
System.out.println(geometry.toText());// 以0的距离进行缓冲(因为各多边形两两共边),生成一个多边形
// 此时则将点云构造成了多边形
Geometry buffer = geometry.buffer(0);
System.out.println(buffer.toText());
泰森多边形图
泰森多边形(Voronoi diagram),是由荷兰气候学家A?H?Thiessen提出了一种根据离散分布的气象站的降雨量来计算平均降雨量的方法,即将所有相邻气象站连成三角形,作这些三角形各边的垂直平分线,于是每个气象站周围的若干垂直平分线便围成一个多边形。用这个多边形内所包含的一个唯一气象站的降雨强度来表示这个多边形区域内的降雨强度,并称这个多边形为泰森多边形。
从几何角度来看,两基站的分界线是两点之间连线的铅直等分线,将全平面分为两个半平面,各半平面中任何一点与本半平面内基站的间隔都要比到另一基站间隔小。当基站数量在二个以上时,全平面会划分为多个包罗一个基站的区域,区域中任何一点都与本区域内基站间隔最近,是以这些个区域可以看作是基站的覆盖区域,我们将这种由多个点将平面划分成的图称为泰森多边形,又称为Voronoi 图。
泰森多边形的特性是:
- 每个泰森多边形内仅含有一个基站;
- 泰森多边形区域内的点到相应基站的距离最近;
- 位于泰森多边形边上的点到其两边的基站的距离相等。
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);
MultiPoint multiPoint = (MultiPoint)reader.read("MULTIPOINT((102.68194556236264 24.99925377243376), (102.6823854446411 24.998563388737196), (102.68381237983702 24.998495322528882), (102.68659114837645 25.000615081033544), (102.68534660339354 25.00118877085464))");VoronoiDiagramBuilder builder = new VoronoiDiagramBuilder();
builder.setSites(multiPoint);Geometry geometry = builder.getDiagram(geometryFactory);
System.out.println(geometry.toText());Geometry buffer = geometry.buffer(0);
System.out.println(buffer.toText());
最小可穿过圆的直径
最小直径
最小封闭矩形
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);
Polygon multiPoint = (Polygon)reader.read("POLYGON((102.68198847770691 24.99927321980566,102.68244981765747 24.99862173117144,102.68380165100098 24.99857311247817,102.68649458885193 25.000624804612144,102.68589377403259 25.000935958721215,102.68534660339357 25.001091535480285,102.68315792083742 25.000012217657115,102.68198847770691 24.99927321980566))");MinimumDiameter diameter = new MinimumDiameter(multiPoint);
System.out.println(diameter.getLength());Geometry geometry = diameter.getMinimumRectangle();
System.out.println(geometry.toText());
最小边界圆
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);
Polygon multiPoint = (Polygon)reader.read("POLYGON((102.68198847770691 24.99927321980566,102.68244981765747 24.99862173117144,102.68380165100098 24.99857311247817,102.68649458885193 25.000624804612144,102.68589377403259 25.000935958721215,102.68534660339357 25.001091535480285,102.68315792083742 25.000012217657115,102.68198847770691 24.99927321980566))");MinimumBoundingCircle diameter = new MinimumBoundingCircle(multiPoint);
System.out.println(diameter.getRadius());Geometry geometry = diameter.getCircle();
System.out.println(geometry.toText());
度量函数
距离
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);Polygon multiPoint1 = (Polygon)reader.read("POLYGON((102.68017530441283 24.999487140693418,102.68065810203551 24.99887454806651,102.68160223960875 24.999312114540217,102.68085122108458 24.999924704985673,102.68017530441283 24.999487140693418))");
Polygon multiPoint2 = (Polygon)reader.read("POLYGON((102.68351197242735 25.001859692840426,102.68454194068907 25.00097485292946,102.68358707427979 25.0004692272621,102.68273949623106 25.00131517672628,102.68351197242735 25.001859692840426))");DistanceOp distance = new DistanceOp(multiPoint1, multiPoint2);
System.out.println(distance.distance());// 0.002221128437310254
豪斯多夫距离
豪斯多夫距离量度度量空间中真子集之间的距离。Hausdorff距离是另一种可以应用在边缘匹配算法的距离,它能够解决SED方法不能解决遮挡的问题。
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);Polygon multiPoint1 = (Polygon)reader.read("POLYGON((102.68017530441283 24.999487140693418,102.68065810203551 24.99887454806651,102.68160223960875 24.999312114540217,102.68085122108458 24.999924704985673,102.68017530441283 24.999487140693418))");
Polygon multiPoint2 = (Polygon)reader.read("POLYGON((102.68351197242735 25.001859692840426,102.68454194068907 25.00097485292946,102.68358707427979 25.0004692272621,102.68273949623106 25.00131517672628,102.68351197242735 25.001859692840426))");DiscreteHausdorffDistance distance = new DiscreteHausdorffDistance(multiPoint1, multiPoint2);
System.out.println(distance.distance());// 0.0033773571609631804
面积
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);Polygon multiPoint1 = (Polygon)reader.read("POLYGON((102.680025100708 24.999380180296086,102.68137693405153 24.99751322018369,102.68296480178833 24.998291123677703,102.6812481880188 25.000294202507845,102.680025100708 24.999380180296086))");
Polygon multiPoint2 = (Polygon)reader.read("POLYGON((102.68111944198608 24.99922460137013,102.68191337585448 24.99825222861996,102.68373727798462 24.998582836218347,102.68266439437866 24.999885810445036,102.68111944198608 24.99922460137013))");SimilarityMeasure similarityMeasure = new AreaSimilarityMeasure();
System.out.println(similarityMeasure.measure(multiPoint1, multiPoint2));// 0.210975468769302
豪斯多夫测度
豪斯多夫测度(Hausdorff measure)是由豪斯多夫(F.Hausdorff)提出和命名的一种测度。为了定量地描述非整数维,豪斯多夫于1919年从测量的角度引进了豪斯多夫测度。该测度是对长度、面积和体积等的推广,也是勒贝格测度的推广。
GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);Polygon multiPoint1 = (Polygon)reader.read("POLYGON((102.68017530441283 24.999487140693418,102.68065810203551 24.99887454806651,102.68160223960875 24.999312114540217,102.68085122108458 24.999924704985673,102.68017530441283 24.999487140693418))");
Polygon multiPoint2 = (Polygon)reader.read("POLYGON((102.68351197242735 25.001859692840426,102.68454194068907 25.00097485292946,102.68358707427979 25.0004692272621,102.68273949623106 25.00131517672628,102.68351197242735 25.001859692840426))");SimilarityMeasure similarityMeasure = new HausdorffSimilarityMeasure();
System.out.println(similarityMeasure.measure(multiPoint1, multiPoint2));// 0.36149546293844836
空间算法
线段相交算法
Coordinate p1 = new Coordinate(102.68048644065857, 24.999477417024806);
Coordinate p2 = new Coordinate(102.68146276473998, 24.998388361268198);
Coordinate q1 = new Coordinate(102.68021821975707, 24.999030127435674);
Coordinate q2 = new Coordinate(102.68193483352661, 24.998757863410333);RobustLineIntersector intersector = new RobustLineIntersector();
intersector.computeIntersection(p1, p2, q1, q2);
System.out.println(intersector.isInteriorIntersection()); // true
计算线段排列和线段序列结点
java doc
实现线绳编码的Snap Rounding算法
java doc
确定几何体中点的拓扑位置
java doc
数学函数
角度分析
Angle angle = new Angle();Coordinate c1 = new Coordinate(102.68048644065857, 24.999477417024806);
Coordinate c2 = new Coordinate(102.68021821975707, 24.999030127435674);
Coordinate c3 = new Coordinate(102.68193483352661, 24.998757863410333);// 返回两个无向最小差异角度,范围 [0, 180]
double radia1 = angle.angle(c1, c2);
double radia2 = angle.angle(c2, c3);
System.out.println(angle.toDegrees(angle.diff(radia1, radia2))); // 111.93701386769487// 返回两个向量之间的最小夹角,范围[0,180]
double ang1 = angle.angleBetween(c1, c2, c3);
System.out.println(angle.toDegrees(ang1)); //68.06298613230513// 从angle到angle按什么方向旋转
// 顺时针: -1
// 逆时针: 1
System.out.println(angle.getTurn(radia1, radia2)); // 1// 判断是否是锐角
System.out.println(angle.isAcute(c1, c2, c3)); // true// 判断是否是钝角
System.out.println(angle.isObtuse(c1, c2, c3)); // false// 角度 -> 弧度的转换
System.out.println(angle.toDegrees(Math.PI)); // 180.0// 弧度 -> 角度的转换
System.out.println(angle.toRadians(180)); // 3.141592653589793
Spatial structures 空间结构
空间索引
原图形:
待压占测试图形:
四叉树
四叉树是一种空间索引结构,用于对以二维矩形为界限的项目进行有效的范围查询。几何体可以通过使用它们的包络来进行索引。任何类型的对象也可以被索引,只要它的范围可以用一个包络来表示。
这个四叉树索引为范围矩形查询提供了一个主要过滤器。各种查询方法会返回一个可能与查询矩形相交的所有项目的列表。
注意,它可能会返回事实上没有与查询矩形相交的项目。需要一个二级过滤器来测试查询矩形和每个候选项目的包络之间的实际相交。二级过滤器可以显式执行,也可以由项目的后续操作隐式提供(例如,如果索引查询之后计算了查询几何体和树形项目之间的空间谓词,则自动执行包络线交叉检查。
这个实现不需要事先指定插入的项目的范围。它将自动扩展以适应数据集的任何范围。
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67994999885558 24.999389903972443,102.68048644065857 24.99870924477092,102.68125891685486 24.999088469648342,102.68058300018309 24.99976912674917,102.67994999885558 24.999389903972443))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.6807117462158 24.998971785195337,102.68134474754332 24.99820360978046,102.68229961395264 24.998650902378202,102.6815378665924 24.999496864361305,102.6807117462158 24.998971785195337))");
Polygon polygon3 = (Polygon)reader.read("POLYGON((102.68196702003478 24.998670349845497,102.68261075019836 24.997950791505005,102.68352270126343 24.99851477002082,102.68287897109984 24.999321838221945,102.68196702003478 24.998670349845497))");
Polygon polygon4 = (Polygon)reader.read("POLYGON((102.6810336112976 25.000371991318303,102.6815915107727 24.999827468610945,102.68239617347716 25.000274755297553,102.68178462982178 25.000877617385797,102.6810336112976 25.000371991318303))");
Polygon polygon5 = (Polygon)reader.read("POLYGON((102.68110871315001 24.9999927704022,102.6815915107727 24.99924404874666,102.68223524093628 24.99953575902505,102.68160223960876 25.000216413648175,102.68110871315001 24.9999927704022))");
Polygon polygon6 = (Polygon)reader.read("POLYGON((102.68248200416564 25.000760934631828,102.68208503723145 24.999856639531444,102.682728767395 24.99973995580791,102.68312573432921 25.000605357454162,102.68248200416564 25.000760934631828))");
Polygon polygon7 = (Polygon)reader.read("POLYGON((102.68091559410094 24.998398085023027,102.683265209198 24.998116095820905,102.68280386924742 24.997289572017735,102.68087267875671 24.997717420327533,102.68091559410094 24.998398085023027))");Quadtree quadtree = new Quadtree();
quadtree.insert(polygon1.getEnvelopeInternal(), polygon1);
quadtree.insert(polygon2.getEnvelopeInternal(), polygon2);
quadtree.insert(polygon3.getEnvelopeInternal(), polygon3);
quadtree.insert(polygon4.getEnvelopeInternal(), polygon4);
quadtree.insert(polygon5.getEnvelopeInternal(), polygon5);
quadtree.insert(polygon6.getEnvelopeInternal(), polygon6);
quadtree.insert(polygon7.getEnvelopeInternal(), polygon7);// 点
Point q1 = (Point)reader.read("POINT(102.68122673034668 24.998563388737182)");
for (Object o : quadtree.query(q1.getEnvelopeInternal())) {Polygon polygon = (Polygon) o;if (q1.intersects(polygon)) {System.out.println(polygon.toText());}
}System.out.println("--- --- --- ---");// 线
LineString q2 = (LineString)reader.read("LINESTRING(102.68004655838013 24.999321838221945,102.68317937850951 24.997804934571022)");
for (Object o : quadtree.query(q2.getEnvelopeInternal())) {Polygon polygon = (Polygon) o;if (q2.intersects(polygon)) {System.out.println(polygon.toText());}
}System.out.println("--- --- --- ---");// 面
Polygon q3 = (Polygon)reader.read("POLYGON((102.68004655838013 24.999380180296086,102.68182754516602 24.99687144609392,102.68300771713257 24.997610458389715,102.68107652664185 24.99990525771689,102.68004655838013 24.999380180296086))");
for (Object o : quadtree.query(q3.getEnvelopeInternal())) {Polygon polygon = (Polygon) o;if (q3.intersects(polygon)) {System.out.println(polygon.toText());}
}
STR树
使用Sort-Tile-Recursive(STR)算法创建的纯查询R树。用于二维空间数据。
STR打包的R树实现起来很简单,并能最大限度地利用空间;也就是说,尽可能多的叶子被填充到容量。节点之间的重叠要比基本的R树少得多。然而,一旦树被建立起来(明确地或在第一次调用#query时),项目就不能被添加或删除。
请注意:
- 向树中
插入
项目不是线程安全的
。在一个以上的线程上进行的插入必须在外部进行同步。 - 查询一棵树是线程安全的。
构建阶段是同步进行的,而查询是无状态的。
待压占测试图形:
WKTReader reader = new WKTReader(new GeometryFactory());
Polygon polygon1 = (Polygon)reader.read("POLYGON((102.67994999885558 24.999389903972443,102.68048644065857 24.99870924477092,102.68125891685486 24.999088469648342,102.68058300018309 24.99976912674917,102.67994999885558 24.999389903972443))");
Polygon polygon2 = (Polygon)reader.read("POLYGON((102.6807117462158 24.998971785195337,102.68134474754332 24.99820360978046,102.68229961395264 24.998650902378202,102.6815378665924 24.999496864361305,102.6807117462158 24.998971785195337))");
Polygon polygon3 = (Polygon)reader.read("POLYGON((102.68196702003478 24.998670349845497,102.68261075019836 24.997950791505005,102.68352270126343 24.99851477002082,102.68287897109984 24.999321838221945,102.68196702003478 24.998670349845497))");
Polygon polygon4 = (Polygon)reader.read("POLYGON((102.6810336112976 25.000371991318303,102.6815915107727 24.999827468610945,102.68239617347716 25.000274755297553,102.68178462982178 25.000877617385797,102.6810336112976 25.000371991318303))");
Polygon polygon5 = (Polygon)reader.read("POLYGON((102.68110871315001 24.9999927704022,102.6815915107727 24.99924404874666,102.68223524093628 24.99953575902505,102.68160223960876 25.000216413648175,102.68110871315001 24.9999927704022))");
Polygon polygon6 = (Polygon)reader.read("POLYGON((102.68248200416564 25.000760934631828,102.68208503723145 24.999856639531444,102.682728767395 24.99973995580791,102.68312573432921 25.000605357454162,102.68248200416564 25.000760934631828))");
Polygon polygon7 = (Polygon)reader.read("POLYGON((102.68091559410094 24.998398085023027,102.683265209198 24.998116095820905,102.68280386924742 24.997289572017735,102.68087267875671 24.997717420327533,102.68091559410094 24.998398085023027))");STRtree strTree = new STRtree();
strTree.insert(polygon1.getEnvelopeInternal(), polygon1);
strTree.insert(polygon2.getEnvelopeInternal(), polygon2);
strTree.insert(polygon3.getEnvelopeInternal(), polygon3);
strTree.insert(polygon4.getEnvelopeInternal(), polygon4);
strTree.insert(polygon5.getEnvelopeInternal(), polygon5);
strTree.insert(polygon6.getEnvelopeInternal(), polygon6);
strTree.insert(polygon7.getEnvelopeInternal(), polygon7);// 点
Point q1 = (Point)reader.read("POINT(102.68122673034668 24.998563388737182)");
for (Object o : strTree.query(q1.getEnvelopeInternal())) {Polygon polygon = (Polygon) o;if (q1.intersects(polygon)) {System.out.println(polygon.toText());}
}System.out.println("--- --- --- ---");// 线
LineString q2 = (LineString)reader.read("LINESTRING(102.68004655838013 24.999321838221945,102.68317937850951 24.997804934571022)");
for (Object o : strTree.query(q2.getEnvelopeInternal())) {Polygon polygon = (Polygon) o;if (q2.intersects(polygon)) {System.out.println(polygon.toText());}
}System.out.println("--- --- --- ---");// 面
Polygon q3 = (Polygon)reader.read("POLYGON((102.68004655838013 24.999380180296086,102.68182754516602 24.99687144609392,102.68300771713257 24.997610458389715,102.68107652664185 24.99990525771689,102.68004655838013 24.999380180296086))");
for (Object o : strTree.query(q3.getEnvelopeInternal())) {Polygon polygon = (Polygon) o;if (q3.intersects(polygon)) {System.out.println(polygon.toText());}
}
压覆图形同上!
KD树
一个2-D KD树的实现。KD树为点数据提供快速的范围搜索和快速查找。
这个实现支持检测和捕捉那些比给定距离公差更近的点。如果同一个点(在容差范围内)被插入超过一次,它就会被抢占到现有节点上。换句话说,如果一个点被插入,并且位于索引中已经存在的节点的公差范围内,那么它就会被捕捉到该节点。当一个点被卡在一个节点上时,一个新的节点不会被创建,但现有节点的计数会被增加。如果树中有多个节点在插入点的容差范围内,则会将最近的、然后是最低的节点套入。
请注意,KD树的结构取决于插入点的顺序。如果插入的点是连贯的(例如,在一个或两个维度上是单调的),那么一棵树可能会变得不平衡。一个完全平衡的树的深度只有log2(N),但一个不平衡的树可能会更深。这对查询效率有严重影响。更糟糕的是,由于递归被用于查询树,一个极深的树可能会导致StackOverflowException。解决这个问题的方法之一是在插入前随机化点的顺序(例如,通过使用Fisher-Yates洗牌)。
原图形:
待压占测试图形:
KdTree kdTree = new KdTree();
kdTree.insert(new Coordinate(102.68006801605225, 24.999321838221945));
kdTree.insert(new Coordinate(102.6806688308716, 24.998446703785575));
kdTree.insert(new Coordinate(102.68129110336305, 24.99778548696672));
kdTree.insert(new Coordinate(102.681999206543, 24.99702702799921));
kdTree.insert(new Coordinate(102.6829218864441, 24.997474324879704));
kdTree.insert(new Coordinate(102.68229961395265, 24.998291123677703));
kdTree.insert(new Coordinate(102.68161296844484, 24.99895233777569));
kdTree.insert(new Coordinate(102.68101215362549, 24.999944152251373));
kdTree.insert(new Coordinate(102.68195629119874, 25.000683146067615));
kdTree.insert(new Coordinate(102.68285751342775, 24.999827468610945));
kdTree.insert(new Coordinate(102.68347978591919, 24.998991232611843));
kdTree.insert(new Coordinate(102.68408060073854, 24.99807720070774));GeometryFactory geometryFactory = new GeometryFactory();
WKTReader reader = new WKTReader(geometryFactory);// 点 无重叠
Point q1 = (Point)reader.read("POINT(102.68122673034668 24.998563388737182)");
for (Object o : kdTree.query(q1.getEnvelopeInternal())) {KdNode node = (KdNode)o;Point point = (Point)reader.read("POINT(" + node.getX() + " " + node.getY() + ")");System.out.println(point.toText());
}System.out.println("--- --- --- ---");// 线
LineString q2 = (LineString)reader.read("LINESTRING(102.68004655838013 24.999321838221945,102.68317937850951 24.997804934571022)");
for (Object o : kdTree.query(q2.getEnvelopeInternal())) {KdNode node = (KdNode)o;Point point = (Point)reader.read("POINT(" + node.getX() + " " + node.getY() + ")");System.out.println(point.toText());
}System.out.println("--- --- --- ---");// 面
Polygon q3 = (Polygon)reader.read("POLYGON((102.68004655838013 24.999380180296086,102.68182754516602 24.99687144609392,102.68300771713257 24.997610458389715,102.68107652664185 24.99990525771689,102.68004655838013 24.999380180296086))");
for (Object o : kdTree.query(q3.getEnvelopeInternal())) {KdNode node = (KdNode)o;Point point = (Point)reader.read("POINT(" + node.getX() + " " + node.getY() + ")");System.out.println(point.toText());
}
Interval R树
包含用于实现一维区间的R树索引的包。
单调链
包含实现单调链的包。
可平面图
能在平面上画出没有相交的边的图,称为平面图(planar)。
这个类和这个包中的其他类作为一个框架,为特定的算法构建平面图。这个类必须被子类化以暴露适当的方法来构建图形。这允许控制可以被添加到图形中的图形组件(DirectedEdges、Edges和Nodes)的类型。一个使用图形框架的应用程序几乎总是为一个或多个图形组件提供子类,这些组件持有应用程序特定的数据和图形算法。
可平面图算法
在平面图上实现图算法的包。
输入/输出
WKT 导入 / 导出
GeometryFactory factory = new GeometryFactory();
// 导入
String wkt = "POLYGON((102.68004655838013 24.999380180296086,102.68182754516602 24.99687144609392,102.68300771713257 24.997610458389715,102.68107652664185 24.99990525771689,102.68004655838013 24.999380180296086))";
WKTReader reader = new WKTReader(factory);
Geometry geometry = reader.read(wkt);
System.out.println(geometry);
// 导出
WKTWriter writer = new WKTWriter(2);
String wkt2 = writer.write(geometry);
System.out.println(wkt2);
WKB 导入 / 导出
GeometryFactory factory = new GeometryFactory();
// 导入
byte[] wkb = {0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 5, 64, 89, -85, -123, -30, 0, 0, 0, 64, 56, -1, -41, 97, 38, -92, 108, 64, 89, -85, -93, 16, 0, 0, 0, 64, 56, -1, 50, -9, -109, 74, 36, 64, 89, -85, -74, 102, 0, 0, 0, 64, 56, -1, 99, 102, 36, -18, 112, 64, 89, -85, -106, -62, 0, 0, 0, 64, 56, -1, -7, -54, 124, -2, 20, 64, 89, -85, -123, -30, 0, 0, 0, 64, 56, -1, -41, 97, 38, -92, 108};
WKBReader reader = new WKBReader(factory);
Geometry geometry = reader.read(wkb);
System.out.println(geometry);
// 导出
WKBWriter writer = new WKBWriter(2);
byte[] bytes = writer.write(geometry);
System.out.println(new String(bytes, StandardCharsets.UTF_8));
GML(V2) 导入 / 导出
GeometryFactory factory = new GeometryFactory();
// 导入
String gml = "<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>102.68004655838013,24.999380180296086 102.68182754516602,24.99687144609392 102.68300771713257,24.997610458389715 102.68107652664185,24.99990525771689 102.68004655838013,24.999380180296086</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon>";
GMLReader reader = new GMLReader();
Geometry geometry = reader.read(gml, factory);
System.out.println(geometry);
// 导出
GMLWriter writer = new GMLWriter();
String write = writer.write(geometry);
System.out.println(write);
高精度算法
2x2双精度行列式的鲁棒性评估
执行一种算法,以稳健地计算双精度值的2x2行列式的符号。
DoubleDouble扩展精度算术
实施扩展精度的浮点数,保持106位(约30位小数)的精度。