windows 安装 GDAL并用Java集成GDAL

前言:

GDAL(Geospatial Data Abstraction Library)是一个在X/MIT许可协议下的开源栅格空间数据转换库。它利用抽象数据模型来表达所支持的各种文件格式。它还有一系列命令行工具来进行数据转换和处理。

  • 我这里主要是用作:autocad的xxx.dxf格式文件转成xxx.geojson格式文件。
  • 思路:gdalc++写的,安装调用它可以用命令行,也可以集成到java项目上。所以首先要在web服务器上安装gdal,然后把gdal提供的jar依赖引到java项目中,并且把gdal的一些动态链接库(win系统是.dll文件,linux.so文件)给到jdk去调用。
    我这里是本地开发,所以本文记录的是本地windows系统上搭环境。
    linux系统,请参考:linux 安装 GDAL 3.3.2 - (jianshu.com)

一、下载

下载gdal windows版本64位最新的稳定版,前往:GISInternals Support Site

image.png

image.png

二、安装gdal

双击打开:
image.png

选择custom安装即可。

三、配置环境变量

Note: In order to have the bindings work the location of the core components must be included manually in the PATH environment variable.

  • a.系统变量-path中新增:C:\Program Files\GDAL\
  • b.系统变量-新增-变量名:GDAL_DATA,变量值:C:\Program Files\GDAL\gdal-data
  • c.系统变量-新增-变量名:GDAL_DRIVER_PATH,变量值:C:\Program Files\GDAL\gdalplugins
    测试gdal安装是否成功,cmd输入:
    # gdalinfo --version
    image.png

四、java集成gdal

说明
java集成gdal主要是引gdal.jar依赖以及配置jdk可以调用到.dll动态链接库。
引jar包可以有很多种方式,比如:

  • 直接引入(不推荐);
  • 打到本地maven仓库之后pom.xml引入(推荐);
  • jar包复制到src/main/resources/下,直接在pom.xml中引入(最简单推荐,下文说的就是这种);

步骤

  1. 本地使用springboot自建web应用,maven作为项目管理和构建工具;
  2. 把C:\Program Files\GDAL\下所有的.dll文件全部复制到jdk/bin/下;
  3. 把C:\Program Files\GDAL\java\下的gdal.jar和C:\Program Files\GDAL\下的gdalalljni.dll复制到src/main/resources/gdal/win32/下;
    如图:


    image.png

注意的就是linux的jar包和win32的jar包可能不一样,linux的要去下载gdal二进制资源然后编译等,而且第2第3部应用动态链接库也完全不一样,后期我再验证更新。我这里先说下win的开发环境配置。

  1. pom.xml中引入gdal.jar包:

        ...
        src/main/resources/gdal/win32
    

        
            org.gdal
            gdal
            3.5.0
            system
            ${project.basedir}/${gdal.binddir}/gdal.jar
        

  1. 测试dxf文件转geojson。
    说明:把.dxf转成.geojson,转换后的geojson可读性很强导致文件过大,所以我使用分页分文件夹去转换,详情见测试代码源码。


    image.png

    效果:


    image.png

    image.png

    image.png

源码:

说明:源码中涉及到下面几点的可以先忽略注释或者删掉

  • 转换可能导致中文乱码,我这里在源码注掉了本篇不涉及,可以参见我这篇文章:GDAL 在把 dxf 转成 geojson 后,cad的图层名中文乱码问题 - (jianshu.com)
  • 在FileUtil.java中可以看到main方法里有个dealDxf(path, inputFileName);方法,是手动处理个别cad图转换报错的问题,
    原因:查看gdal的c++源码,在解析dxf中的id字符串属性长度超长报错,dxf实际就是个文本查看它发现里面有个别元素的id字符串过长,而且是重复有规律的(类似于在同文件夹下一直复制同一个文件windows会自动给新文件重命名),猜测是制图师在编辑cad时无脑复制粘贴某些元素导致。
    解决:用计数的方式去处理这种超长id,如:把 id_1_1 转成 id_002,把 id_1_1_1_1 转成 id_004,把 id_1_1_1_1_1_1_1_1_1_1_1_1 转成 id_012。
  1. pom.xml:


    4.0.0

    com.ckk
    gismap
    1.0-SNAPSHOT

    
        8
        8
        src/main/resources/gdal/win32
    

    

        
            org.gdal
            gdal
            3.5.0
            system
            ${project.basedir}/${gdal.binddir}/gdal.jar
        

        





















        
        
            org.slf4j
            slf4j-api
            2.0.0-alpha7
        

        
        
            org.projectlombok
            lombok
            1.18.24
            provided
        

        
        
            cn.hutool
            hutool-all
            5.8.2
        


    


  1. test.java:
