【GIS系列】GeoTools+MybatisPlus+PostGIS实现Shp数据的读取及写入数据库

本文将基于开源GIS后端技术展示如何实现Shp数据的读取及写入数据库。

作者:后端小肥肠

姊妹篇:juejin.cn/post/734683...

1. 前言

在当前的软件开发和数据处理领域,地理信息系统(GIS)的应用变得越来越广泛。而对于GIS数据的读取和处理,GeoTools是一个强大而灵活的Java库,提供了丰富的功能和工具。与此同时,MyBatisPlus作为一种优秀的ORM框架,简化了与数据库的交互过程,提高了开发效率。而PostGIS是一个基于PostgreSQL数据库的空间数据库扩展,提供了许多地理空间函数和数据类型,使得数据库能够有效地存储和处理地理空间数据。在这篇博客中,我们将结合GeoTools、MyBatisPlus和PostGIS,探讨如何实现Shp数据的读取和写入数据库的过程。通过结合这三者的强大功能,我们将能够高效地处理GIS数据,并将其存储到数据库中,为地理信息系统的开发提供了可靠而高效的解决方案。

2. PostGIS简介

PostGIS是一个开源的空间数据库扩展,为PostgreSQL数据库提供了地理空间功能。它扩展了PostgreSQL的数据类型,使得数据库能够存储地理空间数据,并提供了一系列的地理空间函数和索引,以支持地理信息系统(GIS)应用的开发和分析。PostGIS允许用户在数据库中存储地理要素(如点、线、面)以及地理空间对象的属性信息,同时还提供了强大的查询和分析功能,如空间关系查询、空间分析、几何运算等。 主要特点包括:

  1. 空间数据类型支持: PostGIS扩展了PostgreSQL的数据类型系统,引入了几何类型(Geometry)和地理类型(Geography),可以存储各种地理空间对象,如点、线、面等。
  2. 空间索引支持: PostGIS提供了多种空间索引的实现,如R树索引、GiST索引和SP-GiST索引等,可以加快空间查询的速度,提高数据检索效率。
  3. 地理空间函数: PostGIS内置了大量的地理空间函数,用于进行空间分析、空间关系判断、几何操作等,如距离计算、相交判断、缓冲区分析等。
  4. 标准兼容性: PostGIS符合Open Geospatial Consortium(OGC)的空间数据规范,保证了与其他GIS软件的互操作性,可以轻松地导入和导出地理空间数据。

通过结合PostGIS与GeoTools和MyBatisPlus,我们能够实现更加灵活、高效的GIS数据处理和管理,为地理信息系统的开发提供了强大的支持和工具。

3. 开发环境搭建

3.1. 所需版本和工具

依赖 版本
Spring Boot 2.6.14
GeoTools 27-SNAPSHOT
java 1.8以上
ArcGis 10.8
postgres 13.12

3.2. pom依赖

xml 复制代码
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <mybatis-plus-generator.version>3.3.2</mybatis-plus-generator.version>
        <mybatis-plus-boot-starter.version>3.3.1</mybatis-plus-boot-starter.version>
        <geotools.version>27-SNAPSHOT</geotools.version>
        </properties>
     <dependencies>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-shapefile</artifactId>
            <version>${geotools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geojson</artifactId>
            <version>${geotools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-swing</artifactId>
            <version>${geotools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus-generator.version}</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>net.postgis</groupId>
            <artifactId>postgis-jdbc</artifactId>
            <version>2021.1.0</version>
        </dependency>
     </dependencies>

4. 代码实现

4.1. Shp数据准备

Shp数据可以去网上找,也可以自己做,从来没接触过GIS相关知识的同学我建议就网上找一下,有一些GIS基础的我建议就自己做了,比网上找快得多。我是基于AcrMap(ArcGis)自己做了一份Shp数据(4326的几个面要素)。

4.2. 表设计实体类编写

1.PostGis建立空间表

建立空间表的前提是安装PostGis扩展:

ini 复制代码
create extension postgis;

安装扩展成功后,会在数据库下面生成一张spatial_ref_sys表:

建表sql语句:

sql 复制代码
CREATE TABLE "xfc_geo_data" (
  "id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
  "extent" "public"."geometry",
  "center" "public"."geometry",
  CONSTRAINT "geo_data_pkey" PRIMARY KEY ("id")
);


COMMENT ON COLUMN "public"."xfc_geo_data"."id" IS 'id';

COMMENT ON COLUMN "public"."xfc_geo_data"."extent" IS '数据空间范围';

COMMENT ON COLUMN "public"."xfc_geo_data"."center" IS '数据中心点';

2. 基于MybatisPlus的代码生成器创建实体类代码

代码生成器编写:

java 复制代码
public class CodeGenerator {

    // 生成的代码放到哪个工程中

      private static String PROJECT_NAME="xfc-gis";
    // 数据库名称
    private static String DATABASE_NAME = "xfc_gis";

    // 子包名
    private static String MODULE_NAME = "gis";

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:postgresql://127.0.0.1:5432/"+DATABASE_NAME);
        dsc.setDriverName("org.postgresql.Driver");
        dsc.setUsername("postgres");
        dsc.setPassword("postgres");
        mpg.setDataSource(dsc);

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir") + "/";
        gc.setOutputDir(projectPath + PROJECT_NAME +"/src/main/java");
        // 分布式id
        gc.setIdType(IdType.ASSIGN_ID); 
        gc.setAuthor("xfc");
        //覆盖现有的
        gc.setFileOverride(false);
        //是否生成后打开
        gc.setOpen(false); 
        gc.setDateType(DateType.ONLY_DATE);
        //实体属性 Swagger2 注解
        gc.setSwagger2(true); 
        mpg.setGlobalConfig(gc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //父包名
        pc.setParent("com.xfc"); 
        pc.setController(MODULE_NAME+".controller"); 
        pc.setService(MODULE_NAME+".service");
        pc.setServiceImpl(MODULE_NAME+".service.impl");
        pc.setMapper(MODULE_NAME+".mapper");
        pc.setXml(MODULE_NAME+".mapper.xml");
        pc.setEntity(MODULE_NAME+".entities");
        mpg.setPackageInfo(pc);


        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //使用lombok
        strategy.setEntityLombokModel(true);
        // 实体类的实现接口Serializable
        strategy.setEntitySerialVersionUID(true);
        // @RestController
        strategy.setRestControllerStyle(true); 
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }


    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }
}

代码生成器生成的实体类

less 复制代码
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class XfcGeoData implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;

    private String extent;

    private String center;

}

4.3. Handler类编写及适配属性字段

1. 编写PgGeometry84TypeHandler 类

这个类主要是用于适配PostGis的geometry的数据类型。

java 复制代码
@MappedTypes({String.class})
public class PgGeometry84TypeHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        PGgeometry pGgeometry = new PGgeometry(parameter);
        Geometry geometry = pGgeometry.getGeometry();
        geometry.setSrid(4326);
        ps.setObject(i, pGgeometry);
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String string = rs.getString(columnName);
        return getResult(string);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String string = rs.getString(columnIndex);
        return getResult(string);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String string = cs.getString(columnIndex);
        return getResult(string);
    }

2. 适配实体类空间字段

4.4. 读取数据及写入数据库

读取数据主要是基于GeoTools相关函数方法。

java 复制代码
public void insertGeoData(AddGeoDTO addGeoDTO) throws IOException, ParseException {
        String shpPath=addGeoDTO.getShpPath();
        List<Map<String, Object>> proMaps = getProMapByFeatureCollection(shpPath);
        for (Map<String, Object> proMap : proMaps) {
            XfcGeoData xfcGeoData=new XfcGeoData();
            xfcGeoData.setExtent(proMap.get("the_geom").toString());
            xfcGeoData.setCenter(GisUtil.calculateCenter(proMap.get("the_geom").toString()));
            baseMapper.insert(xfcGeoData);
        }
    }
    /**
     * 根据featureCollection获取属性Map
     */
    public List<Map<String, Object>> getProMapByFeatureCollection(String shpPath) throws IOException {
        File file = new File(shpPath);
        ShapefileDataStore dataStore = (ShapefileDataStore) FileDataStoreFinder.getDataStore(file);
        dataStore.setCharset(Charset.forName("GBK"));
        SimpleFeatureSource simpleFeatureSource = dataStore.getFeatureSource();
        SimpleFeatureCollection simpleFeatureCollection = simpleFeatureSource.getFeatures();
        SimpleFeatureIterator featureIterator = simpleFeatureCollection.features();
        List<Map<String, Object>>properMaps=new ArrayList<>();
        while (featureIterator.hasNext()) {
            // 要素对象
            SimpleFeature feature = featureIterator.next();
            Collection<Property> propertyCollection = (Collection<Property>) feature.getValue();
            //填充属性map
            Map<String, Object> properMap = new HashMap<>();
            for (Property property : propertyCollection) {
                properMap.put(property.getName().toString(), property.getValue());
            }
            properMaps.add(properMap);
        }
        return properMaps;
    }

写入数据库成功(wkb格式): wkt格式:

4.6. 数据正确性验证

验证空间数据的正确性有很多方法,我用的是的一个JTS的工具:

由上图可知我们解析插入到PostGis数据库中的数据是没问题的,我这里只是提供一个宽泛的验证,详细的验证需要根据地理要素的id对比验证解析图形的正确性。

5. 结语

本文结合具体技术栈讲解了如何实现Shp空间数据的读取到写入数据库,下期会给大家分享一下空间数据的存储方案,涵盖2/3维数据,对GIS系列感兴趣的同学可动动小手点点关注哦~

6. 参考链接

基于Mybatis-Plus实现Geometry字段在PostGis空间数据库中的使用_mybatisplus postgis-CSDN博客

相关推荐
java小吕布9 分钟前
Java Lambda表达式详解:函数式编程的简洁之道
java·开发语言
NiNg_1_23411 分钟前
SpringSecurity入门
后端·spring·springboot·springsecurity
程序员劝退师_16 分钟前
优惠券秒杀的背后原理
java·数据库
java小吕布31 分钟前
Java集合框架之Collection集合遍历
java
一二小选手33 分钟前
【Java Web】分页查询
java·开发语言
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 小时前
idea 弹窗 delete remote branch origin/develop-deploy
java·elasticsearch·intellij-idea
Code成立1 小时前
《Java核心技术 卷I》用户图形界面鼠标事件
java·开发语言·计算机外设
Lucifer三思而后行1 小时前
YashanDB YAC 入门指南与技术详解
数据库·后端
鸽鸽程序猿1 小时前
【算法】【优选算法】二分查找算法(下)
java·算法·二分查找算法
遇见你真好。1 小时前
自定义注解进行数据脱敏
java·springboot