GIS开源框架:ArcGIS文件地理数据库(GDB)解析与入库

news2024/12/24 0:47:18

        对于GIS专业毕业的同学,想必对于ArcGIS软件不会太陌生,对于地理数据库也有一定的了解和使用经验。但是,撇开软件操作层面不谈,作为一个WebGIS/GIS开发人员,我们如何通过GIS开源框架去完成地理数据库的自动化解析和入库操作呢?这是接下来我们要深入讨论的核心问题。

目录

地理数据库

什么是地理数据库?

地理数据库的作用

ArcGIS地理数据库的基本体系

地理数据库的架构

地理数据库的相关概念

 地理数据库类型

Java后端开源GIS框架

GeoTools

核心特性

支持的数据源格式

GeoTools架构(Architecture)

GeoTools插件(Plugins)

GeoTools拓展(Extensions)

GeoTools衍生项目

GDAL(Geospatial Data Abstraction Library)

GDAL:Windows开发环境搭建

GDAL:基于FileGDB解析GDB

GDAL解析GDB文件地理数据库

GeoTools:GDB数据源自动入库处理


地理数据库

什么是地理数据库?

        先介绍一下什么是地理数据库。

        对于常年征战于后端开发领域的小伙伴们,对于“关系型数据库”一定有深入的理解,通俗地讲,就是:基于计算机的文件管理系统,将客观世界的实体进行抽象,并以关系表的形式存储到计算机中,供软件开发人员进行存取和更新、乃至分析等操作。例如:对公司的职工群体进行抽象,我们就能得到一张大名鼎鼎的职工信息表(tb_employee),里面存储了每一位员工的职工号、姓名、入职时间、所在部门等等各种信息。这也是GIS专业常年在谈的属性数据

        我们通过类比的方式对“地理数据库”做个定义,通俗地讲,就是:基于计算机的文件管理系统,将客观世界的地理实体进行抽象,并以关系表的形式存储到计算机中,专门供GIS开发人员进行存取、更新、分析等操作。注意:我们这里用了地理实体这个词汇,常见的,POI点、一系列宗地区域、行政区划区域、水系范围等等,任何可以通过点、线、面基础几何图形元素进行表达的任何客观实体,我们都可以将其以称之为地理实体,然后将其存储到地理数据库中。

地理数据库的作用

        那么问题来了,地理数据库在GIS开发领域有什么重要意义呢?如何理解地理数据库在实际中的应用价值呢?

        我们以ArcGIS产品的官方文档为基准,来阐述地理数据库在不同方面的多层次含义。

        ①数据结构方面:作为ArcGIS的原生数据结构,地理数据库是用于编辑和数据管理的主要数据格式,承担地理数据的存储载体职能;

        ②数据存储方面:作为地理数据的存储载体,地理数据库仍然是基于数据库管理系统或者文件系统实现的。因此,在实际开发中,我们会发现,像MySQL、Oracle、PostGreSQL等多种数据库,或以自包含的形式、或以插件的形式,先后都提供了对于点线面等几何数据的存储、查询、更新等操作的支持。

        ③信息模型方面:地理数据库的信息模型十分全面,不局限于点线面等几何要素——俗称的矢量数据;也天然的将图像数据——俗称的栅格数据包含在内(例如:遥感影像、DEM数字高程模型等),实现地理信息的表示和管理。同时,地理数据库也支持对于地理数据的空间关系、空间完整性规则(例如:几何图形的拓扑结构完整性)的定义。

        ④通用性方面。严格意义上讲,地理数据库软件可支持各种格式的地理数据,以及不同格式之间的数据交换操作,包括:shapefile、计算机辅助绘图 (CAD) 文件、不规则三角网 (TIN)、格网、CAD 数据、影像、地理标记语言 (GML) 文件和大量其他 GIS 数据源

        ⑤工作流(Workflow)方面。地理数据库不仅仅是数据存储和数据交换与集成的工具,同时也可提供基于GIS数据的工作流事务模型,可天然实现一些工作流程及其各操作步骤之间业务规则的抽象、概括描述、与自动化处理。

ArcGIS地理数据库的基本体系

地理数据库的架构

        以关系型数据库为参考,地理数据库的实现并不存在任何特别之处,因为地理数据库仍是基于其它高级DBMS应用程序中的相同多层应用程序架构实现的。我们在之前有提到,MySQL、Oracle等提供有空间拓展功能,因此,从这个层面上来讲,MySQL也可以作为一个地理数据库来使用——因为它不仅支持GIS数据的存储,还提供了用于计算、输出结果的功能函数模块。

        我们来看一个将PostGreSQL数据库作为地理数据库进行使用的示例,如下为GIS数据在数据表中的存储形式,下表中存储了中国各省份的行政区划面数据(Geometry字段值就是对应的几何数据),

PostGreSQL-数据表部分截图

        为了更加明显的看到我们存储的数据在地理层面的显示效果,以下通过QGIS(一款开源GIS桌面软件)连接PostGreSQL数据库,然后将数据表中的数据进行可视化展示。

