使用java对栅格数据的处理,对栅格文件进行导入导出

需求背景:

对栅格文件进行导入导出(使用代码的方式,非命令方式);

当然也可以使用代码和GDAL的方式进行,但是GDAL配置部署不便捷,故选用GeoTools方式来实现。

ps:若是使用命令方式,首先打开PostgreSQL的安装目录【\PostgreSQL\14\bin】,然后使用如下命令即可实现把栅格文件导入到数据库中:

# 指定切片大小
raster2pgsql -s 4326 -I -C -M D:\model\tif\TDM1_DEM__30_N34E108_DEM.tif -F -t 256x256 sdx.tdm1_dem | psql -h 192.168.31.200 -p 5432 -U postgres -d testShp

# 4326 是ESP
# 256*256 是切片大小
# sdx.tdm1_dem 是哪个模式下的哪个表,也可以是public.tdm1_dem
# -h 192.168.31.200 是数据库地址
# -p 5432 端口
# -U postgres 用户名
# -d testShp 数据库名


# 自动切片大小
raster2pgsql -s 4326 -d -k -N -e -I -C -M "D:\model\tif\TDM1_DEM__30_N34E108_DEM.tif" -F -t auto public.raster_data | psql -h 192.168.31.200 -p 5432 -d testdb -U postgres

 若是使用代码导入的思路:

  • 配置好前置条件
  • 读取栅格数据
  • 连接到数据库
  • 创建表
  • 将数据插入到数据库中

实现:

1、配置好前置条件

  • 确保你的PostgreSQL数据库已安装并启用了PostGIS扩展
-- 安装PostGIS扩展
CREATE EXTENSION postgis;

-- 安装PostGIS的栅格支持
CREATE EXTENSION postgis_raster;
  • 确认PostGIS版本

确保你使用的是支持栅格数据的PostGIS版本。你可以使用以下命令来检查PostGIS版本:

SELECT POSTGIS_VERSION();
-- 查看函数
SELECT proname, proargtypes, prosrc
FROM pg_proc
JOIN pg_namespace ON pg_namespace.oid = pg_proc.pronamespace
WHERE proname = 'st_fromgdalraster';

SELECT proname, proargtypes
FROM pg_proc
JOIN pg_namespace ON pg_namespace.oid = pg_proc.pronamespace
WHERE proname LIKE 'st_f%' AND pg_namespace.nspname = 'public';

-- 查看扩展
SELECT postgis_full_version();

确保返回的版本信息显示PostGIS包含栅格支持

2、读取栅格数据、保存数据到数据库

