JTS(Java Topology Suite) Java拓扑套件
JTS(Java Topology Suite) 可视化界面
学前小故事
项目需求是跟用户当前位置判断是否在给定的地理位置范围内,符合位置限制才可以打卡,其中的位置范围是一个或多个不规则的多边形。如下图,判断用户是在清华还是北大。
图形获取区域坐标
因为项目前端使用微信小程序的wx.getLocation获取地理位置,为了坐标的一致性,后台选取区域范围采用了腾讯地图的地理位置服务,在应用工具->绘制几何图形里,提供了点、线、多边形和圆形可以方便的选取看这里
在官方提供的示例上稍加改动即可获取选定的位置坐标。
存储位置
取到坐标位置后,接着就是怎么存储?
开放地理空间联盟(OGC)是一个由 250多家公司,机构和大学组成的国际联盟,参与开发公开可用的空间解决方案,这些解决方案可用于管理空间数据的各种应用程序。OGC发布了地理信息的 OpenGIS®Implementation 标准,该规范可从 OGC 网站http://www.opengeospatial.org/standards/sfs获得。
为了遵循 OGC 规范,MySQL 将空间 extensions 实现为具有 Geometry Types 环境的 SQL 的子集,提供生成、存储、分析空间的功能。总之,MySQL可以满足我们的需求。
MySQL提供单个的存储类型 POINT、LINESTRING、POLYGON 对应几何图形点、线、多边形,GEOMETRY 可以存储三种中的任何一种。
同时拥有存储多种类型的能力, MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION依次对应单个图形的复数。
回到项目中,我们用到的是 POLYGON ,建表语句 如下:
CREATE TABLE `polygon` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`polygon` polygon NOT NULL,
PRIMARY KEY (`id`),
SPATIAL KEY `d` (`polygon`)
) DEFAULT CHARSET=utf8;
插入数据
MySQL 支持将Well-Known 文本(WKT)格式和Well-Known 二进制(WKB)格式两种格式转换为object类型存储起来,我们使用更易于理解的WKT格式。
插入语句如下:
//GeomFromTextt()函数接受一个字符串,并且返回一个几何对象。
INSERT INTO `polygon` VALUES ('1', '清华大学', GeomFromText('POLYGON((
40.01169924229143 116.31565081888039,39.99304082299905 116.31616541796757,39.99343506780591 116.33297565023167,40.00237067000859 116.33743550702275,40.01340715321479 116.33057418815224,40.01169924229143 116.31565081888039))'));
INSERT INTO `polygon` VALUES ('2', '北京大学', GeomFromText('POLYGON((39.99711457525893 116.30450117461078,39.98673259872773 116.30535884106575,39.98673259872773 116.31702308311287,39.99963848242885 116.31598375134854,39.99711457525893 116.30450117461078))'));
需要注意的是腾讯地图返回的多边形的点不是闭合的,而polygon函数需要为了确定多边形是否闭合要求第一个点和最后一个点是一样的。如果不是闭合的polygon返回的结果将是NULL,插入语句就会执行失败。
如果几何满足诸如此(非穷举)列表中的条件,则它在语法上是 well-formed:
• 线串至少有两个点
• 多边形至少有一个环
• 多边形环关闭(第一个和最后一个点相同)
• 多边形环至少有 4 个点(最小多边形是一个三角形,第一个和最后一个点相同)
• 集合不为空(除了GeometryCollection)
查询判断
SELECT * FROM polygon WHERE
MBRWithin (ST_GeomFromText('POINT(39.991333490218544 116.30964748487895)'), polygon);
# 在北京大学
SELECT * FROM polygon WHERE
MBRWithin (ST_GeomFromText('POINT(39.988967560246685 116.3286905102832)'), polygon);
# 不在北大
细心的同学可能发现了这里的查询语句里用的是函数,在以往的SQL里如果存在查询字段上使用函数必然导致索引失效、全表扫描,但是在空间数据上不会,先看 EXPLAIN 语句和结果:
可见MySQL空间类型的数据同样可以建立索引,使用的关键词是 SPATIAL ,用法如下:
CREATE TABLE geom (g GEOMETRY NOT NULL);
CREATE SPATIAL INDEX g ON geom (g);
常用的空间计算函数
1、判断两点之间的距离
ST_Distance(g1,g2),返回g1和g2之间的距离。如果任一参数是NULL或空几何,则 return value 为NULL。
2、图形1是否完全包含图形2
ST_Contains(g1,g2),返回 1 或 0 以指示g1是否完全包含g2。还可以用ST_Within(g2,g1)达到相同的效果。
3、不相交
ST_Disjoint(g1,g2),返回 1 或 0 以指示g1是否在空间上与(不相交)g2不相交。
学前必备基础知识
-
地理信息系统GIS
地理信息系统(Geographic Information System或 Geo-Information system,GIS)有时又称为“地学信息系统”。它是一种特定的十分重要的空间信息系统。
它是在计算机硬、软件系统支持下,对整个或部分地球表层(包括大气层)空间中的有关地理分布数据进行采集、储存、管理、运算、分析、显示和描述的技术系统。
-
ArcGIS平台
ArcGIS产品线为用户提供一个可伸缩的,全面的GIS平台。ArcObjects包含了许多的可编程组件,从细粒度的对象(例如单个的几何对象)到粗粒度的对象(例如与现有ArcMap文档交互的地图对象)涉及面极广,这些对象为开发者集成了全面的GIS功能。
-
矢量化数据
矢量数据是在直角坐标中,用x、y坐标表示地图图形或地理实体的位置和形状的数据。矢量数据一般通过记录坐标的方式来尽可能地将地理实体的空间位置表现得准确无误。在arcgis中数据矢量化就是将栅格数据转为矢量数据。
arcgis作为地理信息行业的功能之一数据矢量化的详细步骤:
• 添加数据按钮打开栅格数据和待矢量用的GDB数据或者shp数据或者mdb数据(用于存矢量化后的独迅称数据)
• 编辑矢量数据,右键点击数据——编辑要素——开始编辑
• 使用鼠标根据栅格影像中的地物形状进行描绘,一般就是在拐点处添加节点(点击鼠标左键即添加节点),描绘完成后,双击即可
• 再对刚刚完成的数据进行赋属性,也就是在属性表中输入描绘的类型,如这里是“房子”。
• 再使用鼠标对下一个地物进行描绘,如图中的公路。以此循环对影像中各种地物进行描绘,这便是矢量化的过程 -
Geometry数据类型
Geometry是一种空间几何数据类型,常用于描述空间几何信息,例如坐标点、线、面、三维信息等。
也就是说,GIS一般使用Geometry数据类型来存储及展示地理信息。
常见的支持Geometry的数据库有Oracle、SqlServer、Mysql、PostgreSQL
-
实际运用特殊性
数据库中有geometry类型,但是很多语言中没有直接的geometry类型。
以java为例,如果想要存储geometry字段,需要增加SET @g1 = geomFromText(geometry) ,也就是将字符串转换成几何对象存储;同样,从数据库中直接取出的geometry类型字段,java无法使用,需要在数据库中查询的时候,做个几何类型转字符串类型的处理
st_AsText(geom) as geom
-
Geometry之间的关系
关系 说明 相等(Equals) 几何形状拓扑上相等 脱节(Disjoint) 几何形状没有共有的点 相交(Intersects) 几何形状至少有一个共有点(区别于脱节) 接触(Touches) 几何形状有至少一个公共的边界点,但是没有内部点 交叉(Crosses) 几何形状共享一些但不是所有的内部点 内含(Within) 几何形状A的线都在几何形状B内部 包含(Contains) 几何形状B的线都在几何形状A的内部(区别于内含) 重叠(Overlaps) 几何形状共享一部分但不是所有的公共点,而且相交处有他们自己相同的区域 -
关系分析
分析 说明 缓冲区分析(Buffer) 包含所有的点在一个指定距离内的多边形和多多边形 凸壳分析(ConvexHull) 包含几何形体的所有点的最小凸壳多边形(外包多边形) 交叉分析(Intersection) A∩B 交叉操作就是多边形AB中所有共同点的集合 联合分析(Union) 两个图形取交集 差异分析(Difference) 两个图形取差集 对称差异分析(SymDifference) (AUB-A∩B) AB形状的对称差异分析就是位于A中或者B中但不同时在AB中的所有点的集合
新增空间数据
当我们有了数据库,也有了arcgis提供的服务,现在该如何新增空间数据呢?
先通过WKT了解下空间数据Geometry大概长什么样
WKT,是一种文本标记语言,用于表示矢量几何对象、空间参照系统及空间参照系统之间的转换。
它的二进制表示方式,亦即WKB(well-known binary)则胜于在传输和在数据库中存储相同的信息。该格式由开放地理空间联盟(OGC)制定。
WKT可以表示的几何对象包括:点,线,多边形,TIN(不规则三角网)及多面体。可以通过几何集合的方式来表示不同维度的几何对象。几何物体的坐标可以是2D(x,y),3D(x,y,z),4D(x,y,z,m),加上一个属于线性参照系统的m值。
以下为几何WKT字串样例:
◈ POINT(6 10)
◈ LINESTRING(3 4,10 50,20 25)
◈ POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))
◈ MULTIPOINT(3.5 5.6, 4.8 10.5)
◈ MULTILINESTRING((3 4,10 50,20 25),(-5 -8,-10 -8,-15 -4))
◈ MULTIPOLYGON(((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2)),((6 3,9 2,9 4,6 3)))
◈ GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))
◈ POINT ZM (1 1 5 60)
◈ POINT M (1 1 80)
◈ POINT EMPTY
◈ MULTIPOLYGON EMPTY
到这里我们清楚了,所谓空间数据就是一个或一组坐标,这个坐标或坐标组有类型(POINT点LINESTRING线POLYGON面),通过这些坐标,GIS系统可以完整地定位查询绘制这些坐标信息
Java可以将WKT格式的Geomotry转换成GeoJSON
-
Meven添加依赖
<!-- 引入json处理包 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <!-- jts处理Geometry --> <dependency> <groupId>com.vividsolutions</groupId> <artifactId>jts</artifactId> <version>1.13</version> </dependency> <!-- https://mvnrepository.com/artifact/org.geotools/gt-geojson --> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-geojson</artifactId> <version>9.3</version> </dependency> <dependency> <groupId>com.googlecode.json-simple</groupId> <artifactId>json-simple</artifactId> <version>1.1.1</version> </dependency>
如添加依赖遇到not fount,可手动配置maven repository解决
<repositories> <repository> <id>OSGeo Repository</id> <url>http://download.osgeo.org/webdav/geotools/</url> </repository> </repositories>
-
解析方法源码
package com.bret.utils; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.WKTReader; import org.geotools.geojson.geom.GeometryJSON; import java.io.StringWriter; public class WKTUtil { /** * 由wkt格式的geometry生成geojson * @param wkt * @return */ public static String wktToJson(String wkt) { String json = null; try { WKTReader reader = new WKTReader(); Geometry geometry = reader.read(wkt); StringWriter writer = new StringWriter(); GeometryJSON g = new GeometryJSON(20); g.write(geometry, writer); json = writer.toString(); } catch (Exception e) { e.printStackTrace(); } return json; } }
POINT(6 10) --> {“type”:“Point”,“coordinates”:[6,10]}
LINESTRING(3 4,10 50,20 25) --> {“type”:“LineString”,“coordinates”:[[3,4],[10,50],[20,25]]}
GeoJSON格式的Geomotry
GeoJSON是一种对各种地理数据结构进行编码的格式。GeoJSON对象可以表示几何、特征或者特征集合。
GeoJSON支持下面几何类型:点、线、面、多点、多线、多面和几何集合。GeoJSON里的特征包含一个几何对象和其他属性,特征集合表示一系列特征。
GeoJSON 可以转 shp格式,GeoTools提供封装方法,详情可自行查询。(GeoJSON --> .shp --> .nc)
geojson格式规定
• 一个完整的GeoJSON数据结构总是一个(JSON术语里的)对象。
• geojson必须有一个名字为"type"的成员。这个成员的值是由GeoJSON对象的类型所确定的字符串。
即必须是下面之一:“Point”, “MultiPoint”, “LineString”, “MultiLineString”, “Polygon”, “MultiPolygon”, “GeometryCollection”, “Feature”, 或者 “FeatureCollection”。这些值分别对应:点、多点、线、多线、面、多面、几何集合、特征、特征集合。
• 一个features下的Feature要素,包括图形(geometry)和属性(type)在一起的,该属性是必须的
• 一个features下的Feature要素里,geometry里面也是一个对象,对象里面的type就是点线面、多点、多线、多面六种
• 一个features下的Feature要素里,properties表示该对象的属性,该属性不是必须的
geojson示例
-
点坐标
//点坐标是按照x,y顺序的(投影坐标的东向、北向,地理坐标的长度、高度) { "type": "Point", "coordinates": [100.0, 0.0] }
-
线坐标
//线的坐标是位置数组,二维数组,两点确定一条直线 { "type": "LineString","coordinates": [ [100.0, 0.0], [101.0, 1.0] ]}
-
面坐标(没有孔)
//面的坐标是线性环坐标数组的数组。这个数组的第一个元素表示的是外部环。其他后续的元素表示的内部环(或者孔) { "type": "Polygon", "coordinates": [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] ] }
-
面坐标(有孔)
//一个洞的面是三维数组 { "type": "Polygon", "coordinates": [ // 最大的面 [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ], // 面里面的洞 [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ] ] }
-
多面的坐标
//多面的坐标是面坐标数组的数组 { "type": "MultiPolygon", "coordinates": [ // 无孔面 [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], // 有孔面 [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]] ] }
-
几何集合
{ //type是由GeoJSON对象的类型所确定的字符串,类型有:"Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "GeometryCollection", "Feature", "FeatureCollection" "type": "FeatureCollection", //特征对象的结合数组 "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [102.0, 0.5]}, "properties": { "prop0": "value0"} }, { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [102.0,