搭建GDAL JAVA环境;DXF转KML;坐标转换;PROJ: proj_create_from_database 错误解决

搭建JAVA GDAL环境

GDAL是一个栅格和矢量地理空间数据格式的转换库,由开源地理空间基金会按照MIT开源协议发布。作为一个库,它向应用程序为所有支持的数据格式提供统一的栅格抽象数据模型和矢量抽象数据模型。它还提供了用于数据转换和处理的各种有用的命令行实用工具。

GDAL官网: GDAL — GDAL documentation

中文官网:GDAL — GDAL 文档 (osgeo.cn)

下载安装

GISInternals 下载GDAL生产环境包

GISInternals is an online system for creating daily built binary packages for the GDAL and MapServer projects.

GISIinternals是一个在线系统,用于为GDAL和MapServer项目创建每日构建的二进制包。

打开上述地址后,根据自己系统选择对应的版本

本地部署

  1. 解压后将GDAL的/bin文件夹下所有的.dll(不包括bin目录下的文件夹)和/bin/gdal/java下的dll文件拷贝到jdk安装路径的bin目录下

    1. 注意不要把GDAL解压到带有中文或空格的目录下
    2. JDK安装目录,指的是当前java程序依赖的jdk的安装目录,如使用的jre运行java程序则需要将相关dll文件方到jre的bin目录下【无法确定运行环境时,建议,直接给jdk和jre都放】
  2. 将GDAL的bin目录添加到Path的环境变量下【建议将以下图片的这些目录都添加到环境变量中,避免后续重复添加】

  3. 添加环境变量

    1. D:\gdal\bin\gdal\plugins 目录添加到环境变量,变量名为GDAL_DRIVER_PATH
    2. proj.db所在路径D:\gdal\bin\proj9\share添加到环境变量,变量名为PROJ_LIB

使用GDAL命令行

这个库分为两大部分是GDAL和OGR,分别管理着栅格和矢量数据。底层是c语言,上层可以是多种语言(包括python,java等)调用,下方是(栅格和矢量)数据处理的中文索引,以及中文帮助;

附官网数据来源:栅格:栅格raster— GDAL 文档矢量Programs — GDAL documentation

栅格

矢量

使用JAVA读取DXF文件并转为KML

  1. gdal安装目录下D:\gdal\bin\gdal\javabin目录下gdal下java目录中的gdal.jar复杂到java项目中

  1. 将gdal.jar添加到项目依赖中,右击gdal.jar选择Add as Library;

  1. java中复杂以下代码测试
import org.gdal.gdal.gdal;
import org.gdal.ogr.ogr;

//测试案例
class GdalDemo {
	//获取所有驱动的名称并打印
	public void printDriver() {
		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();
	}

	public static void main(String[] args) {
		GdalDemo demo = new GdalDemo();
		demo.printDriver();
	}
}
  1. 测试成功

  1. 完整代码

/**
 * 使用gdal 解析dxf,并将数据读取转为KML 或 GEOJSON
 * 包含坐标转换
 */
public class DXFToGeoJSONConverter {

    public static void main(String[] args) {

        Boolean converter = converter("E://test//1.kml", "E://test//白马2000-3-108.dxf");
        if (converter) {
            System.out.println("成功");
        } else {
            System.out.println("失败");
        }
    }