添加依赖

        <gt.version>20.0</gt.version>

        <!-- 添加GeoTools依赖 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-opengis</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-api</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-data</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-jdbc</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools.jdbc</groupId>
            <artifactId>gt-jdbc-postgis</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-main</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-metadata</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-referencing</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-render</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-shapefile</artifactId>
            <version>${gt.version}</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-coverage</artifactId>
            <version>${gt.version}</version>
        </dependency>
        <!-- 如果使用的 GeoTools 功能需要额外的依赖,如坐标转换,可能还需要添加这个 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-epsg-hsql</artifactId>
            <version>${gt.version}</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geojson</artifactId>
            <version>${gt.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.15</version>
        </dependency>


<!-- 1 栅格相关依赖 -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-image</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-swing</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-process</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-process-raster</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geotiff</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.geotools/gt-swing -->
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-swing</artifactId>
            <version>${gt.version}</version>
        </dependency>

        <!--<dependency>
            <groupId>org.jdal</groupId>
            <artifactId>jdal-core</artifactId>
            <version>2.0.0</version>
        </dependency>
-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-imaging</artifactId>
            <version>1.0-alpha3</version>
        </dependency>

        <!--        <dependency>
                    <groupId>org.geotools</groupId>
                    <artifactId>gt-netcdf</artifactId>
                    <version>20.0</version>
                </dependency>-->

        <!-- https://mvnrepository.com/artifact/edu.ucar/netcdf -->
        <dependency>
            <groupId>edu.ucar</groupId>
            <artifactId>netcdf</artifactId>
            <version>4.3.22</version>
        </dependency>

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geometry</artifactId>
            <version>20.0</version>
        </dependency>

        <!-- TwelveMonkeys ImageIO TIFF -->
        <dependency>
            <groupId>com.twelvemonkeys.imageio</groupId>
            <artifactId>imageio-tiff</artifactId>
            <version>3.8.0</version>
        </dependency>

        <!-- Maven example for JAI TIFF plugin -->
        <dependency>
            <groupId>javax.media</groupId>
            <artifactId>jai-core</artifactId>
            <version>1.1.3</version>
        </dependency>

        <!--gdal-->
        <dependency>
            <groupId>org.gdal</groupId>
            <artifactId>gdal</artifactId>
            <version>3.9.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/net.postgis/postgis-jdbc -->
        <dependency>
            <groupId>net.postgis</groupId>
            <artifactId>postgis-jdbc</artifactId>
            <version>2024.1.0</version>
        </dependency>



        <!-- 11 栅格相关依赖 -->

实现类 


    public String importRasterData(GridDataDto gridDataDto) throws IOException {
        // 1. 连接数据库,创建模式、表相关内容 实现导入栅格数据的逻辑
        GridData gridData = gridDataDto.getParameter();
        String sourceFilePath = gridData.getSourceFilePath();
        String targetDatasource = gridData.getTargetDatasource();
        String targetDatasetName = gridData.getTargetDatasetName().toLowerCase();
        String importMode = gridData.getImportMode();
        // 解析 PostgreSQL 数据库 连接参数
        Map<String, Object> dataSourceMap = parseDatabaseParameters(targetDatasource);
        log.info("dataSourceMap = " + dataSourceMap);
        // 创建 PostgreSQL/PostGIS 表
        JDBCDataStoreFactory dsf = new PostgisNGDataStoreFactory();
        JDBCDataStore datastore = dsf.createDataStore(dataSourceMap);
        // 如果不存该模式,则创建模式schema 确保 schema 存在
        String schemaName = dataSourceMap.get("schema").toString();
        try (Connection connection = datastore.getDataSource().getConnection()) {
            Statement stmt = connection.createStatement();
            // 创建依赖扩展
            String[] extensions = {"postgis", "postgis_raster"};
            for (String extension : extensions) {
                String createExtensionSql = "CREATE EXTENSION IF NOT EXISTS " + extension;
                stmt.execute(createExtensionSql);
            }
            log.info("Extensions created successfully");

            // 检查 schema 是否存在
            String checkSchemaSql = "SELECT schema_name FROM information_schema.schemata WHERE schema_name = '" + schemaName + "'";
            ResultSet rs = stmt.executeQuery(checkSchemaSql);
            if (!rs.next()) {
                // 如果 schema 不存在,创建 schema
                String createSchemaSql = "CREATE SCHEMA " + schemaName;
                stmt.execute(createSchemaSql);
                log.info("schema created successfully");
            }
            // 确保表存在 根据importMode的值NONE、OVERWRITE、APPEND进行相关操作
            if ("NONE".equals(importMode) || "OVERWRITE".equals(importMode)) {
                // 删除表
                String dropTableSql = "DROP TABLE IF EXISTS " + schemaName + "." + targetDatasetName;
                stmt.execute(dropTableSql);
                // 如果表不存在,创建表
                // String createTableSql = "CREATE TABLE IF NOT EXISTS " + schemaName + "." + targetDatasetName + " (rid INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, rast raster, srid text, filename text)";
                // 如果表不存在,创建表
                String createTableSql = "CREATE TABLE IF NOT EXISTS " + schemaName + "." + targetDatasetName +
                        " (rid SERIAL PRIMARY KEY, rast raster, srid text, filename text)";
                stmt.execute(createTableSql);
                log.info("Table created successfully");
            } else if ("APPEND".equals(importMode)) {
                // 检查表是否存在,不存在则创建
                String checkTableSql = "SELECT to_regclass('" + schemaName + "." + targetDatasetName + "')";
                rs = stmt.executeQuery(checkTableSql);
                if (!rs.next() || rs.getString(1) == null) {
                    // 如果表不存在,创建表
                    // String createTableSql = "CREATE TABLE IF NOT EXISTS " + schemaName + "." + targetDatasetName +" (rid INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, rast raster, srid text)";
                    String createTableSql = "CREATE TABLE IF NOT EXISTS " + schemaName + "." + targetDatasetName + " (rid SERIAL PRIMARY KEY, rast raster, srid text)";
                    stmt.execute(createTableSql);
                    log.info("Table created successfully");
                }
            }
        } catch (SQLException e) {
            log.error("Failed to ensure schema exists", e);
            throw new IOException("Failed to ensure schema exists", e);
        }

        // 保存到记录表中
        ServerImportInfo serverImportInfo = new ServerImportInfo();
        BeanUtils.copyProperties(gridData, serverImportInfo);
        Date now = new Date();
        serverImportInfo.setCreateTime(now);
        serverImportInfo.setStartTime(now);
        serverImportInfo.setUpdateTime(now);
        serverImportInfo.setState("RUNNING");
        serverImportInfo.setDataType("栅格-导入");
        serverImportInfoService.save(serverImportInfo);
        // 数据库相关连接和判断完成,下面实现
        // 2. 读取栅格数据
        try {
            File file = new File(gridData.getSourceFilePath());
            GeoTiffReader reader = new GeoTiffReader(file);
            GridCoverage2D coverage = reader.read(null);
            System.out.println("coverage = " + coverage);
            log.info("栅格数据读取完成");
            if (coverage != null) {
                // 获取和打印坐标参考系统信息
                CoordinateReferenceSystem crs = coverage.getCoordinateReferenceSystem();
                System.out.println("CRS: " + crs);
                // 获取栅格图像
                RenderedImage image = coverage.getRenderedImage();
                System.out.println("image = " + image);
                // 获取栅格的范围(Envelope)
                Envelope2D envelope = coverage.getEnvelope2D();
                System.out.println("Raster bounds: " + envelope.toString());
                System.out.println("Raster read successfully!");
            } else {
                System.err.println("Failed to read raster data.");
            }
            // 3. 导入栅格数据
            Integer saveRaster = storeRasterIntoDatabase(datastore, sourceFilePath, schemaName, targetDatasetName);
            System.out.println("saveRaster = " + saveRaster);
            log.info("栅格数据导入完成");
            // 4. 更新导入状态 判断是否大于0
            if (saveRaster > 0) {
                serverImportInfo.setState("FINISHED");
            } else {
                serverImportInfo.setState("FAILED");
            }
            // 5. 保存导入信息
            saveGridInfo(serverImportInfo);
            log.info("导入信息保存完成");

        } catch (Exception e) {
            log.error("导入栅格数据失败", e);
        } finally {
            // 关闭数据源
            datastore.dispose();
            log.info("数据源已关闭");
        }
        return String.valueOf(serverImportInfo.getId());
    }
public Integer storeRasterIntoDatabase(JDBCDataStore dataSource, String filePath, String schemaName, String targetDatasetName) throws IOException, SQLException, FactoryException {
        File file = new File(filePath);
        GeoTiffReader reader = new GeoTiffReader(file);
        GridCoverage2D coverage = reader.read(null);
        if (coverage == null) {
            throw new IOException("Failed to read raster data.");
        }
        RenderedImage image = coverage.getRenderedImage();
        Envelope2D envelope = coverage.getEnvelope2D();
        CoordinateReferenceSystem crs = coverage.getCoordinateReferenceSystem();
        int width = image.getWidth();
        int height = image.getHeight();
        double xPixelSize = envelope.getWidth() / width;
        double yPixelSize = envelope.getHeight() / height;
        double upperLeftX = envelope.getMinimum(0);
        double upperLeftY = envelope.getMaximum(1);
        // 获取srid
        int srid = CRS.lookupEpsgCode(crs, true);
        log.info("srid = " + srid);

        log.info("Raster size: " + width + " x " + height);
        log.info("Pixel size: " + xPixelSize + " x " + yPixelSize);
        log.info("Upper-left corner: " + upperLeftX + ", " + upperLeftY);
        String tableName = schemaName + "." + targetDatasetName;
        // Convert GridCoverage2D to image
        File tempFile = File.createTempFile("raster", ".tif");
        GeoTiffWriter writer = null;
        try {
            writer = new GeoTiffWriter(tempFile);
            writer.write(coverage, null);
        } finally {
            if (writer != null) {
                // Dispose of the writer to release resources
                writer.dispose();
            }
        }
        byte[] rasterData = convertCoverageToByteArray(coverage);
        //System.out.println("rasterData1 = " + Arrays.toString(rasterData));
        // 将栅格数据转换为字节数组
        Integer saveraster = 0;
        // Insert into PostGIS
        try (Connection connection = dataSource.getDataSource().getConnection()) {
            // raster ST_FromGDALRaster(bytea gdaldata, integer srid=NULL);
            //String sql = "INSERT INTO " + tableName + " (rast) VALUES (st_fromgdalraster(?::bytea,?))";
            String sql = "INSERT INTO " + tableName + " (rast) VALUES (st_fromgdalraster(?,?))";
            try (PreparedStatement stmt = connection.prepareStatement(sql)) {
                // 将字节数组作为二进制数据插入
                stmt.setBinaryStream(1, new ByteArrayInputStream(rasterData));
                // 设置栅格数据的SRID(可选)
                stmt.setInt(2, srid);
                log.info("stmt = " + stmt);
                stmt.executeUpdate();
                ++saveraster;
            }
            log.info("Inserted {} raster(s) into {}", saveraster, tableName);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            reader.dispose();
            dataSource.dispose();
        }

        return saveraster;
    }
    private byte[] convertCoverageToByteArray(GridCoverage2D coverage) throws IOException {
        // Create a temporary file to store the GeoTIFF
        File tempFile = File.createTempFile("coverage", ".tif");
        tempFile.deleteOnExit();

        GeoTiffWriter writer = null;
        try {
            writer = new GeoTiffWriter(tempFile);
            writer.write(coverage, null);
        } finally {
            if (writer != null) {
                // Dispose of the writer to release resources
                writer.dispose();
            }
        }
        // Read GeoTIFF file into byte array
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            Files.copy(tempFile.toPath(), baos);
            return baos.toByteArray();
        }
    }

