接上文,我们的数据里是有几何类型的,点和面。
我们在navicat中运行一条sql:SELECT geom FROM v6_time_cnty_pts_utf_wgs84 LIMIT 1
查出的结果geom是0101000020E6100000A165DD3F16C55B4089963C9E96814340
这种格式是geohash编码的。
再运行另一条sql:SELECT st_astext(geom) FROM v6_time_cnty_pts_utf_wgs84 LIMIT 1
POINT(111.079483 39.012409)
这种是WKT格式。
都是空间数据的存储格式,WKT比geohash编码要直观。
在mybatis中,直接查geometry对象,返回的是字符类型的geohash,但是交互的时候,我们不能返一串geohash码。对于后端来说,我们应该直接操作geometry类,给前端提供json数组。
还用之前的程序,看看怎么对geometry对象进行增删改查。
Springboot2+mybatis+postgresql+typehandler+jts;
一、引入依赖
Jts是空间处理jar包,功能很全很强大,这个jar包的maven依赖,我们之前已经在pom中引用了。
再加一个解析json的jar包。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.history</groupId> <artifactId>gismap</artifactId> <version>0.0.1-SNAPSHOT</version> <name>gismap</name> <description>Demo project for Spring Boot</description> <properties> <!-- 标注一下编码为utf8,jdk版本为1.8--> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.2</version> <!-- <scope>runtime</scope>--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> <!--解析json的--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-joda</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-parameter-names</artifactId> </dependency> <!-- 分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> <!-- alibaba的druid数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <!-- 解析几何geometry对象用的--> <dependency> <groupId>com.vividsolutions</groupId> <artifactId>jts</artifactId> <version>1.13</version> </dependency> <!--引入alibaba的json处理jar包--> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
二、增加mybatis自定义类
新建一个包:D:\gismap\java\gismap\src\main\java\com\history\gismap\mybatis
在这个package包下新建一个类,D:\gismap\java\gismap\src\main\java\com\history\gismap\mybatis\GeometryTypeHandler.java,扩展mybatis的typehandler,WKBReader.hexToBytes(pGgeometry.getValue())就是从解码geohash,获取geometry类。
package com.history.gismap.mybatis; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKBReader; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedTypes; import org.postgresql.util.PGobject; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @MappedTypes(Geometry.class) public class GeometryTypeHandler extends BaseTypeHandler<Geometry> { public void setNonNullParameter(PreparedStatement preparedStatement, int i, Geometry geometry, JdbcType jdbcType) throws SQLException { PGobject pGobject=new PGobject(); pGobject.setValue(geometry.toString()); pGobject.setType("geometry"); preparedStatement.setObject(i,pGobject); } public Geometry getNullableResult(ResultSet resultSet, String columnName) throws SQLException { PGobject pGgeometry= (PGobject) resultSet.getObject(columnName); if(pGgeometry==null){ return null; }else{ WKBReader wkbReader=new WKBReader(); try { return wkbReader.read(WKBReader.hexToBytes(pGgeometry.getValue())); } catch (ParseException e) { e.printStackTrace(); return null; } } } public Geometry getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException { PGobject pGgeometry= (PGobject) resultSet.getObject(columnIndex); if(pGgeometry==null){ return null; }else{ WKBReader wkbReader=new WKBReader(); try { return wkbReader.read(WKBReader.hexToBytes(pGgeometry.getValue())); } catch (ParseException e) { e.printStackTrace(); return null; } } } public Geometry getNullableResult(CallableStatement callableStatement, int i) throws SQLException { PGobject pGgeometry= (PGobject) callableStatement.getObject(i); if(pGgeometry==null){ return null; }else{ WKBReader wkbReader=new WKBReader(); try { return wkbReader.read(WKBReader.hexToBytes(pGgeometry.getValue())); } catch (ParseException e) { e.printStackTrace(); return null; } } } }
三、修改model
加一个geometry属性。
D:\gismap\java\gismap\src\main\java\com\history\gismap\model\PointModel.java
package com.history.gismap.model; import com.vividsolutions.jts.geom.Geometry; import lombok.Getter; import lombok.Setter; import lombok.ToString; @Getter @Setter @ToString public class PointModel { private Integer gId; private String nameCh; private Geometry geometry; }
四、修改dao
这个文件没啥要改的。
D:\gismap\java\gismap\src\main\java\com\history\gismap\dao\MapDao.java
package com.history.gismap.dao; import com.history.gismap.model.PointModel; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Service; import java.util.List; @Service public interface MapDao { List<PointModel> getCntyPoint(@Param("gId") Integer gId); int insertCntyPoint(PointModel pointModel); int updateCntyPoint(PointModel pointModel); int deleteCntyPoint(@Param("gId") Integer gId); }
五、修改mapper
主要是加上typeHandler="com.history.gismap.mybatis.GeometryTypeHandler"
D:\gismap\java\gismap\src\main\resources\mapper\HistoryGISMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.history.gismap.dao.MapDao" > <resultMap id="pointModelResult" type="com.history.gismap.model.PointModel"> <result property="gId" column="gid" jdbcType="BIGINT"/> <result property="nameCh" column="name_ch" jdbcType="VARCHAR"/> <result property="geometry" column="geom" typeHandler="com.history.gismap.mybatis.GeometryTypeHandler"/> </resultMap> <sql id="BASE_TABLE"> v6_time_cnty_pts_utf_wgs84 </sql> <sql id="BASE_COLUMN"> gid,name_ch,geom </sql> <select id="getCntyPoint" resultMap="pointModelResult"> SELECT <include refid="BASE_COLUMN"></include> FROM <include refid="BASE_TABLE"/> WHERE gid=#{gId} </select> <insert id="insertCntyPoint" parameterType="com.history.gismap.model.PointModel"> INSERT INTO <include refid="BASE_TABLE"/> <trim prefix="(" suffix=")" suffixOverrides=","> <if test="gId != null"> gid, </if> <if test="nameCh != null"> name_ch, </if> <if test="geometry != null"> geom, </if> </trim> <trim prefix="VALUES(" suffix=")" suffixOverrides=","> <if test="gId != null"> #{gId, jdbcType=BIGINT}, </if> <if test="nameCh != null"> #{nameCh, jdbcType=VARCHAR}, </if> <if test="geometry != null"> #{geometry,typeHandler=com.history.gismap.mybatis.GeometryTypeHandler} </if> </trim> </insert> <update id="updateCntyPoint" parameterType="com.history.gismap.model.PointModel"> UPDATE <include refid="BASE_TABLE"/> SET name_ch=#{nameCh}, geom=#{geometry,typeHandler=com.history.gismap.mybatis.GeometryTypeHandler} WHERE gid=#{gId} </update> <delete id="deleteCntyPoint" parameterType="com.history.gismap.model.PointModel"> DELETE FROM <include refid="BASE_TABLE"/> WHERE gid=#{gId} </delete> </mapper>
六、修改service
Service没改。
D:\gismap\java\gismap\src\main\java\com\history\gismap\service\MapService.java
package com.history.gismap.service; import com.history.gismap.model.PointModel; import org.springframework.stereotype.Service; import java.util.List; public interface MapService { List<PointModel> getCntyPointByGid(Integer gId); int addCntyPoint(PointModel pointModel); int modifyCntyPoint(PointModel pointModel); int removeCntyPoint(Integer gId); }
impl也没有变动。
D:\gismap\java\gismap\src\main\java\com\history\gismap\service\impl\MapServiceImpl.java
package com.history.gismap.service.impl; import com.history.gismap.dao.MapDao; import com.history.gismap.model.PointModel; import com.history.gismap.service.MapService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class MapServiceImpl implements MapService { @Autowired private MapDao mapDao; @Override public List<PointModel> getCntyPointByGid(Integer gId){ return mapDao.getCntyPoint(gId); } @Override public int addCntyPoint(PointModel pointModel){ return mapDao.insertCntyPoint(pointModel); } @Override public int modifyCntyPoint(PointModel pointModel){ return mapDao.updateCntyPoint(pointModel); } @Override public int removeCntyPoint(Integer gId){ return mapDao.deleteCntyPoint(gId); } }
七、修改controller
为了便于前端读写,我们要把geometry对象转化成geojson。
这个改动比较大,查询返回结果、增加入参、修改入参都改成了json格式,WKTReader用来读取WKT文本,mybatis自定义引擎中引入的WKBReader是用来读geohash文本的。
package com.history.gismap.controller; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; import com.history.gismap.model.PointModel; import com.history.gismap.service.MapService; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping(value = "/history") public class MapController { @Autowired private MapService mapService; @ResponseBody @GetMapping("/pointmodel") public JSONObject getPoint(@RequestParam("gid") Integer gId){ PointModel pointModel=mapService.getCntyPointByGid(gId).get(0); JSONObject jsonObject=new JSONObject(); jsonObject.put("gid",pointModel.getGId()); jsonObject.put("namech",pointModel.getNameCh()); JSONObject geometry=new JSONObject(); geometry.put("type",pointModel.getGeometry().getGeometryType()); JSONArray coordinateArray=new JSONArray(); Coordinate[] coordinates=pointModel.getGeometry().getCoordinates(); JSONObject coor=new JSONObject(); coor.put("longitude",coordinates[0].x); coor.put("latitude",coordinates[0].y); coordinateArray.add(coor); geometry.put("coordinate",coordinateArray); jsonObject.put("geometry",geometry); return jsonObject; } @ResponseBody @PostMapping("/add") public int addPoint(@RequestBody JSONObject request){ PointModel pointModel=new PointModel(); pointModel.setGId((Integer) JSONPath.eval(request,"$.gId")); pointModel.setNameCh((String) JSONPath.eval(request,"$.nameCh")); String pointStr= (String) JSONPath.eval(request,"$.point"); GeometryFactory geometryFactory = new GeometryFactory(); WKTReader reader = new WKTReader( geometryFactory ); try { Geometry point = (Point) reader.read(pointStr); pointModel.setGeometry(point); } catch (ParseException e) { e.printStackTrace(); } return mapService.addCntyPoint(pointModel); } @ResponseBody @PostMapping("/modify") public int update(@RequestBody JSONObject request){ PointModel pointModel=new PointModel(); pointModel.setGId((Integer) JSONPath.eval(request,"$.gId")); pointModel.setNameCh((String) JSONPath.eval(request,"$.nameCh")); String pointStr= (String) JSONPath.eval(request,"$.point"); GeometryFactory geometryFactory = new GeometryFactory(); WKTReader reader = new WKTReader( geometryFactory ); try { Geometry point = (Point) reader.read(pointStr); pointModel.setGeometry(point); } catch (ParseException e) { e.printStackTrace(); } return mapService.modifyCntyPoint(pointModel); } @ResponseBody @GetMapping("/remove") public int removetPoint(@RequestParam("gid") Integer gId){ return mapService.removeCntyPoint(gId); } }
八、启动测试
启动工程D:\gismap\java\gismap\src\main\java\com\history\gismap\GismapApplication.java,用postman看下。
先看下查询的结果:
访问http://localhost:8080/history/pointmodel?gid=1
{
"gid": 1,
"geometry": {
"coordinate": [
{
"latitude": 39.012409,
"longitude": 111.079483
}
],
"type": "Point"
},
"namech": "保德州"
}
新增结果:
http://localhost:8080/history/add
post一下。
{
"gId":14357,
"nameCh":"test",
"point":"POINT (109.013388 32.715519)"
}
修改结果:
http://localhost:8080/history/modify
{
"gId":14357,
"nameCh":"test",
"point":"POINT (0 0)"
}
九、提交git,从git上pull分支
处理geometry对象就到这里了。
把程序提交到git上。
本例程序在https://github.com/yimengyao13/gismap.git上,但是为了不和之前的程序冲突,所以新建了一个分支geometry。
切换新分支,右下角Git:master,点开Git Branchs——+New Branch;
写上新分支名称,也就是geometry,勾选Checkout branch,点击ok。
这样就可以在线上pull代码下来了。
VCS——Git——Pull
先刷新下,选择分支,pull。
这样就可以看代码了。
接下来要把这个工程完善,因为三张表的其他属性都要加载进来,而且web显示,不能仅仅读数据库,这个太慢了,要把数据加载到内存中,以便于快速读取。
一样一样来。
先把整个增删改查完善后,再进行内存加载。