    /**
     *
     * @param outPath 输出文件全路径
     * @param inPath  读取文件全路径
     * @return
     */
    public static Boolean converter(String outPath, String inPath) {
        // 注册所有的驱动
        //gdal.AllRegister();
        ogr.RegisterAll();
        gdal.SetConfigOption("DWG_ENCODING", "UTF-8");//DWG文件编码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");//支持中文路径
        gdal.SetConfigOption("SHAPE_ENCODING", "CP936");//属性表字段支持中文


        DataSource dataSource = ogr.Open(inPath, 0);
        if (dataSource == null) {
            System.out.println("打开文件失败!" + gdal.GetLastErrorMsg());
            return false;
        }
        System.out.println("打开文件成功!");

        //构造坐标系转换对象
        SpatialReference src = new SpatialReference();
//        src.ImportFromProj4("+proj=tmerc +lat_0=0 +lon_0=108 +k=1 +x_0=36500000 +y_0=0 +ellps=GRS80 +units=m +no_defs");
        src.ImportFromEPSG(4545); // CGCS2000 中央经线 108 3分带  36带
        System.out.println("原坐标系:" + src.ExportToProj4());

        SpatialReference dest = new SpatialReference();
        dest.ImportFromEPSG(4326); // WGS84

        System.out.println("目标坐标系:" + dest.ExportToProj4());
//        SpatialReference dest = dest1.CloneGeogCS();
        CoordinateTransformation transformation = new CoordinateTransformation(src, dest);//源,目标
        //1.创建驱动程序
        Driver dv = ogr.GetDriverByName("KML"); //直接转KML
//        Driver dv = ogr.GetDriverByName("GeoJSON"); //直接转GEOJSON

        int layerCount = dataSource.GetLayerCount();
        System.out.println("总图层数:" + layerCount);
        DataSource ds = dv.CreateDataSource(outPath);

        for (int i = 0; i < layerCount; i++) {
            // 2.循环读取每个图层
            Layer inLayer = dataSource.GetLayerByIndex(i);
            if (inLayer == null) {
                continue;
            }
            String layerName = inLayer.GetName();
            //3.每个DataSource创建一个输出的layer
            Layer outLayer = ds.CreateLayer(layerName, dest, ogrConstants.wkbUnknown, null);
            long featureCount = inLayer.GetFeatureCount();
            System.out.println("总图形数:" + featureCount);
            for (long j = 0; j < featureCount; j++) {
                Feature feature = inLayer.GetFeature(j);
                feature.GetStyleString();
                //4.获取输入的几何对象,调用其转换方法,转换后存入新的layer中
                Geometry geometry = feature.GetGeometryRef();
                geometry.Transform(transformation);
                //注意,转换为后时 经度 纬度 的顺序,如果需要纬度在前,需要交换xy
                //  geometry.SwapXY();
                geometry.CloseRings();
                outLayer.CreateFeature(feature);
            }
        }
        System.out.println("转换成功!!!");
        return true;
    }
}

GDAL坐标转换


/**
 * 用gdal进行坐标转换
 */
public class CoordinateTransformationExample {
    public static void main(String[] args) {
        // 注册GDAL驱动
        gdal.AllRegister();

        // 输入的坐标
        double inputX = 36587998.113885;
        double inputY = 3796047.384520;

        // 创建源坐标系统 (CGCS2000 3度带,中央经线108)
        SpatialReference srcSRS = new SpatialReference();
        srcSRS.ImportFromProj4("+proj=tmerc +lat_0=0 +lon_0=108 +k=1 +x_0=36500000 +y_0=0 +ellps=GRS80 +units=m +no_defs");
        // 创建目标坐标系统 (WGS84)
        SpatialReference tgtSRS = new SpatialReference();
        tgtSRS.ImportFromEPSG(4326);
        // 创建坐标转换对象
        CoordinateTransformation transformation = new CoordinateTransformation(srcSRS, tgtSRS);


        // 执行坐标转换
        double[] transformedCoords = transformation.TransformPoint(inputX, inputY);

        // 输出转换后的经纬度 (转换后经纬度位置交换了,注意不要取错参数)
        System.out.println("Transformed Coordinates (WGS84):");
        System.out.println("Longitude: " + transformedCoords[1]);
        System.out.println("Latitude: " + transformedCoords[0]);
    }
}

可能遇到的错误

ERROR 1: PROJ: proj_create_from_database: D:\ideawork\pkgs\proj-6.2.1-h9f7ef89_0\Library\share\proj\proj.db lacks DATABASE.LAYOUT.VERSION.MAJOR / DATABASE.LAYOUT.VERSION.MINOR metadata. It comes from another PROJ installation.

原因分析:

使用osgeo.osr能够直接从安装的GDAL包读取指定投影类型的信息,在proj.db中存储了常用的投影系统的参数,因此指定EPSG号就能从proj.db中取到投影信息。

经过排查,我的情况是在开发环境中同时安装了GDAL和其他地理空间库(如PostGresql),此时同一环境路径下可能会产生多个proj.db文件,访问它的时候程序会无法判断访问哪个。因此只需要去查找gdal安装的那个proj.db在哪里,在程序中指定环境变量即可解决

解决方法:

其他参考文章:

Gdal 之 dxf转geojson (附加坐标转换)

gdal for java 从安装到各种案例demo实现

GDAL JAVA 开发文档