window java集成gdal
1.gdal下载
gdal下载官网地址: https://www.gisinternals.com/archive.php
下载后文件:

2.安装gdal:
安装gdal-3.9.0-1930-x64-core.msi,再安装gdal-3.9.0-1930-x64-filegdb.msi,一直下一步,filegdb用户数据导出
双击安装:t开头默认,c开头自定义
卸载:右键有卸载功能
安装后文件:

解压release-1930-x64-gdal-3-9-0-mapserver(地图服务器组件)-8-0-1.zip文件,然后把需要的文件都复制到解压的这个文件中:
解压后的文件:

把C:\Program Files\GDAL的文件gdalplugins文件夹和 FileGDBAPI.dll 都复制到 解压目录下的bin下


把解压目录下 gdal\java 下的 gdal.jar 和 gdalalljin.dll 复制到 bin 下

把解压目录下的 bin\gdal\apps下所有文件都复制到 bin下

3.环境变量配置:
GDAL_HOME : E:\02-SoftWare\use\gdal\gdal-3-9
GDAL_DRIVER_PATH : %GDAL_HOME%\bin\gdalplugins
GDAL_DATA: %GDAL_HOME%\bin\gdal-data
PROJ_LIB: E:\02-SoftWare\use\gdal\gdal-3-9\bin\proj9\share
path: %GDAL_HOME%\bin



4.测试:
打开cmd, 进到解压目录下的 bin目录, 输入 ogrinfo --formats , 如果有 FileGDB 就说明 ok了 (这里我自己改了文件夹名字,原始的是 bin)
gdalinfo --version

简单介绍gdal:
gdb是arcgis重要的数据格式之一(还有shp文件,java会处理这两种数据格式),它把矢量数据按以下层级结构存为本地文件

gdal提供了一些函数允许我们读取gdb,我们可以通过ini调用gdal来再java中读取gdal
5.Java开发:
在springboot项目中添加gd## 标题al.jar包