package test;

import lombok.extern.slf4j.Slf4j;
import org.gdal.gdal.gdal;
import org.gdal.ogr.DataSource;
import org.gdal.ogr.*;
import org.gdal.osr.SpatialReference;
import util.file.FileUtil;

/**
 * gdal测试类
 * @author ckk
 * @create 2022-08-06 16:07
 **/
@Slf4j
public class Test {

    public static void main(String[] args) {
        // 1.dxf 转 geojson
        boolean b = dxf2GeoJson("D:\\cad2wms\\", "葫芦素-最新.dxf", "D:\\cad2wms\\hulusu_colliery_id\\");
        System.out.println("bbbbbb==="+b);
    }

    /**
     * dxf转geojson(矢量拆分)
     * @param inputPath
     * @param cadFileName
     * @param outputPath
     * @return
     */
    public static boolean dxf2GeoJson(String inputPath, String cadFileName, String outputPath){
        try {
            ogr.RegisterAll();
            // gdal.SetConfigOption 选项配置参见:https://trac.osgeo.org/gdal/wiki/ConfigOptions
            // 为了支持中文路径,请添加下面这句代码
            gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
            // 为了使属性表字段支持中文,请添加下面这句
            gdal.SetConfigOption("SHAPE_ENCODING", "");

            /**
             * 判断编码dxf文件编码类型:
             * 在cad另存为dxf时,由于不同版本问题导致编码不同。
             * 已知:dxf >=2007 版本编码为 UTF-8,其他低版本编码为 GB2312
             * 若为 UTF-8 需要设置:gdal.SetConfigOption("DXF_ENCODING", "ASCII");
             */
//            String charset = EncodingDetector.getCharset(new File(inputPath + cadFileName));
//            if(null != charset && charset.equals("UTF-8")){
//                //设置DXF缺省编码
//                gdal.SetConfigOption("DXF_ENCODING", "ASCII");
//            }

//        String inputPath = "F:\\ckk\\mapbox研究\\java集成gdal\\xjlmk.dxf";
            DataSource ds = ogr.Open(inputPath + cadFileName, 0);
            if (ds == null) {
                log.info("打开dxf文件失败!filePath="+inputPath + cadFileName);
                return false;
            }
            log.info("打开dxf文件成功!");

            // 拆分矢量文件为多个单要素矢量文件,注意拆分后的fid需要重置,
            String fieldName = null;
//        String saveFolderPath = "F:\\ckk\\mapbox研究\\java集成gdal\\xjlmk-1628561264202\\";// 生成的矢量文件保存目录
            String saveFolderPath = outputPath;// 生成的矢量文件保存目录

            boolean dirExists = FileUtil.judgeDirExists(saveFolderPath, true);
            if(!dirExists){
                log.error("dxf2GeoJson,文件目录创建失败!saveFolderPath ="+saveFolderPath);
                return false;
            }

            Driver dv = ogr.GetDriverByName("GeoJSON");
            if (dv == null) {
                log.error("打开GeoJSON驱动失败!");
                return false;
            }
            log.info("打开GeoJSON驱动成功!");

            Layer layer = ds.GetLayer(0);
            SpatialReference spatial = layer.GetSpatialRef();
            int geomType = layer.GetGeomType();
            FeatureDefn layerDefn = layer.GetLayerDefn();
            int fieldCount = layerDefn.GetFieldCount();
            Feature feature = layer.GetNextFeature();
            int count = 1;
            while(feature != null){
//                Feature featureCopy = feature.Clone();
                String outName = "", outLayerName = "";
                if(fieldName ==  null){
                    Long fid = feature.GetFID();
//                    int fid = feature.GetFID();
                    outName = String.valueOf(fid);
                    outLayerName = String.valueOf(fid);
                }else{
                    outLayerName = feature.GetFieldAsString(fieldName);
                    outName = outLayerName;
                }
                if(outName == null){
                    continue;
                }
                String outFileName = outName + ".geojson";
                int dir = count % 100;// 需要分页储存,因为geojson文件过多,严重影响写入和读取效率。
                String subDir = saveFolderPath + dir + "/";
                if(!FileUtil.judgeDirExists(subDir, true)){
                    log.error("dxf2GeoJson,文件子目录创建失败!subDir ="+subDir);
                    return false;
                }
                String outFilePath = subDir + outFileName;
                DataSource outData = dv.CreateDataSource(outFilePath);
                Layer outLayer = outData.CreateLayer(outLayerName, spatial, geomType);
                for(int i=0; i
  1. FileUtil.java
package util.file;

import lombok.extern.slf4j.Slf4j;
import java.io.*;

/**
 * file io工具类
 *
 * @author ckk
 * @create 2022-08-06 19:44
 **/
@Slf4j
public class FileUtil {

    public static int totalLine = 0;

    public static void main(String[] args) throws IOException {
        String path = "F:\\ckk\\资料\\超图底图\\dxf做离线底图\\红庆河煤矿配准20220110\\处理dxf文件\\";
        String inputFileName = "采掘工程平面图(新)20220110";
        // 处理dxf文件,重复id的问题,lr_blocked_124_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1
        dealDxf(path, inputFileName);
    }

    /**
     * 处理dxf文件,_1_1_1_1 重复id的问题
     * @param path
     * @param inputFileName
     */
    public static void dealDxf(String path, String inputFileName){
        String suffix = ".dxf";
        String outputFileName = inputFileName + "_校正后";

        totalLine = 0;
        try {
            String inputFilePathName = path + inputFileName + suffix;
            String outputFilePathName = path + outputFileName + suffix;
            // 判断是否存在
            judgeFileExists(outputFilePathName, true);
            InputStreamReader isr = new InputStreamReader(new FileInputStream(inputFilePathName), "UTF-8");
            BufferedReader br = new BufferedReader(isr);

            StringBuffer sb = new StringBuffer();
            String temp = null;

            // 1000行写一次
            int page = 1000;
            int curLine = 0;
            while((temp = br.readLine()) != null){
                temp = dealLine(temp);
                sb.append(temp);
                sb.append("\r\n");
                curLine++;
                if(curLine == page){
                    // 追加写入
                    appendWriteFile(outputFilePathName, sb.toString());

                    // 重置
                    curLine = 0;
                    sb = new StringBuffer();
                }
            }
            // 追加写入
            appendWriteFile(outputFilePathName, sb.toString());

            br.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();

        }catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("一共处理:"+totalLine+"行!!!");
    }

    /**
     * 处理dxf文件每行过长重复的id字符串,
     * 如:lr_blocked_124_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1
     * @param line
     * @return
     */
    public static String dealLine(String line){
//        String line = "lr_blocked_124_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1_1";
        if(line!=null && line.length() > 2 && line.substring(line.length()-2).equals("_1")){
            String[] split = line.split("_");
            int count = 0;
            String id = "";
            for(int i = split.length -1; i > 0; i--){
                if(split[i].equals("1")){
                    count++;
                }else {
                    for(int j = 0; j <= i; j++){
                        id += split[j] + "_";
                    }
                    break;
                }
            }
            id += count < 10 ? "00" + count : count < 100 ? "0" + count : count;
            line = id;
            System.out.println("line:"+line);
            totalLine++;
        }
        return line;
    }

    /**
     * 追加写文件
     * @param outputFilePathName
     * @param content
     * @return
     */
    public static void appendWriteFile(String outputFilePathName, String content){
        FileWriter fwriter = null;
        try {
            fwriter = new FileWriter(outputFilePathName, true);
            fwriter.write(content);
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                fwriter.flush();
                fwriter.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * 获取路径下所有文件或文件夹
     * @param path
     * @return
     */
    public static File[] listFiles(String path) {
        File f = new File(path);//获取路径  F:\测试目录
        if (!f.exists()) {
            System.out.println(path + " not exists");// 不存在就输出
            return null;
        }
        File[] files = f.listFiles();// 用数组接收
        return files;
    }

    /**
     * 读文件
     * @param fileName
     * @return
     */
    public static String readFile(String fileName) {
        String jsonStr = "";
        try {
            File jsonFile = new File(fileName);
            FileReader fileReader = new FileReader(jsonFile);
            Reader reader = new InputStreamReader(new FileInputStream(jsonFile),"utf-8");
            int ch = 0;
            StringBuffer sb = new StringBuffer();
            while ((ch = reader.read()) != -1) {
                sb.append((char) ch);
            }
            fileReader.close();
            reader.close();
            jsonStr = sb.toString();
            return jsonStr;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 写文件
     * @param outputPath
     * @param fileName
     * @param content
     * @return
     */
    public static boolean writeFile(String outputPath, String fileName, String content){
        File dir = new File(outputPath);
        boolean dirAvailable = FileUtil.judgeDirExists(dir, true);
        if(!dirAvailable){
            System.out.println("fileName:"+fileName+",写入失败;原因:写入路径不可用!dir:"+outputPath);
            return false;
        }
        String filePath = outputPath + fileName;
        File file = new File(filePath);
        boolean fileAvailable = FileUtil.judgeFileExists(file, true);
        if(!fileAvailable){
            System.out.println("fileName:"+fileName+",写入失败;原因:写入文件不可用!file:"+filePath);
            return false;
        }
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(filePath));
            // 执行写入操作
            bw.write(content);
            return true;
        } catch (IOException e) {
            log.error("fileName:"+fileName+",写入失败!!!!!!!!!");
            return false;
        }finally {
            //关闭流
            try {
                bw.close();
            } catch (IOException e) {
                log.error("bw.close()失败。。");
            }
        }
    }

    /**
     * 判断文件是否存在,不存在 && create ,则创建
     * @param filePath
     * @param create true:不存在时,创建; false:不存在时,不创建
     * @return
     */
    public static boolean judgeFileExists(String filePath, boolean create) {
        return judgeFileExists(new File(filePath), create);
    }

    /**
     * 判断文件是否存在,不存在 && create ,则创建
     * @param file
     * @param create true:不存在时,创建; false:不存在时,不创建
     * @return
     */
    public static boolean judgeFileExists(File file, boolean create) {
        if (file.exists()) {
        } else {
            if(!create)return false;
            try {
                return file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    /**
     * 判断目录是否存在,不存在 && create ,则创建
     * @param dirPath
     * @param create true:不存在时,创建; false:不存在时,不创建
     * @return
     */
    public static boolean judgeDirExists(String dirPath, boolean create) {
        return judgeDirExists(new File(dirPath), create);
    }

    /**
     * 判断目录是否存在,不存在 && create ,则创建
     * @param dir
     * @param create true:不存在时,创建; false:不存在时,不创建
     * @return
     */
    public static boolean judgeDirExists(File dir, boolean create) {
        if (dir.exists()) {
            if (dir.isDirectory()) {
            } else {
                return mkdirs(dir, create);
            }
            return true;
        } else {
            return mkdirs(dir, create);
        }
    }

    /**
     * 递归删除目录下所有文件
     * @param dirOrFilePath
     */
    public static void delDir(String dirOrFilePath) {
        delDir(new File(dirOrFilePath));
    }

    /**
     * 递归删除目录下所有文件
     * @param dirOrFile
     */
    public static void delDir(File dirOrFile) {
        if (dirOrFile.isDirectory()) {
            File zFiles[] = dirOrFile.listFiles();
            for (File file2 : zFiles) {
                delDir(file2);
            }
            dirOrFile.delete();
        } else {
            dirOrFile.delete();
        }
    }

    /**
     * 将InputStream写入本地文件(将InputStream文件流转换为File文件)
     * @param destination
     * @param input
     * @return
     */
    public static boolean inputStreamToFile(String destination, InputStream input){
        int index;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = null;
        try {
            downloadFile = new FileOutputStream(destination);
            while ((index = input.read(bytes)) != -1) {
                downloadFile.write(bytes, 0, index);
                downloadFile.flush();
            }
        } catch (FileNotFoundException e) {
            log.error("error:inputStreamToFile:FileNotFoundException");
            return false;
        } catch (IOException e) {
            log.error("error:inputStreamToFile:IOException");
            return false;
        }finally {
            try {
                input.close();
                downloadFile.close();
            } catch (IOException e) {
            }
        }
        return true;
    }

    private static boolean mkdirs(File dirPathFile, boolean create){
        if(!create)return false;
        return dirPathFile.mkdirs();
    }

}

参考:2、JAVA集成GDAL-JAVA开发环境整合GDAL - (jianshu.com)

你可能感兴趣的:(windows 安装 GDAL并用Java集成GDAL)