这个是整体导入的示例,拓展一下也可以切片导入

既然可以导入栅格,那么反过来也是可以导出栅格数据变成栅格文件的,示例代码如下:

public String exportRasterData(GridDataDto gridDataDto) throws IOException {
        GridData gridData = gridDataDto.getParameter();
        // 导出目标文件的路径信息
        String targetFilePath = gridData.getTargetFilePath();
        // 导出的源数据集
        String sourceDataset = gridData.getSourceDataset();
        // 解析 PostgreSQL 数据库 连接参数
        Map<String, Object> dataSourceMap = parseDatabaseParameters(sourceDataset);
        log.info("Data source map: {}", dataSourceMap);
        // 创建 PostgreSQL/PostGIS 表
        JDBCDataStoreFactory dsf = new PostgisNGDataStoreFactory();
        JDBCDataStore datastore = dsf.createDataStore(dataSourceMap);
        // 如果不存该模式,则给出提示
        String schemaName = dataSourceMap.get("schema").toString();
        String tableName = dataSourceMap.get("dataset").toString();
        try (Connection connection = datastore.getDataSource().getConnection()) {
            Statement stmt = connection.createStatement();
            String checkSchemaSql = "SELECT schema_name FROM information_schema.schemata WHERE schema_name = '" + schemaName + "'";
            ResultSet rs = stmt.executeQuery(checkSchemaSql);
            if (!rs.next()) {
                log.error("schema notexist, create schema: {}", schemaName);
            }
            // 检查表是否存在,不存在则给出提示
            String checkTableSql = "SELECT to_regclass('" + schemaName + "." + tableName + "')";
            rs = stmt.executeQuery(checkTableSql);
            if (!rs.next() || rs.getString(1) == null) {
                log.error("Table notexist, create table: {}", tableName);
            }
            // 把数据库中的栅格数据导出成一个文件
            exportRasterFromDatabaseToImageFile(connection, schemaName, tableName, targetFilePath);

            // 保存到记录表中
            ServerImportInfo serverImportInfo = new ServerImportInfo();
            BeanUtils.copyProperties(gridData, serverImportInfo);
            Date now = new Date();
            serverImportInfo.setCreateTime(now);
            serverImportInfo.setStartTime(now);
            serverImportInfo.setUpdateTime(now);
            serverImportInfo.setState("RUNNING");
            serverImportInfo.setDataType("栅格-导出");
            serverImportInfo.setState("FINISHED");
            serverImportInfoService.save(serverImportInfo);
            log.info("导出信息保存完成");

            return String.valueOf(serverImportInfo.getId());

        } catch (SQLException e) {
            log.error("Failed to ensure schema exists", e);
            throw new IOException("Failed to ensure schema exists", e);
        } finally {
            datastore.dispose();
        }

    }