数据表中地理数据的可视化展示

         如上图,即为基于PostGreSQL数据库实现的地理数据存储示例。

        至于这种机制是如何实现的呢?我们可以将实现转移到PostGreSQL的数据类型方面,通过查阅官网文档,会发现:PostGreSQL内置的支持Geometry几何类型字段,该字段就是用于存储GIS数据的。换句话说,只要将一个字段设置为Geometry类型,那么就可以实现GIS数据的存储,将一个PostGreSQL数据库作为地理数据库使用而用于存储地理数据的这些表或者以文件的形式存储到磁盘上,或者存储到 DBMS 的数据库中,如 Oracle、IBM DB2、PostgreSQL、IBM Informix 或 Microsoft SQL Server。——这也是上面部分我们为什么说“地理数据库的实现并不存在任何特别之处”的原因所在。

PostGreSQL-官网文档截图

地理数据库的相关概念

        (1)数据集:地理数据库中包含3种数据集类型:①要素类;②栅格数据集;③表。通常我们通常是基于上述3种形式,将其以表的形式进行存储,实现地理数据库的构建的。

        (2)地理数据库策略:一个重要的地理数据库策略是充分利用数据库管理系统 (DBMS) 将 GIS 数据集以及用户数量扩展到极大的规模(例如,从仅能支持一个或几个用户的简单小型数据库扩展到可以支持上百万个要素和几千个同步用户的大型数据库)。表是地理数据集的主要存储形式。SQL 十分适用于对表中的行进行查询和集处理,因此地理数据库策略就是要充分利用这些功能。地理数据库支持使用 SQL 访问以下 DBMS 中的要素几何:

        (3)地理数据库元素:无论 GIS 用户使用什么系统,他们都将用到以下三种基本的数据集类型。

要素类(几何数据通过shpe字段存储)

栅格数据与空间参考

 地理数据库类型

        地理数据库是用于保存数据集集合的“容器”。ArcGIS将其分为3种类型:ArcSDE地理数据库、文件地理数据库、个人地理数据库。

        其中:

                ①文件地理数据库(gdb):在文件系统中以文件夹形式存储。每个数据集都以文件形式保存,单个文件最多可拓展至1TB。

                ②个人地理数据库(mdb):所有数据集都存储在Microsoft Access数据文件内,该数据文件最大为2G。

                ③ArcSDE地理数据库:也称为多用户地理数据库,这种类型的数据库使用Oracle、Microsoft SQL Server、IBM DB2、IBM Informix 或 PostgreSQL 存储于关系数据库中。这些地理数据需要使用ArcSDE,并且在大小和用户数量方面没有限制。

        以下为ArcGIS官方文档给出的对比结果,

三种类型地理数据库的比较

        PS:基于以上对比结果,在实际应用中,更建议使用文件地理数据库而不是个人地理数据库。

文件地理数据库-后缀名通常为GDB

Java后端开源GIS框架

        在以上内容的基础上,我们进一步讨论如何基于开源GIS框架来解析GDB文件地理数据库。

        以下介绍较大多数人熟知的开源GIS框架:geotools、gdal,并且这两者提供了对Java开发语言的支持。

GeoTools

geotools-官网首页截图

         geotools的官网页面可点击此处查看。

         geotools是一款用于处理空间数据(geospatial data)的、开源的、遵循OGC标准的Java开发库。其基本结构如下,

         基于Geotools框架,可以实现web地图服务(web services)、命令行工具(command line tools)以及桌面软件(desktop applications)的开发。

核心特性

        (1)定义了空间概念和数据结构接口:主要是基于Java Topology Suite (JTS)实现了对Goemetry几何数据实体类的定义,并实现了对属性数据和空间数据的的过滤支持。

        (2)提供了数据访问接口:支持feature要素获取、事务支持以及线程锁操作。

                可以从多种文件和空间数据库中获取数据;提供了地理坐标系支持;地图投影操作;基于属性数据和空间数据的过滤和分析操作。

        (3)提供了渲染器支持:一种适用于服务器端环境的、无状态的、低内存消耗的渲染器,可以实现复杂样式的地图展示、文本标签和颜色渲染控制。

        (4)GeoTools插件

        (5)GeoTools拓展:提供了对图形和网络支持(用于寻找最短路径)、拓扑验证、web地图服务器客户端、xml解析和编码的绑定以及颜色定义/渲染。

支持的数据源格式

(1)栅格格式

 (2)数据库jdbc支持

 (3)矢量格式

 (4)XML文件

GeoTools架构(Architecture)

        了解一些基本的GeoTools Library的结构是很有必要的,这将帮助我们在开发过程中快速找到适合业务场景的jar包。

GeoTools基础架构图

         PS:实际开发中,Maven工具可以帮助我们分析出需要引入的jar包。

        基于以上架构图,可以将GeoTools划分为以下几大模块,其中各模块的作用如下,

GeoTools-模块解析

GeoTools插件(Plugins)

        在以上核心模块的基础上,GeoTools推出了各种插件,以满足实际的应用需求。

geotools plugins

GeoTools拓展(Extensions)

        GeoTools在顶层架构基础上,还提供了一套拓展模块,可以实现绘、空间数据拓扑验证、地图服务等功能。

         当然,GeoTools还提供了对XML文档的支持,详细内容可点击此处查看。

GeoTools衍生项目

        如下为GeoTools的衍生项目,或者说是基于GeoTools开发出来的一些产品列表。

         其它内容可参考GeoTools官网文档。或者在之后也会再出一些文章进行详细的讨论。

GDAL(Geospatial Data Abstraction Library)

        GDAL是一个在X/MIT许可协议下的开源栅格空间数据转换库。它利用抽象数据模型来表达所支持的各种文件格式。它还有一系列命令行工具来进行数据转换和处理。GDAL也是一个相当复杂的开源GIS框架,提供了对Java开发语言的支持。

GDAL API支持情况

        之所以要介绍GDAL,是因为在之前对GeoTools框架的介绍中,我们并没有看到、实际上它也不支持对于ArcGIS文件地理数据库(GDB)的解析操作。而GDAL是支持的,通过它的FileGDB驱动模块,可以实现GDB文件地理数据库的数据源解析、表信息/要素信息获取等操作。

GDAL-FileGDB

         由于GDAL是一个相当庞大的开源项目,以下我们只针对感兴趣的FileGDB驱动部分、以及GDAL的环境搭建方面进行介绍。

GDAL:Windows开发环境搭建

(1)安装包下载。

         安装包可从GDAL官网进行下载,当前使用版本为release-1900-x64-2.4.4-mapserver-7.4.3。也可下载其它与本地JDK环境相配套的安装包(zip包即可)。

(2)zip包解压

        下载完成后,将zip包解压到本地(解压目录如下图),

        其中:bin/gdal/java目录下的gdal.jar是开发中要使用到的依赖包。

 (3)配置环境变量

           接着进行环境变量的配置操作。将bin目录下的子文件夹路径配置到系统环境变量中,

 (4)测试GDAL环境

        使用如下命令,查看GDAL的版本信息,测试GDAL是否安装成功。

# 查看GDAl版本信息
gdalinfo --version
# 获取GDAL支持的驱动列表
gdalinfo --formats

        显示类似如下的信息即为配置成功。 

GDAL:基于FileGDB解析GDB

        使用Java版本的GDAL解析GDB,需要将GDAL安装目录下的动态链接库(dll文件)合并到jdk安装目录下的bin目录,然后正常引入gdal.jar文件到项目中即可。

动态链接库配置

        将gdal安装目录的bin路径下的所有dll文件拷贝到jdk的bin目录下,进行动态链接库的配置。

 Java后端项目引入gdal.jar

        基于Maven构建的后端项目中,要使用GDAL,只需要引入gdal.jar文件即可。有两种引入方式,

(1)以文件方式引入

        在项目的根目录下创建lib文件夹,并将gdal安装目录下的gdal.jar文件放入,

        例如:此处创建了名称为gdalApps的项目,即拷贝gdal.jar文件到gdalApps/lib路径下,

        接着需要配置pom.xml文件的dependencies结点,配置内容如下,

<!--gdal-->
<dependency>
    <groupId>org.gdal</groupId>
    <artifactId>gdal</artifactId>
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/gdal.jar</systemPath>
</dependency>

(2)以pom.xml文件配置的方式引入

        还可以通过直接配置pom.xml文件的方式引入gdal.jar开发依赖,但是请注意:强烈建议此种方式引入的gdal.jar文件版本与本地配置好的GDAL环境版本保持一致。

<dependency>
    <groupId>org.gdal</groupId>
    <artifactId>gdal</artifactId>
    <version>xxx</version>
</dependency>

        至Maven依赖项下显示gdal.jar即为引入成功。

GDAL解析GDB文件地理数据库

        GDAL解析GDB文件地理数据库,依赖于FileGDB/OpenFileGDB驱动项。因此,使用GDAL解析GDB的基本思路一般如下,

        ①调用ogr.GetDriverByName(GDBDriver)方法获取FileGDB/OpenFileGDB驱动实例driver,并借助driver.Open(filePath)方法获取一个GDB数据源,得到一个DataSource实例;

        ②使用①中获取到的DataSource实例,调用其getLayer(layerIndex)方法获取GDB数据源中包含的每一个图层Layer对象,然后借助该对象的GetLayerDefn()方法获取到图层的元信息,如:空间参考/坐标系信息、属性表字段信息、获取图层内部Feature要素类实例的游标对象等(具体可参考示例代码)。

        ③通过Layer图层对象调用GetNextFeature()方法,逐个解析图层内的Feature要素信息即可。

        PS:Java版本的GDAL解析GDB文件地理数据库时,使用到的类及其层次结构如下,

        示例代码如下,

package com.xwd;

import org.gdal.gdal.gdal;
import org.gdal.ogr.*;
import org.gdal.osr.SpatialReference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ResourceUtils;

import java.io.FileNotFoundException;
import java.util.*;

/**
 * @className ReadGDB_Test
 * @description: com.xwd
 * @auther: xiwd
 * @date: 2023-02-13 - 02 - 13 - 21:04
 * @version: 1.0
 * @jdk: 1.8
 */
@SpringBootTest(classes = GDALSpringBootApp.class)
@RunWith(value = SpringRunner.class)
public class ReadGDB_Test {
    //properties

