前言:
GDAL(Geospatial Data Abstraction Library)是一个在X/MIT许可协议下的开源栅格空间数据转换库。它利用抽象数据模型来表达所支持的各种文件格式。它还有一系列命令行工具来进行数据转换和处理。
- 我这里主要是用作:autocad的
xxx.dxf
格式文件转成xxx.geojson
格式文件。 - 思路:
gdal
是c++
写的,安装调用它可以用命令行,也可以集成到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
二、安装gdal
双击打开:选择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
四、java集成gdal
说明:
java集成gdal主要是引gdal.jar依赖以及配置jdk可以调用到.dll动态链接库。
引jar包可以有很多种方式,比如:
- 直接引入(不推荐);
- 打到本地maven仓库之后pom.xml引入(推荐);
- jar包复制到
src/main/resources/
下,直接在pom.xml中引入(最简单推荐,下文说的就是这种);
步骤:
- 本地使用springboot自建web应用,maven作为项目管理和构建工具;
- 把C:\Program Files\GDAL\下所有的.dll文件全部复制到jdk/bin/下;
-
把C:\Program Files\GDAL\java\下的gdal.jar和C:\Program Files\GDAL\下的gdalalljni.dll复制到src/main/resources/gdal/win32/下;
如图:
注意的就是linux的jar包和win32的jar包可能不一样,linux的要去下载gdal二进制资源然后编译等,而且第2第3部应用动态链接库也完全不一样,后期我再验证更新。我这里先说下win的开发环境配置。
- pom.xml中引入gdal.jar包:
...
src/main/resources/gdal/win32
org.gdal
gdal
3.5.0
system
${project.basedir}/${gdal.binddir}/gdal.jar
-
测试dxf文件转geojson。
说明:把.dxf转成.geojson,转换后的geojson可读性很强导致文件过大,所以我使用分页分文件夹去转换,详情见测试代码源码。
效果:
源码:
说明:源码中涉及到下面几点的可以先忽略注释或者删掉
- 转换可能导致中文乱码,我这里在源码注掉了本篇不涉及,可以参见我这篇文章: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。
- 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
- 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
- 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)