1、前言
前两天写了个地图瓦片采集及拼接工具,在导入到geoserver使用时发生了以下错误,查找原因后发现是未在拼接的图片文件中加入坐标信息,研究了一下,发现使用GDAL可以生成带坐标信息的tiff文件,于是进行尝试JAVA项目中引入GDAL库并进行使用。
无坐标信息的文件导入geoserver时的报错信息如下:
- Could not list layers for this store, an error occurred retrieving them: Failed to create reader from file:workspaces/jilin/tile-merge/z1.tiff and hints Hints: REPOSITORY = org.geoserver.catalog.CatalogRepository@25e71a88 EXECUTOR_SERVICE = java.util.concurrent.ThreadPoolExecutor@32768ba7[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0] System defaults: FORCE_AXIS_ORDER_HONORING = http FILTER_FACTORY = FilterFactoryImpl LENIENT_DATUM_SHIFT = true FORCE_LONGITUDE_FIRST_AXIS_ORDER = true GRID_COVERAGE_FACTORY = GridCoverageFactory TILE_ENCODING = null LOCAL_DATE_TIME_HANDLING = true COMPARISON_TOLERANCE = 1.0E-8 STYLE_FACTORY = StyleFactoryImpl FEATURE_FACTORY = org.geotools.feature.LenientFeatureFactoryImpl@1716c774
本文所用开发环境如下:
操作系统:windows 11
Java JDK:OpenJDK21
构建工具:Gradle 8.4
开发工具:VsCode - Visual Studio Code 1.84.1
关键组件库:gdal 3.7.2 http://www.gisinternals.com/release.php
2、下载引入GDAL库设置环境变量
GDAL是使用C++编写的程序库,GDAL使用SWIG生成了用于在C#、Java、Python等语言中使用GDAL的接口文件,在JAVA项目中正常引用GDAL是关键的一步。
到GDAL官网下载,并安装GDAL,有多种方式,我这里选择其中一种:
从 http://www.gisinternals.com/release.php 下载 GDAL 核心文件包 release-1930-x64-gdal-3-7-2-mapserver-8-0-1.zip ,解压到 D:\gdal 。
设置一系列环境变量:
添加新环境变量:
GDAL_DATA:D:\gdal\bin\gdal-data
GDAL_DRIVER_PATH:D:\gdal\bin\gdal\plugins
PROJ_LIB:D:\gdal\bin\proj9\share
现有PATH变量中添加:
D:\gdal\bin
D:\gdal\bin\gdal-data
D:\gdal\bin\gdal\java
D:\gdal\bin\gdal\apps
D:\gdal\bin\gdal\plugins-external
D:\gdal\bin\gdal\plugins-optional
3、将GDAL库引入JAVA项目工程
在java项目创建lib\gdal目录,将相关.dll文件和D:\gdal\bin\gdal\java\gdal-3.7.2.jar文件复制到该目录下。
在java项目build.gradle的dependencies代码块中增加:implementation(files("lib/gdal/gdal-3.7.2.jar")),如下图:
4、用GDAL库将坐标加入瓦片拼接图
在java运行程序代码中引入gdal类库,如下:
调用gdal为拼接后的图片写入经纬度信息,具体代码如下:
/**
* 为拼接图增加坐标信息,生成tiff文件
*
* @param mergeFileName 瓦片拼接图路径
* @param tiffFilename 生成tiff文件路径
*/
public static void geotiff(String mergeFileName, String tiffFilename) {
System.out.println("生成tifff增加坐标信息。。。");
// 注册
gdal.AllRegister();
/*
* 定义空间参考,由于WGS84是世界上比较知名的大地坐标系统,所以可以使用SetWellKnownGeogCS定义,从而简化代码。
* 如果要定义其他空间参考系,可以调用ImportFromEPSG方法实现,前提是该空间参考系在EPSG编码中
* 如果不在EPSG编码中的自定义空间参考系,可以使用wkt字符串描述空间参考系,然后调用dataset.SetProjection()来定义投影
*/
SpatialReference spatialReference = new SpatialReference();
spatialReference.SetWellKnownGeogCS("WGS84");
// 定义仿射转换参数(从设置的开始点经纬度获取)
double[] gt = { startLon, 0.1, 0.0, startLat, 0.0, -0.1 };
// 建立源图片数据集
// System.out.println(mergeFileName);
Dataset dataSoure = gdal.Open(mergeFileName, 0); // 可写状态
int xSize = dataSoure.getRasterXSize();
int ySize = dataSoure.getRasterYSize();
System.out.println("X " + xSize);
System.out.println("Y " + ySize);
// 获取图片大小
// // 获取驱动
Driver driver = gdal.GetDriverByName("GTiff");
// Dataset dataset = driver.Create(tiffFilename, xSize, ySize);
// 获取数据集,一般来说需要指定数据类型,默认为Byte - 8位3无符号整型
driver.CreateCopy(tiffFilename, dataSoure);
// 获取新生成的tiff
Dataset dataset = gdal.Open(tiffFilename, 1);
// 配置信息含坐标存入dataset中
dataset.SetGeoTransform(gt);
dataset.SetSpatialRef(spatialReference);
// 将缓存数据写入磁盘
dataset.FlushCache();
// 释放资源
dataset.delete();
}
瓦片拼接生成结果如下: