从零开始,构建电子地图网站:0_8_mybatis+TypeHandler+jts处理geometry

时间:2024-04-05 16:55:17

接上文,我们的数据里是有几何类型的,点和面。

我们在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)"

}

 

从零开始,构建电子地图网站:0_8_mybatis+TypeHandler+jts处理geometry

 

九、提交git,从git上pull分支

处理geometry对象就到这里了。

把程序提交到git上。

本例程序在https://github.com/yimengyao13/gismap.git上,但是为了不和之前的程序冲突,所以新建了一个分支geometry。

 

切换新分支,右下角Git:master,点开Git Branchs——+New Branch;

写上新分支名称,也就是geometry,勾选Checkout branch,点击ok。

从零开始,构建电子地图网站:0_8_mybatis+TypeHandler+jts处理geometry

 

这样就可以在线上pull代码下来了。

VCS——Git——Pull

从零开始,构建电子地图网站:0_8_mybatis+TypeHandler+jts处理geometry

 

先刷新下,选择分支,pull。

从零开始,构建电子地图网站:0_8_mybatis+TypeHandler+jts处理geometry

 

这样就可以看代码了。

 

接下来要把这个工程完善,因为三张表的其他属性都要加载进来,而且web显示,不能仅仅读数据库,这个太慢了,要把数据加载到内存中,以便于快速读取。

一样一样来。

先把整个增删改查完善后,再进行内存加载。