public void exportRasterFromDatabaseToImageFile(Connection connection, String schemaName, String targetDatasetName, String imagePath) {
        try {
            // Retrieve raster data from the database
            byte[] rasterData = getRasterDataFromDB(connection, schemaName, targetDatasetName);
            // log.info("rasterData = " + Arrays.toString(rasterData));
            log.info("Converting raster data to images...");
            convertRasterDataToImages(rasterData, imagePath);
            log.info("Conversion complete.");
        } catch (SQLException | IOException e) {
            e.printStackTrace();
        }
    }

  private byte[] getRasterDataFromDB(Connection connection, String schemaName, String targetDatasetName) throws SQLException, IOException {
        String tableName = schemaName + "." + targetDatasetName;
        String sql = "SELECT ST_AsGDALRaster(rast, 'GTiff') FROM " + tableName + " LIMIT 1";
        log.info("Executing SQL: " + sql);
        try (PreparedStatement stmt = connection.prepareStatement(sql);
             ResultSet rs = stmt.executeQuery()) {
            if (rs.next()) {
                log.info("Retrieved raster data from database.");
                try (InputStream is = rs.getBinaryStream(1); ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = is.read(buffer)) != -1) {
                        baos.write(buffer, 0, bytesRead);
                    }
                    return baos.toByteArray();
                }
            } else {
                throw new SQLException("No raster data found.");
            }
        }
    }
 private void convertRasterDataToImages(byte[] rasterData, String outTiffPath) throws IOException {
        File tempFile = File.createTempFile("raster", ".tif");
        tempFile.deleteOnExit();
        try (FileOutputStream fos = new FileOutputStream(tempFile)) {
            fos.write(rasterData);
        }
        AbstractGridFormat format = GridFormatFinder.findFormat(tempFile);
        GridCoverage2DReader reader = format.getReader(tempFile, null);
        GridCoverage2D coverage = reader.read(null);
        log.info("Coverage name: {}", coverage.getName());
        writeTiff(coverage, outTiffPath);
    }
 public static void writeTiff(Coverage coverage, String outTiffPath) throws IOException {
        File file = new File(outTiffPath);
        GeoTiffWriter geoTiffWriter = new GeoTiffWriter(file);
        final GeoTiffFormat format = new GeoTiffFormat();
        final GeoTiffWriteParams wp = new GeoTiffWriteParams();
        // 设置写出参数
        wp.setCompressionMode(GeoTiffWriteParams.MODE_DEFAULT);
        wp.setTilingMode(GeoToolsWriteParams.MODE_DEFAULT);
        ParameterValueGroup paramWrite = format.getWriteParameters();
        paramWrite.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp);
        geoTiffWriter.write((GridCoverage) coverage, paramWrite.values().toArray(new GeneralParameterValue[4]));
        geoTiffWriter.dispose();
    }

至此!即可实现栅格数据通过代码进行导入导出了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值