接上文,我們的資料裡是有幾何類型的,點和面。
我們在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顯示,不能僅僅讀資料庫,這個太慢了,要把資料加載到記憶體中,以便于快速讀取。
一樣一樣來。
先把整個增删改查完善後,再進行記憶體加載。