相关的jar:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.8</version>
</dependency>
<!--Geometry使用JTS包-->
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>org.wololo</groupId>
<artifactId>jts2geojson</artifactId>
<version>0.17.0</version>
</dependency>
java测试gdb代码:
public static void main(String[] args) throws Exception {
gdal.AllRegister();
int count = ogr.GetDriverCount();
for (int i = 0; i < count; i++) {
String driverName = ogr.GetDriver(i).getName();
System.out.print(driverName + "\t");
}
gdal.GDALDestroyDriverManager();
}
java导入导出gdal,shp文件:
package com.kjh.gdal.utils;
import cn.hutool.core.io.FileUtil;
import com.kjh.gdal.pojo.poi.GdalExportField;
import com.kjh.gdal.pojo.poi.PoiGdalData;
import com.kjh.gdal.pojo.poi.PoiShpData;
import org.gdal.gdal.gdal;
import org.gdal.ogr.*;
import org.gdal.osr.SpatialReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;
import java.lang.reflect.Field;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.BiFunction;
/**
* @program: jstool
* @description: <p>
* @author: KJH
* @create: 2022-11-25 13:39
*/
public class GDALUtil {
/**
* GDAL 数据导入/导出工具类
* 支持 FileGDB、OpenFileGDB、ESRI Shapefile 等 OGR 支持的格式
*/
private static final Logger logger = LoggerFactory.getLogger(GDALUtil.class);
// 驱动名称常量
private static final String DRIVER_FILE_GDB = "FileGDB";
private static final String DRIVER_OPEN_FILE_GDB = "OpenFileGDB";
private static final String DRIVER_ESRI_SHAPEFILE = "ESRI Shapefile";
// 注册标志
private static volatile boolean registered = false;
static {
register();
}
private GDALUtil() {
// 工具类不允许实例化
}
/**
* 注册 GDAL 驱动并设置环境配置
*/
public static synchronized void register() {
if (registered) {
return;
}
try {
logger.info("GDAL 版本信息: {}", gdal.VersionInfo("--version"));
gdal.AllRegister();
gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
gdal.SetConfigOption("SHAPE_ENCODING", "CP936");
registered = true;
logger.info("GDAL 注册成功");
} catch (Exception e) {
logger.error("GDAL 注册失败", e);
throw new RuntimeException("GDAL 初始化失败", e);
}
}
// ======================== 导入功能 ========================
/**
* 通用导入方法,自动检测数据源格式
* 支持 GDB、Shapefile、GeoJSON 等所有 OGR 支持的格式
*
* @param filePath 文件路径(.gdb 文件夹、.shp 文件、.geojson 文件等)
* @return 包含所有要素属性及几何(WKT 格式)的列表
*/
public static List<Map<String, Object>> importFromFile(String filePath) {
register();
List<Map<String, Object>> result = new ArrayList<>();
DataSource dataSource = null;
try {
// 使用 ogr.Open 自动检测格式
dataSource = ogr.Open(filePath, 0);
if (dataSource == null) {
logger.warn("无法打开数据源: {}", filePath);
return result;
}
int layerCount = dataSource.GetLayerCount();
logger.info("数据源包含 {} 个图层", layerCount);
for (int i = 0; i < layerCount; i++) {
Layer layer = dataSource.GetLayer(i);
if (layer == null) continue;
String layerName = layer.GetName();
logger.info("读取图层: {}", layerName);
Feature feature;
while ((feature = layer.GetNextFeature()) != null) {
try {
Map<String, Object> row = new LinkedHashMap<>();
// 获取几何
Geometry geometry = feature.GetGeometryRef();
if (geometry != null) {
String geoJson = geometry.ExportToJson();
String wkt = WktGeomjsonUtils.geojson2Wkt(geoJson);
row.put("POINTS", wkt);
}
// 获取所有属性字段
for (int j = 0; j < feature.GetFieldCount(); j++) {
String fieldName = feature.GetFieldDefnRef(j).GetName();
Object value = readFieldValue(feature, j);
row.put(fieldName, value);
}
result.add(row);
} finally {
feature.delete();
}
}
}
logger.info("导入完成,共 {} 条记录", result.size());
} catch (Exception e) {
logger.error("导入数据失败", e);
throw new RuntimeException("导入数据失败: " + e.getMessage(), e);
} finally {
if (dataSource != null) {
dataSource.delete();
}
}
return result;
}
/**
* 从 GDB 文件导入数据(保留原方法,保持向后兼容)
*
* @param gdbPath GDB 文件夹或 .gdb 路径
* @return 包含所有要素属性及几何(WKT 格式)的列表
*/
public static List<Map<String, Object>> importFromGdb(String gdbPath) {
// 直接调用通用方法
return importFromFile(gdbPath);
}
/**
* 从 Feature 读取指定索引字段的值,根据字段类型自动转换
*/
private static Object readFieldValue(Feature feature, int index) {
int type = feature.GetFieldType(index);
return FieldTypeHandler.readValue(feature, index, type);
}
/**
* 字段类型处理器枚举
*/
private enum FieldTypeHandler {
INTEGER(ogr.OFTInteger, (f, i) -> f.GetFieldAsInteger(i)),
INTEGER_LIST(ogr.OFTIntegerList, (f, i) -> f.GetFieldAsIntegerList(i)),
REAL(ogr.OFTReal, (f, i) -> f.GetFieldAsDouble(i)),
REAL_LIST(ogr.OFTRealList, (f, i) -> f.GetFieldAsDoubleList(i)),
STRING(ogr.OFTString, (f, i) -> f.GetFieldAsString(i)),
STRING_LIST(ogr.OFTStringList, (f, i) -> f.GetFieldAsStringList(i)),
BINARY(ogr.OFTBinary, (f, i) -> f.GetFieldAsBinary(i)),
DATE(ogr.OFTDate, (f, i) -> {
int[] year = new int[1], month = new int[1], day = new int[1];
int[] hour = new int[1], minute = new int[1], tz = new int[1];
float[] second = new float[1];
f.GetFieldAsDateTime(i, year, month, day, hour, minute, second, tz);
return java.sql.Date.valueOf(LocalDate.of(year[0], month[0], day[0]));
}),
TIME(ogr.OFTTime, (f, i) -> {
int[] year = new int[1], month = new int[1], day = new int[1];
int[] hour = new int[1], minute = new int[1], tz = new int[1];
float[] second = new float[1];
f.GetFieldAsDateTime(i, year, month, day, hour, minute, second, tz);
int sec = (int) second[0];
int nano = (int) ((second[0] - sec) * 1_000_000_000);
return Time.valueOf(LocalTime.of(hour[0], minute[0], sec, nano));
}),
DATETIME(ogr.OFTDateTime, (f, i) -> {
int[] year = new int[1], month = new int[1], day = new int[1];
int[] hour = new int[1], minute = new int[1], tz = new int[1];
float[] second = new float[1];
f.GetFieldAsDateTime(i, year, month, day, hour, minute, second, tz);
int sec = (int) second[0];
int nano = (int) ((second[0] - sec) * 1_000_000_000);
LocalDateTime ldt = LocalDateTime.of(
LocalDate.of(year[0], month[0], day[0]),
LocalTime.of(hour[0], minute[0], sec, nano)
);
return Timestamp.valueOf(ldt);
}),
INTEGER64(ogr.OFTInteger64, (f, i) -> f.GetFieldAsInteger64(i)),
INTEGER64_LIST(ogr.OFTInteger64List, (f, i) -> f.GetFieldAsIntegerList(i));
final int ogrType;
final BiFunction<Feature, Integer, Object> reader;
FieldTypeHandler(int ogrType, BiFunction<Feature, Integer, Object> reader) {
this.ogrType = ogrType;
this.reader = reader;
}
static Object readValue(Feature feature, int index, int type) {
for (FieldTypeHandler handler : values()) {
if (handler.ogrType == type) {
try {
return handler.reader.apply(feature, index);
} catch (Exception e) {
logger.warn("读取字段索引 {} 失败,类型码: {}", index, type, e);
return null;
}
}
}
return feature.GetFieldAsString(index);
}
}
// ======================== 导出功能 ========================
/**
* 导出 POI 数据到 GDB(要素类)
*/
public static void exportToGdb(List<?> dataList, String savePath) {
export(dataList, savePath, DRIVER_FILE_GDB, ogr.wkbPoint);
}
/**
* 导出 POI 数据到 Shapefile
*/
public static void exportToShapefile(List<?> dataList, String savePath) {
logger.info("实际导出类型: {}", dataList.get(0).getClass().getName());
export(dataList, savePath, DRIVER_ESRI_SHAPEFILE, ogr.wkbPoint);
}
/**
* 通用导出方法
*/
private static void export(List<?> dataList, String savePath, String driverName, int geometryType) {
register();
if (dataList == null || dataList.isEmpty()) {
logger.warn("导出数据为空,跳过");
return;
}
DataSource dataSource = null;
try {
Driver driver = ogr.GetDriverByName(driverName);
if (driver == null) {
throw new IllegalStateException("驱动 " + driverName + " 未找到");
}
dataSource = driver.CreateDataSource(savePath);
if (dataSource == null) {
throw new RuntimeException("创建 DataSource 失败: " + savePath);
}
SpatialReference sr = new SpatialReference();
sr.ImportFromEPSG(4490);
Layer layer = dataSource.CreateLayer("POI", sr, geometryType, null);
if (layer == null) {
throw new RuntimeException("创建图层失败");
}
Class<?> dataClass = dataList.get(0).getClass();
List<Field> exportFields = getExportFields(dataClass);
createFields(layer, exportFields);
writeFeatures(layer, dataList, exportFields);
dataSource.SyncToDisk();
dataSource.FlushCache();
logger.info("数据导出成功: {}", savePath);
} catch (Exception e) {
logger.error("导出到 {} 失败: {}", driverName, savePath, e);
throw new RuntimeException("导出失败", e);
} finally {
if (dataSource != null) {
dataSource.delete();
}
}
}
/**
* 获取带有 @GdalExportField 注解的字段列表
*/
private static List<Field> getExportFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(GdalExportField.class)) {
field.setAccessible(true);
fields.add(field);
}
}
return fields;
}
/**
* 在图层上创建字段
*/
private static void createFields(Layer layer, List<Field> fields) {
for (Field field : fields) {
GdalExportField annotation = field.getAnnotation(GdalExportField.class);
if (annotation.isGeom()) {
continue;
}
String fieldName = annotation.name().isEmpty() ? field.getName() : annotation.name();
int width = annotation.length();
// 字符串字段最大宽度 254
if (annotation.type() == ogr.OFTString) {
if (width == 0 || width > 254) {
width = 254;
}
}
FieldDefn fieldDefn = new FieldDefn();
fieldDefn.SetName(fieldName);
fieldDefn.SetType(annotation.type());
fieldDefn.SetWidth(width);
if (!annotation.alternativeName().isEmpty()) {
fieldDefn.SetAlternativeName(annotation.alternativeName());
}
if (!annotation.nullAble()) {
fieldDefn.SetNullable(0);
}
if (!annotation.defaultValue().isEmpty()) {
fieldDefn.SetDefault(annotation.defaultValue());
}
fieldDefn.SetSubType(annotation.subType());
layer.CreateField(fieldDefn);
}
}
/**
* 写入要素数据
*/
private static void writeFeatures(Layer layer, List<?> dataList, List<Field> fields) throws IllegalAccessException {
FeatureDefn featureDefn = layer.GetLayerDefn();
for (Object obj : dataList) {
Feature feature = new Feature(featureDefn);
boolean hasGeom = false;
boolean skipFeature = false;
for (Field field : fields) {
GdalExportField annotation = field.getAnnotation(GdalExportField.class);
String fieldName = annotation.name().isEmpty() ? field.getName() : annotation.name();
Object value = field.get(obj);
if (value == null) continue;
if (annotation.isGeom()) {
Geometry geometry = toGeometry(value);
if (geometry == null) {
logger.warn("要素几何无效,跳过: {}", obj);
skipFeature = true;
break;
}
feature.SetGeometry(geometry);
hasGeom = true;
} else {
setFeatureField(feature, fieldName, annotation.type(), value);
}
}
if (skipFeature || (!hasGeom && layer.GetGeomType() != ogr.wkbNone)) {
feature.delete();
continue;
}
try {
layer.CreateFeature(feature);
} catch (Exception e) {
logger.error("创建要素失败,跳过该要素", e);
} finally {
feature.delete();
}
}
}
/**
* 将对象转换为 GDAL Geometry
*/
private static Geometry toGeometry(Object value) {
if (value instanceof Geometry) {
return (Geometry) value;
}
if (value instanceof String) {
try {
String wkt = ((String) value).trim();
return Geometry.CreateFromWkt(wkt);
} catch (Exception e) {
logger.warn("WKT 转换失败: {}", value, e);
return null;
}
}
return null;
}
/**
* 根据字段类型设置 Feature 字段值
*/
private static void setFeatureField(Feature feature, String fieldName, int fieldType, Object value) {
try {
switch (fieldType) {
case ogr.OFTInteger:
feature.SetField(fieldName, value.toString());
break;
case ogr.OFTReal:
feature.SetField(fieldName, Double.valueOf(value.toString()));
break;
case ogr.OFTString:
String strVal = value.toString();
if (strVal.length() > 254) {
strVal = strVal.substring(0, 254);
logger.warn("字段 {} 超长,已截断至254字符", fieldName);
}
feature.SetField(fieldName, strVal);
break;
case ogr.OFTDate:
case ogr.OFTDateTime:
setDateTimeField(feature, fieldName, value);
break;
case ogr.OFTBinary:
feature.SetField(fieldName, value.toString());
break;
default:
feature.SetField(fieldName, value.toString());
}
} catch (Exception e) {
logger.warn("设置字段 {} 失败,值: {}", fieldName, value, e);
}
}
/**
* 设置日期时间字段
*/
private static void setDateTimeField(Feature feature, String fieldName, Object value) {
if (value instanceof Date) {
Date date = (Date) value;
Calendar cal = Calendar.getInstance();
cal.setTime(date);
feature.SetField(fieldName,
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH) + 1,
cal.get(Calendar.DATE),
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
cal.get(Calendar.SECOND),
0);
} else if (value instanceof LocalDateTime) {
LocalDateTime ldt = (LocalDateTime) value;
feature.SetField(fieldName,
ldt.getYear(),
ldt.getMonthValue(),
ldt.getDayOfMonth(),
ldt.getHour(),
ldt.getMinute(),
ldt.getSecond(),
0);
} else if (value instanceof LocalDate) {
LocalDate ld = (LocalDate) value;
feature.SetField(fieldName,
ld.getYear(),
ld.getMonthValue(),
ld.getDayOfMonth(),
0, 0, 0, 0);
} else {
feature.SetField(fieldName, value.toString());
}
}
// ======================== 辅助方法 ========================
public static Geometry createGeometry(String wkt) {
if (ObjectUtils.isEmpty(wkt)) {
return null;
}
try {
return Geometry.CreateFromWkt(wkt);
} catch (Exception e) {
logger.warn("创建几何失败: {}", wkt, e);
return null;
}
}
// ======================== 测试入口 ========================
public static void main(String[] args) {
// 开启调试模式
// gdal.SetConfigOption("CPL_DEBUG", "ON");
// 导出 POI 数据
List<PoiGdalData> poiGdalList = buildPoiGdalDataList();
List<PoiShpData> poiShpList = buildPoiShpDataList();
String exportGDBPath = "D:\\website\\POI\\export_gdb";
String exportShapefilePath = "D:\\website\\POI\\export_gdb\\POI_shp";
FileUtil.del(exportGDBPath);
FileUtil.del(exportShapefilePath);
FileUtil.mkdir(exportGDBPath);
FileUtil.mkdir(exportShapefilePath);
exportGDBPath=exportGDBPath+"\\POITest.gdb";
exportShapefilePath=exportShapefilePath+"\\POITest.shp";
// 导出 Shapefile
GDALUtil.exportToShapefile(poiShpList, exportShapefilePath);
// 导出 GDB
GDALUtil.exportToGdb(poiGdalList, exportGDBPath);
// 导入 GDB
List<Map<String, Object>> data = GDALUtil.importFromGdb(exportGDBPath);
System.out.println("GDB 导入数据条数: " + data.size());
// 导入 Shapefile(使用通用方法)
List<Map<String, Object>> data2 = GDALUtil.importFromFile(exportShapefilePath);
System.out.println("Shapefile 导入数据条数: " + data2.size());
}
private static List<PoiShpData> buildPoiShpDataList() {
List<PoiShpData> list = new ArrayList<>();
// 第一条数据
PoiShpData poi1 = new PoiShpData();
poi1.setPoiid("P001");
poi1.setName("绍兴泉岗机械科技有限公司");
poi1.setAddress("三界镇上官岭(104国道1572段)");
poi1.setType("公司企业;公司;机械电子");
poi1.setGeom("POINT(120.840486 29.743005)"); // WKT 格式,经度在前
list.add(poi1);
// 第二条数据
PoiShpData poi2 = new PoiShpData();
poi2.setPoiid("P002");
poi2.setName("杭州西湖风景区");
poi2.setAddress("杭州市西湖区龙井路1号");
poi2.setType("风景区;旅游景点");
poi2.setGeom("POINT(120.130778 30.258708)");
list.add(poi2);
// 可以继续添加更多数据...
return list;
}
/**
* 构建示例 POI 数据
*/
private static List<PoiGdalData> buildPoiGdalDataList() {
List<PoiGdalData> list = new ArrayList<>();
// 第一条数据
PoiGdalData poi1 = new PoiGdalData();
poi1.setPoiid("P001");
poi1.setName("绍兴泉岗机械科技有限公司");
poi1.setAddress("三界镇上官岭(104国道1572段)");
poi1.setType("公司企业;公司;机械电子");
poi1.setGeom("POINT(120.840486 29.743005)"); // WKT 格式,经度在前
list.add(poi1);
// 第二条数据
PoiGdalData poi2 = new PoiGdalData();
poi2.setPoiid("P002");
poi2.setName("杭州西湖风景区");
poi2.setAddress("杭州市西湖区龙井路1号");
poi2.setType("风景区;旅游景点");
poi2.setGeom("POINT(120.130778 30.258708)");
list.add(poi2);
// 可以继续添加更多数据...
return list;
}
}
PoiGdalData(PoiShpData同字段):
@Data
@ExcelIgnoreUnannotated
public class PoiGdalData {
@GdalExportField(length = 50,name = "ID"/*,nullAble = false*/)
private String poiid;
@GdalExportField(length = 100,name = "NAME",nullAble = false)
private String name;
@GdalExportField(length = 200,name = "ADDRESS")
private String address;
@GdalExportField(name = "TYPE")
private String type;
@GdalExportField(name = "geom",isGeom = true)
private String geom;
public PoiGdalData() {
}
}
WktGeomjsonUtils:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
import org.wololo.geojson.GeoJSON;
import org.wololo.jts2geojson.GeoJSONReader;
import org.wololo.jts2geojson.GeoJSONWriter;
public class WktGeomjsonUtils {
/**
* geojson 转 wkt
*
* @param geojson
* @return
* @throws Exception
*/
public static String geojson2Wkt(String geojson) throws Exception {
Geometry geometry = geojson2Geometry(geojson);
return geometry2Wkt(geometry);
}
}
GdalExportField :
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GdalExportField {
String name() default ""; //字段名,默认使用model字段名
int type() default ogr.OFTString; //字段类型
int length() default 100; //字段长度
String alternativeName() default ""; //字段别名
boolean nullAble() default true;
String defaultValue() default "";
int subType() default 0;
boolean isGeom() default false;
}
以上基本就是java处理gdal和shp文件的大致流程,这里仅供参考学习,本人也参考了别人的文档,翻看了好多文档,感觉很麻烦,所以整理起来放在一起,经测试基本可以使用.
更多推荐


所有评论(0)