    //static properties
    static {
        gdal.AllRegister();//设置gdal环境
        // 为了支持中文路径,请添加下面这句代码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8","YES");
        // 为了使属性表字段支持中文,请添加下面这句
        gdal.SetConfigOption("SHAPE_ENCODING","");
        try {
            //防止报错
            gdal.SetConfigOption("GDAL_DATA", ResourceUtils.getFile("classpath:gdal-data").getPath());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    //methods
    @Test
    public void readGDB(){
        String GDBPath = "D:\\GIS_workspace\\gdb_data\\test-data\\中国行政区划数据.gdb\\test.gdb";
        String FileDriver = "OpenFileGDB";
        Driver driver = null;
        DataSource dataSource = null;
        driver = ogr.GetDriverByName(FileDriver);
        dataSource = driver.Open(GDBPath);
        if (Objects.isNull(dataSource)) return;
        int layerCount = dataSource.GetLayerCount();
        for (int i = 0; i < layerCount; i++) {
            Layer layer = dataSource.GetLayer(i);
            String layerName = layer.GetName();
            double[] extent = layer.GetExtent();
            System.out.println("正在解析"+layerName+",范围:"+ Arrays.toString(extent));
            FeatureDefn featureDefn = layer.GetLayerDefn();
            //解析Layer属性字段信息
            int fieldCount = featureDefn.GetFieldCount();
            Map<String,Object> fieldMap = new HashMap<>();
            for (int j = 0; j < fieldCount; j++) {
                FieldDefn fieldDefn = featureDefn.GetFieldDefn(j);
                String fieldName = fieldDefn.GetName();
                int fieldType = fieldDefn.GetFieldType();
                String typeName = fieldDefn.GetFieldTypeName(fieldType);
                fieldMap.put(fieldName,typeName);
            }
            System.out.println("图层"+layerName+"范围信息:"+fieldMap.toString());
            //解析Layer属性表
            long featureCount = layer.GetFeatureCount();
            for (int j = 0; j < featureCount; j++) {
                Feature feature = null;
                while ((feature = layer.GetNextFeature()) != null){
                    Set<Map.Entry<String, Object>> entries = fieldMap.entrySet();
                    for (Map.Entry<String, Object> next : entries) {
                        String key = next.getKey();
                        String value = feature.GetFieldAsString(key);
                        System.out.print(key + ":" + value);
                    }
                    System.out.println();
                }
            }
            System.out.println("\n");
        }
    }
}

GeoTools:GDB数据源自动入库处理

        将GDB数据源自动入库,则需要借助GeoTools-PostGIS插件的支持,可在pom.xml文件中引入对应的依赖项,其中:设置几何字段时,需要用到JTS模块内置的Geometry类类型;设置空间参考信息时,需要用到EPSG编码信息;通过PostGIS插件进行结果集过滤时,需要借助CQL模块实现。

        <!--gt-postgis-jdbc数据库PostGIS操作支持-->
        <dependency>
            <groupId>org.geotools.jdbc</groupId>
            <artifactId>gt-jdbc-postgis</artifactId>
            <version>${geotools.version}</version>
        </dependency>
        <!--gt-cql过滤查询支持-->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-cql</artifactId>
            <version>${geotools.version}</version>
        </dependency>
        <!-- jts -->
        <dependency>
            <groupId>org.locationtech.jts</groupId>
            <artifactId>jts-core</artifactId>
            <version>${jts.version}</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-epsg-hsql</artifactId>
            <version>${geotools.version}</version>
        </dependency>

        GDB数据源自动入库处理的完整的示例代码如下,

        PS:以下是先将GDB数据源序列化为单个小文件存储到本地,然后入库时将其做反序列化处理,再执行入库操作。

package com.xwd;

import org.gdal.gdal.gdal;
import org.gdal.ogr.*;
import org.geotools.data.*;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.WKBReader;
import org.geotools.referencing.CRS;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.opengis.feature.Feature;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.referencing.FactoryException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @className DataStoreDemo
 * @description: com.xwd
 * @auther: xiwd
 * @date: 2023-02-09 - 02 - 09 - 22:34
 * @version: 1.0
 * @jdk: 1.8
 */
@SpringBootTest(classes = GDALSpringBootApp.class)
@RunWith(value = SpringRunner.class)
class DataStoreToolClass {
    //static properties
    private static final Map<String, Object> dbParams = new HashMap<>();
    private static final GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
    private static final WKTReader wktReader = new WKTReader(geometryFactory);
    //properties
    private DataStore dataStore;



    static {
        /**
         * PostGIS连接参数
         * dbtype数据库类型(固定值):Must be the string postgis
         * host主机名:Machine name or IP address to connect to
         * port端口:Port number to connect to, default 5432
         * schema空间:The database schema to access
         * database数据库名: The database to connect to
         * user用户名:User name
         * passwd密码:Password
         */
        dbParams.put("dbtype", "postgis");
        dbParams.put("host", "localhost");
        dbParams.put("port", 5432);
        dbParams.put("schema", "public");
        dbParams.put("database", "gdb_db");
        dbParams.put("user", "postgres");
        dbParams.put("passwd", "postgres");
    }

    /**
     * 构造器
     */
    DataStoreToolClass() throws IOException {
        /**
         * FactoryFinder-提供多线程环境的安全、默认同步支持
         *      DataStoreFinder - 用于获取一个DataStore实例
         *  * access(访问):shapefile、database(PostGIS/web service[WFS])
         *  * create(创建):shapefile,然后借助FileDataStoreFinder对shp文件进行操作
         *  PS:DataStore数据源是不可重复的,即以单例的形式存在,但是可以复用
         *
         */
        this.dataStore = DataStoreFinder.getDataStore(dbParams);
    }

    /**
     * 动态创建表结构
     *
     * @param tableName 数据表名
     * @param fieldsMap 字段->字段类型映射表,强制使用LinkedHashMap,保证字段有序性
     * @param crs       空间参考系的EPSG编码
     * @return SimpleFeatureType表结构对象
     */
    public SimpleFeatureType createSimpleFeatureType(String tableName, LinkedHashMap<String, Class> fieldsMap, String crs) throws FactoryException {
        if (Objects.isNull(crs) || "".equals(crs)) {
            crs = "4326";
        }
        SimpleFeatureTypeBuilder ftb = new SimpleFeatureTypeBuilder();
        ftb.setName(tableName);
        ftb.setCRS(CRS.decode("EPSG:" + crs));
        Set<Map.Entry<String, Class>> entries = fieldsMap.entrySet();
        for (Map.Entry<String, Class> next : entries) {
            if ("geometry".equals(next.getKey())) {
                ftb.add(next.getKey(), next.getValue());
            } else {
                ftb.add(next.getKey(), next.getValue());
            }
        }
        return ftb.buildFeatureType();
    }

    /**
     *
     * @param schemaName 数据表名称
     * @return 布尔值,true-表示数据表已经存在,无需创建;false-表示数据表不存在,需要创建
     */
    public boolean hasSchema(String schemaName){
        SimpleFeatureType schema = null;
        try {
            schema = dataStore.getSchema(schemaName);
            return Objects.nonNull(schema);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除数据表
     * @param schemaName 数据表名
     */
    public void dropSchema(String schemaName){
        try {
            this.dataStore.removeSchema(schemaName);
            System.out.println(schemaName+"删除表成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 动态创建数据表
     *
     * @param schemaName  数据表名
     * @param featureType 数据表结构
     * @param dropExistsBefore 否先删除已有同名表-再执行添加操作-true-是;false-否
     * @return boolean, 布尔值-true:创建成功;false-创建失败
     */
    public boolean createSchema(String schemaName, SimpleFeatureType featureType,boolean dropExistsBefore) {
        // create a featureType and write it to PostGIS
        try {
            boolean hasSchema = this.hasSchema(schemaName);
            if (hasSchema) {
                if (dropExistsBefore){
                    this.dropSchema(schemaName);
                }else{
                    return true;
                }
            }
            dataStore.createSchema(featureType);
            //尝试获取数据表
            SimpleFeatureType schema = dataStore.getSchema(schemaName);
            if (Objects.nonNull(schema)) return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /***
     * 为数据表添加数据
     * @param schemaName 数据表名
     * @param dataMap 包含id值和data数据体的Map映射表
     * @return int,影响的行数/添加成功的记录条数
     */
    public int inertForSchema(String schemaName,LinkedHashMap<String,Object[]> dataMap) {
        if (Objects.isNull(schemaName) || "".equals(schemaName)) return 0;
        SimpleFeatureType schema = null;
        FeatureIterator iterators = null;
        FeatureWriter<SimpleFeatureType, SimpleFeature> featureWriter = null;
        int affectRows = 0;
        try {
            schema = this.dataStore.getSchema(schemaName);//从数据源获取数据表对象
            SimpleFeatureBuilder build = new SimpleFeatureBuilder(schema);
            List<SimpleFeature> featureList = new ArrayList<>();
            Set<Map.Entry<String, Object[]>> entries = dataMap.entrySet();
            for (Map.Entry<String, Object[]> next : entries) {
                SimpleFeature simpleFeature = build.buildFeature(next.getKey(), next.getValue());
                featureList.add(simpleFeature);
            }
            FeatureCollection collection = new ListFeatureCollection(schema, featureList);
            featureWriter = this.dataStore.getFeatureWriterAppend(schemaName, Transaction.AUTO_COMMIT);
            iterators = collection.features();
            while (iterators.hasNext()){
                Feature feature = iterators.next();
                SimpleFeature simpleFeature = featureWriter.next();
                Collection<Property> properties = feature.getProperties();//获取features属性集合
                for (Property property : properties) {
                    simpleFeature.setAttribute(property.getName(), property.getValue());
                }
                featureWriter.write();
                affectRows++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (Objects.nonNull(iterators))
                iterators.close();
            if (Objects.nonNull(featureWriter)) {
                try {
                    featureWriter.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return affectRows;
    }


    /**
     * 数据表查询-[过滤查询]
     * @param schemaName 数据表名称
     * @param sqlFilter 过滤条件的SQL语句,例如:模糊查询过滤,name like '%a'
     * @return Collection<Property> 查询结果集,查询失败则返回null
     */
    public Collection<Property> queryFilterForSchema(String schemaName, String sqlFilter) {
        if (Objects.isNull(schemaName) || "".equals(schemaName)) return null;
        Filter filter = null;
        Query query = null;
        FeatureReader<SimpleFeatureType, SimpleFeature> featureReader = null;
        Collection<Property> properties = null;
        try {
            filter = CQL.toFilter("name like '%a'");
            query = new Query(schemaName,filter);
            featureReader = this.dataStore.getFeatureReader(query, Transaction.AUTO_COMMIT);
            while (featureReader.hasNext()) {
                SimpleFeature feature = featureReader.next();
                properties = feature.getProperties();
                System.out.println(Arrays.toString(properties.toArray()));
            }
            return properties;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (Objects.nonNull(featureReader)) {
                try {
                    featureReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return properties;
    }

    /**
     * 数据表查询操作-[全量查询]
     * @param schemaName 数据表名称
     * @return Collection<Property> 查询结果集,查询失败则返回null
     */
    public Collection<Property> queryAllForSchema(String schemaName){
        if (Objects.isNull(schemaName) || "".equals(schemaName)) return null;
        SimpleFeatureSource featureSource = null;
        SimpleFeatureIterator iterator = null;
        Collection<Property> properties = null;
        try {
            featureSource = this.dataStore.getFeatureSource(schemaName);
            SimpleFeatureCollection features = featureSource.getFeatures();
            iterator = features.features();
            while (iterator.hasNext()){
                SimpleFeature simpleFeature = iterator.next();
                properties = simpleFeature.getProperties();
//                for (Property property : properties) {
//                    System.out.print(property.getName() + "(" + property.getType().getBinding().getName() + "):" + property.getValue()+"\t");
//                }
            }
            return properties;
        } catch (IOException e) {
            e.printStackTrace();
            return properties;
        } finally {
            if (Objects.nonNull(iterator))
                iterator.close();
        }
    }

}

class GDBToolClass{
    private static final ExecutorService executorService = Executors.newFixedThreadPool(10);//线程池
    private static final String GDBDriver = "OpenFileGDB";

    private String  GDBPath;
    private DataSource dataSource;

    static {
        gdal.AllRegister();//设置gdal环境
        // 为了支持中文路径,请添加下面这句代码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
        // 为了使属性表字段支持中文,请添加下面这句GDBReadTaskExecutor
        gdal.SetConfigOption("SHAPE_ENCODING", "");
//        gdal.SetConfigOption("GDAL_DATA", "classpath:gdal-data");
        gdal.SetConfigOption("OGR_ORGANIZE_POLYGONS", "ONLY_CCW");//开启此配置-加速读取
        try {
            //防止报错
            gdal.SetConfigOption("GDAL_DATA", ResourceUtils.getFile("classpath:gdal-data").getPath());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }


    GDBToolClass(String GDBPath){
        this.GDBPath = GDBPath;
        Driver driver = ogr.GetDriverByName(GDBDriver);
        this.dataSource = driver.Open(this.GDBPath);
    }

    /**
     * 获取所有图层
     * @return
     */
    public List<Layer> getLayers(){
        int count = this.dataSource.GetLayerCount();
        List<Layer> layers = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            layers.add(this.dataSource.GetLayer(i));
        }
        return layers;
    }

    /**
     * 根据字符串类型描述获取对应的Class运行时类类型
     * @param name 类型字符串
     * @return Class运行时类类型
     */
    private Class getClassByStringName(String name){
        if ("".equals(name)||Objects.isNull(name)) return String.class;
        switch (name){
            case "String":{
                return String.class;
            }
            case "Real":{
                return Double.class;
            }
            default:
                return String.class;
        }
    }

    /**
     * 获取名称为layerName的图层属性表结构
     * @param layerName 图层名称
     * @return 以LinkedHashMap<String,Class>描述的属性表结构
     */
    public LinkedHashMap<String,Class> getLayerSchemaStructure(String layerName){
        Layer layer = this.dataSource.GetLayer(layerName);
        System.out.println(layer);
        FeatureDefn featureDefn = layer.GetLayerDefn();
        LinkedHashMap<String,Class> classMap = new LinkedHashMap<>();
        for (int i = 0; i < featureDefn.GetFieldCount(); i++) {
            FieldDefn fieldDefn = featureDefn.GetFieldDefn(i);
            int fieldType = fieldDefn.GetFieldType();
            String typeName = fieldDefn.GetFieldTypeName(fieldType);
            String name = fieldDefn.GetName();
            System.out.println(typeName+"-"+name);
            classMap.put(name,getClassByStringName(typeName));
        }
        classMap.put("geometry",Geometry.class);
        return classMap;
    }
}

class FileToolClass{
    private static final GeometryFactory factory = JTSFactoryFinder.getGeometryFactory(null);
    private static final WKBReader wkbReader = new WKBReader(factory);

    FileToolClass(){ }

    /**
     * 从文件中解析List<Map>结构的数据集
     * @param filePath 文件路径
     * @return List<Map>结构的数据集
     */
    public List<Map<String,Object>> readFile(String filePath){
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fis = new FileInputStream(filePath);
            ois = new ObjectInputStream(fis);
            List<Map<String,Object>> object = (List<Map<String, Object>>) ois.readObject();
            return object;
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从文件中解析List<Map>结构的数据集
     * @param file 文件路径
     * @return List<Map>结构的数据集
     */
    public List<Map<String,Object>> readFileByFile(File file){
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fis = new FileInputStream(file);
            ois = new ObjectInputStream(fis);
            List<Map<String,Object>> object = (List<Map<String, Object>>) ois.readObject();
            return object;
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     *
     * @param maps readFile读取单个文件返回的结果集
     * @param fieldClassMap 原始GDB中获取到的字段-字段类型的映射Map
     * @return 转换之后符合要求的结果集
     */
    public LinkedHashMap<String,Object[]> trans2ParamMap(List<Map<String,Object>> maps,LinkedHashMap<String, Class> fieldClassMap){
        LinkedHashMap<String,Object[]> mapResult = new LinkedHashMap<>();
        for (int i = 0; i < maps.size(); i++) {
            Map<String, Object> map = maps.get(i);
            int size = fieldClassMap.size();
            Object[] objects = new Object[size];
            int count = 0;
            for (Map.Entry<String, Class> next : fieldClassMap.entrySet()) {
                if ("geometry".equals(next.getKey())) {
                    Geometry geometry = null;
                    try {
                        geometry = wkbReader.read((byte[]) map.get(next.getKey()));
                        objects[count] = geometry;
                    } catch (ParseException e) {
                        e.printStackTrace();
                        objects[count] = null;
                    }
                } else {
                    objects[count] = map.get(next.getKey());
                }
                count++;
            }
            mapResult.put(String.valueOf(i+1),objects);
        }
        return mapResult;
    }


}

public class DataStoreDemo {
    //static properties

    //测试GDB---GDB数据入库处理关键代码
    @Test
    public void readAllFilesFromDirectory() throws Exception {
        String GDBPath = "D:\\GIS_workspace\\gdb_data\\test-data\\中国行政区划数据.gdb\\test.gdb";
        String outputDirectory = "D:\\GIS_workspace\\gdb_data\\test-data\\中国行政区划数据.gdb\\output";
        File file = new File(outputDirectory);
        if (!file.isDirectory()) throw new Exception("文件夹路径不合法");
        boolean dropExistsBefore = true;//是否先删除已有同名表-再执行添加操作-true-是;false-否
        GDBToolClass toolClass = new GDBToolClass(GDBPath);//GDB数据源解析类
        DataStoreToolClass dataStoreToolClass = new DataStoreToolClass();//数据库操作类
        FileToolClass fileToolClass = new FileToolClass();//文件解析操作类
        List<Layer> layers = toolClass.getLayers();
        for (Layer layer : layers) {
            String layerName = layer.GetName();//图层名称/数据表名
            LinkedHashMap<String, Class> classMap = toolClass.getLayerSchemaStructure(layerName);//获取图层的数据表结构
            SimpleFeatureType simpleFeatureType = dataStoreToolClass.createSimpleFeatureType(layerName, classMap, "4326");
            boolean flag = dataStoreToolClass.createSchema(layerName, simpleFeatureType,dropExistsBefore);//创建数据表
            //判断数据表是否创建成功
            if (flag) {
                System.out.println("数据表创建成功");
            } else {
                System.out.println("数据表创建失败/已存在");
            }
            File[] files = file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.getName().contains(layerName);
                }
            });
            if (Objects.isNull(files) || files.length == 0) {
                System.out.println("数据源-[" + layerName + "]解析失败!");
                continue;
            }
            //解析数据源
            for (File currentFile : files) {
                List<Map<String, Object>> maps = fileToolClass.readFileByFile(currentFile);
                LinkedHashMap<String, Object[]> hashMap = fileToolClass.trans2ParamMap(maps, classMap);
                //添加数据到数据库中
                int rows = dataStoreToolClass.inertForSchema(layerName, hashMap);
                System.out.println("成功添加" + rows + "条记录到[" + layerName + "]表中");
            }
        }
    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/344258.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决不同影像裁剪后栅格数据行列不一致问题

前言在处理栅格数据时&#xff0c;尽管用同一个矢量文件裁剪栅格数据&#xff0c;不同数据来源的栅格行列数也会出现不一致的情况。如果忽略或解决不好&#xff0c;会导致后续数据处理出现意想不到的误差或错误&#xff0c;尤其是利用编程实现数据处理时。因此&#xff0c;应当…

VisualGDB 5.6R9 FOR WINDOWS

Go cross-platform with comfort VisualGDB 是 Visual Studio 的一个非常强大的扩展&#xff0c;它允许您调试或调试嵌入式系统。这个程序有一个非常有吸引力的用户界面&#xff0c;它有许多调试或调试代码的功能。VisualGDB 还有一个向导可以帮助您调试程序&#xff0c;为您提…

【C++】关键字、命名空间、输入和输出、缺省参数、函数重载

C关键字(C98)命名空间产生背景命名空间定义命名空间使用输入&输出缺省参数什么叫缺省参数缺省参数分类函数重载函数重载概念C支持函数重载的原理--名字修饰C关键字(C98) C总计63个关键字&#xff0c;C语言32个关键字。 下面我们先看一下C有多少关键字&#xff0c;不对关键…

Linux 解压JAR包 查看class内容

快速解决方案 查询class相对路径&#xff1a;jar tf test.jar | grep "test.class"单独解压class文件&#xff1a;jar xvf test.jar com/test/test.class查看class文件内容&#xff1a;javap -c com/test/test.class 背景 服务运行后&#xff0c;日志打印出来发现…

【taichi】利用 taichi 编写深度学习算子 —— 以提取右上三角阵为例

本文以取 (bs, n, n) 张量的右上三角阵并展平为向量 (bs, n*(n1)//2)) 为例&#xff0c;展示如何用 taichi 编写深度学习算子。 如图&#xff0c;要把形状为 (bs,n,n)(bs,n,n)(bs,n,n) 的张量&#xff0c;转化为 (bs,n(n1)2)(bs,\frac{n(n1)}{2})(bs,2n(n1)​) 的向量。我们先写…

各种素材网站大全【全部倾倒,福利倒计时-JS,HTML,游戏素材,UI,图片素材等

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;解忧杂货铺 ⭐各种素材网站大全⭐ 文章目录⭐各种素材网站大全⭐&#x1f3b6;大家必逛的四大天王…

STM32F769BIT6微控制器STM32F769IGT6详细规格

说明STM32F7 32 位 MCUFPU 基于高性能的 ARMCortex-M7 32 位 RISC 内核&#xff0c;工作频率高达 216MHz。Cortex-M7 内核具有单浮点单元(SFPU)精度&#xff0c;支持所有 ARM 单精度数据处理指令与数据类型。同时执行全套 DSP 指令和存储保护单元&#xff08;MPU&#xff09;&a…

JVM02类加载子系统

1. 加载阶段 通过一个类的全限定名获取定义此类的二进制字节流 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 在内存中生成一个代表这个类的java.lang.Class对象&#xff0c;作为方法区这个类的各种数据的访问入口 加载class文件的方式 从本地系统中直接…

六、HTTP 首部字段

HTTP 首部字段 一、HTTP 报文首部 HTTP 请求报文由方法、URI、HTTP 版本、HTTP 首部字段等部分构成。 HTTP 响应报文由HTTP版本、状态码&#xff08;数字和原因短语&#xff09;、HTTP首部字段3部分构成。 HTTP 协议的请求和响应报文中必定包含 HTTP 首部。首部内容为客户端…

TensorRT如何工作

TensorRT如何工作 本章提供了有关 TensorRT 工作原理的更多详细信息。 文章目录TensorRT如何工作5.1. Object Lifetimes5.2. Error Handling and Logging5.3 Memory5.3.1. The Build Phase5.3.2. The Runtime Phase5.4. Threading5.5. Determinism5.1. Object Lifetimes Tenso…

ChatGPT通俗导论:从RL之PPO算法、RLHF到GPT-N、instructGPT

前言 自从我那篇BERT通俗笔记一经发布&#xff0c;然后就不断改、不断找人寻求反馈、不断改&#xff0c;其中一位朋友倪老师(之前我司NLP高级班学员现课程助教老师之一)在谬赞BERT笔记无懈可击的同时&#xff0c;给我建议到&#xff0c;“后面估计可以尝试尝试在BERT的基础上…

MACD多周期共振指标公式,日周月共振

有人问多周期MACD怎么写&#xff0c;编写指标的难度其实不大&#xff0c;主要问题是解决多周期MACD显示的问题。日线、周线、月线三个周期&#xff0c;每个周期都有快线DIF和慢线DEA两条线&#xff0c;一共6条&#xff0c;怎么在副图上清晰显示出来。 一、MACD多周期共振指标公…

第三章SpringBoot配置文件

文章目录什么是配置文件比如我们的QQ的配置文件配置文件作用SpringBoot配置文件的格式Spring Boot 配置文件主要分为以下两种格式properties 配置文件说明properties 基本语法properties 缺点分析yml 配置文件yml概述yml基础语法读取配置文件Value 注解使用“${}”的格式读取Co…

深度使用国产Bg-Tinkle数据库客户端—太赞了,居然还集成chatGPT AI生成SQL

软件概述数据库客户端软件是一种用于连接、管理和操作数据库的软件。它通常与数据库管理系统&#xff08;DBMS&#xff09;配合使用&#xff0c;允许用户在其上执行SQL 语句、浏览数据库中的数据、执行备份和恢复操作以及执行其他管理任务。常见的数据库客户端软件包括 MySQL W…

攻防世界-Confusion1

题目 访问题目场景 某天&#xff0c;Bob说&#xff1a;PHP是最好的语言&#xff0c;但是Alice不赞同。所以Alice编写了这个网站证明。在她还没有写完的时候&#xff0c;我发现其存在问题。(请不要使用扫描器) 然后结合图片我们知道&#xff0c;这个网址是python写的&#xff0…

有序表(上)

文章目录1、引入2、左旋和右旋3、AVL树3.1 AVL 树的平衡条件3.2 搜索二叉树如何删除节点3.3 AVL树的平衡性被破坏的四种类型3.4 AVL 树平衡性如何检查&#xff1f;如何调整失衡&#xff1f;3.4.1 AVL树新增节点如何检查树的平衡性&#xff1f;3.4.2 AVL树删除节点如何检查树的平…

学习笔记:Java 并发编程⑥_并发工具_JUC

若文章内容或图片失效&#xff0c;请留言反馈。 部分素材来自网络&#xff0c;若不小心影响到您的利益&#xff0c;请联系博主删除。 视频链接&#xff1a;https://www.bilibili.com/video/av81461839配套资料&#xff1a;https://pan.baidu.com/s/1lSDty6-hzCWTXFYuqThRPw&am…

4. 寻找两个正序数组的中位数(数组)

文章目录题目描述方法一,重组排序方法二,调用系统函数题目描述 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 示例 1&#xff1a; 输入&#…

最全面的SpringBoot教程(五)——整合框架

前言 本文为 最全面的SpringBoot教程&#xff08;五&#xff09;——整合框架 相关知识&#xff0c;下边将对SpringBoot整合Junit&#xff0c;SpringBoot整合Mybatis&#xff0c;SpringBoot整合Redis等进行详尽介绍~ &#x1f4cc;博主主页&#xff1a;小新要变强 的主页 &…

回归预测 | MATLAB实现NGO-LSTM北方苍鹰算法优化长短期记忆网络多输入单输出

回归预测 | MATLAB实现NGO-LSTM北方苍鹰算法优化长短期记忆网络多输入单输出 目录回归预测 | MATLAB实现NGO-LSTM北方苍鹰算法优化长短期记忆网络多输入单输出预测效果基本介绍程序设计参考资料预测效果 基本介绍 Matlab实现NGO-LSTM北方苍鹰算法优化长短期记忆网络多输入单输出…