当前位置: 首页 > news >正文

行政区划 ZIP 导入(importZip)

一、Maven 依赖

<!-- GDAL Java 绑定(解析 ESRI File Geodatabase) --><dependency><groupId>org.gdal</groupId><artifactId>gdal</artifactId><version>${gdal.version}</version></dependency><!-- GeoTools(解析 Shapefile / GeoJSON / GPKG) --><dependency><groupId>org.geotools</groupId><artifactId>gt-shapefile</artifactId><version>${geotools.version}</version></dependency>

底层通过GDAL + GeoTools双引擎解析地理空间数据格式(.gdb、.shp、.geojson、.gpkg 等)。


二、接口定义

文件路径:/.../xzq/controller/JcXzqController.java

@ApiOperation("导入ZIP文件(包含地理空间数据GBD格式文件)")@CommonLog("导入行政区ZIP文件")@PostMapping("/base/xzq/importZip")publicCommonResult<Map<String,Object>>importZip(@RequestParam("file")MultipartFilefile,@RequestParam(value="overwriteMode",defaultValue="false")BooleanoverwriteMode){JcXzqZipImportParamparam=newJcXzqZipImportParam();param.setFile(file);param.setOverwriteMode(overwriteMode);returnCommonResult.data(jcXzqService.importZipFile(param));}
  • 请求方式:POST
  • 参数:file(MultipartFile) +overwriteMode(boolean, 默认 false)
  • 返回:{totalCount, successCount, errorCount, layerCounts, errorDetail}

三、服务入口方法

@Transactional(rollbackFor=Exception.class)@OverridepublicMap<String,Object>importZipFile(JcXzqZipImportParamjcXzqZipImportParam){MultipartFilefile=jcXzqZipImportParam.getFile();booleanoverwriteMode=Boolean.TRUE.equals(jcXzqZipImportParam.getOverwriteMode());Map<String,Object>result=newHashMap<>();List<String>errorList=newArrayList<>();Map<String,Integer>layerCounts=newHashMap<>();inttotalCount=0,successCount=0,errorCount=0;// 创建临时目录StringtempDir=System.getProperty("java.io.tmpdir")+File.separator+"xzq_import_"+IdUtil.simpleUUID();PathtempPath=Paths.get(tempDir);try{Files.createDirectories(tempPath);// 1. 校验文件格式if(!file.getOriginalFilename().toLowerCase().endsWith(".zip")){thrownewCommonException("文件格式错误,仅支持ZIP格式文件");}// 2. 解压ZIPunzipFile(file,tempPath);// 3. 查找GDB文件List<File>gdbFiles=findGdbFiles(tempPath.toFile());if(gdbFiles.isEmpty()){thrownewCommonException("ZIP文件中未找到GDB格式文件");}// 4. 解析每个GDB文件for(FilegdbFile:gdbFiles){GbdFileParser.MultiLayerGdbResultgdbResult=GbdFileParser.parseMultiLayerGdbWithGdal(gdbFile);for(StringlayerName:gdbResult.getLayerNames()){List<?>layerData=gdbResult.getLayerDataMap().get(layerName);if(layerData==null||layerData.isEmpty())continue;intlayerSuccess=processLayerData(layerName,layerData,overwriteMode,errorList);layerCounts.put(layerName,layerSuccess);totalCount+=layerData.size();successCount+=layerSuccess;errorCount+=(layerData.size()-layerSuccess);}}result.put("totalCount",totalCount);result.put("successCount",successCount);result.put("errorCount",errorCount);result.put("layerCounts",layerCounts);result.put("errorDetail",errorList);}catch(Exceptione){thrownewCommonException("GDB文件导入失败:{}",e.getMessage());}finally{FileUtil.del(tempDir);}returnresult;}

四、核心方法详解

4.1 ZIP 解压

privatevoidunzipFile(MultipartFilefile,PathdestPath)throwsIOException{try(ZipInputStreamzipInputStream=newZipInputStream(file.getInputStream(),StandardCharsets.UTF_8)){ZipEntryentry;while((entry=zipInputStream.getNextEntry())!=null){if(!entry.isDirectory()){PathfilePath=destPath.resolve(entry.getName());Files.createDirectories(filePath.getParent());try(FileOutputStreamfos=newFileOutputStream(filePath.toFile())){IoUtil.copy(zipInputStream,fos);}}zipInputStream.closeEntry();}}}

逻辑:使用ZipInputStream+ UTF-8 逐条解压 ZIP 中的文件/目录到临时目录,保留原始目录结构(.gdb 内部包含多个系统文件,必须完整保留)。

4.2 GDB 文件递归查找

privateList<File>findGdbFiles(Filedirectory){List<File>gdbFiles=newArrayList<>();File[]files=directory.listFiles();if(files!=null){for(Filefile:files){if(file.isDirectory()){if(file.getName().toLowerCase().endsWith(".gdb")){gdbFiles.add(file);// .gdb 在文件系统中是一个目录}else{gdbFiles.addAll(findGdbFiles(file));// 递归子目录}}elseif(isGdbFile(file.getName())){gdbFiles.add(file);}}}returngdbFiles;}

逻辑:ESRI File Geodatabase 在磁盘上表现为一个目录(xxx.gdb/),内部包含.gdbtable.freelist.gdbindexes等文件。递归遍历解压目录,找到所有以.gdb结尾的目录/文件。

4.3 GDB 多图层解析(GDAL 方式)

核心类:GbdFileParser.java

路径:/.../xzq/util/GbdFileParser.java

/** * 使用GDAL解析ESRI File Geodatabase (.gdb)文件 * * 注意:此方法需要额外的依赖和配置: * 1. 添加GDAL Java绑定依赖 * 2. 安装GDAL本地库 * 3. 配置GDAL环境变量 * * Maven依赖示例: * <dependency> * <groupId>org.gdal</groupId> * <artifactId>gdal</artifactId> * <version>3.6.0</version> * </dependency> * * @param gdbFile GDB文件(实际上是文件夹) * @return 解析后的行政区数据列表 * @throws IOException 解析异常 *//** * 使用GDAL解析多图层GDB文件 * * @param gdbFile GDB文件路径 * @return 多图层解析结果 * @throws IOException 解析异常 */publicstaticMultiLayerGdbResultparseMultiLayerGdbWithGdal(FilegdbFile)throwsIOException{checkGdalEnvironment();MultiLayerGdbResultresult=newMultiLayerGdbResult();// 初始化GDAL/OGRogr.RegisterAll();gdal.AllRegister();// 打开GDB数据源(只读模式)DataSourcedataSource=ogr.Open(gdbFile.getAbsolutePath(),0);// 遍历所有图层intlayerCount=dataSource.GetLayerCount();for(inti=0;i<layerCount;i++){Layerlayer=dataSource.GetLayer(i);StringlayerName=layer.GetName();List<?>layerData=createLayerDataList(layerName);// 遍历图层中的要素layer.ResetReading();org.gdal.ogr.Featurefeature;while((feature=layer.GetNextFeature())!=null){Objectentity=convertGdalFeatureToEntity(feature,layerName);if(entity!=null)addEntityToList(layerData,entity);feature.delete();// 必须手动释放}result.addLayerData(layerName,layerData);}dataSource.delete();returnresult;}

逻辑:

  1. 检测 GDAL 环境变量(GDAL_DATAPROJ_LIBjava.library.path
  2. ogr.RegisterAll()+gdal.AllRegister()注册所有驱动
  3. ogr.Open()打开 GDB 数据源(只读模式 0)
  4. dataSource.GetLayerCount()+GetLayer(i)遍历所有数据图层
  5. 每图层中GetNextFeature()遍历要素,转为业务实体
  6. feature.delete()dataSource.delete()必须显式调用释放 GDAL 内存

4.4 GDAL Feature → 业务实体转换

privatestaticObjectconvertGdalFeatureToEntity(org.gdal.ogr.FeaturegdalFeature,StringlayerName){FeatureDefnfeatureDefn=gdalFeature.GetDefnRef();intfieldCount=featureDefn.GetFieldCount();// 获取几何数据(WKT格式)StringgeomWkt=null;org.gdal.ogr.Geometrygeometry=gdalFeature.GetGeometryRef();if(geometry!=null){geomWkt=geometry.ExportToWkt();}// 按图层名称分流转换switch(layerName.toLowerCase()){case"xzq":returnconvertGdalFeatureToXzq(gdalFeature,featureDefn,fieldCount,geomWkt);case"dxssyjjbxx":returnconvertGdalFeatureToDxssyjjbxx(gdalFeature,featureDefn,fieldCount,geomWkt);case"dxszy_1_5jfq":returnconvertGdalFeatureToDxszy15jfq(gdalFeature,featureDefn,fieldCount,geomWkt);case"gczjd":returnconvertGdalFeatureToGczjd(gdalFeature,featureDefn,fieldCount,geomWkt);case"pdt":returnconvertGdalFeatureToPdt(gdalFeature,featureDefn,fieldCount,geomWkt);case"szy_1_3jfq":returnconvertGdalFeatureToSzy13jfq(gdalFeature,featureDefn,fieldCount,geomWkt);default:returnconvertGdalFeatureToXzq(gdalFeature,featureDefn,fieldCount,geomWkt);}}

字段映射示例(以 Xzq 为例):

privatestaticJcXzqconvertGdalFeatureToXzq(org.gdal.ogr.FeaturegdalFeature,FeatureDefnfeatureDefn,intfieldCount,StringgeomWkt){JcXzqxzq=newJcXzq();for(inti=0;i<fieldCount;i++){FieldDefnfieldDefn=featureDefn.GetFieldDefn(i);StringfieldName=fieldDefn.GetName().toLowerCase();if(gdalFeature.IsFieldSet(i)){Stringvalue=gdalFeature.GetFieldAsString(i);if(value!=null&&!value.trim().isEmpty()){mapAttributeToXzq(xzq,fieldName,value);}}}xzq.setGeom(geomWkt);returnisValidXzq(xzq)?xzq:null;}

逻辑:遍历 Feature 的字段定义,根据字段名(支持中文/英文模糊匹配如xzqdm/行政区代码/adcode)映射到实体属性;几何数据通过ExportToWkt()转为 WKT 字符串存入geom字段。

4.5 GeoTools 方式解析(非 GDB 格式)

对于 Shapefile、GeoPackage、GeoJSON 等格式,使用 GeoTools 库解析(无需 GDAL):

// Shapefile 解析Map<String,Object>map=newHashMap<>();map.put("url",shpFile.toURI().toURL());map.put("charset","UTF-8");// 失败时自动降级 GBKDataStoredataStore=DataStoreFinder.getDataStore(map);SimpleFeatureSourcefeatureSource=dataStore.getFeatureSource(typeName);SimpleFeatureCollectionfeatures=featureSource.getFeatures();try(SimpleFeatureIteratoriterator=features.features()){while(iterator.hasNext()){SimpleFeaturefeature=iterator.next();JcXzqxzq=convertFeatureToXzq(feature);// 坐标系自动转换到 WGS84CoordinateReferenceSystemsourceCRS=feature.getFeatureType().getCoordinateReferenceSystem();CoordinateReferenceSystemtargetCRS=CRS.decode("EPSG:4326",true);MathTransformtransform=CRS.findMathTransform(sourceCRS,targetCRS,true);Geometrygeometry=JTS.transform((Geometry)feature.getDefaultGeometry(),transform);xzq.setGeom(newWKTWriter().write(geometry));}}

逻辑:GeoTools 通过DataStoreFinder自动识别格式,读取 Feature 后完成字段映射 + 坐标系转换(自动转为 WGS84)。


五、注意事项

  1. GDAL 环境— 解析 .gdb 格式必须安装 GDAL 本地库并配置PATHGDAL_DATAPROJ_LIB环境变量及java.library.pathJVM 参数。如环境配置困难可使用ogr2ogr命令行将 GDB 转 Shapefile 后通过 GeoTools 解析。
  2. GDB 目录结构— ESRI File Geodatabase 在文件系统上是一个目录,解压时必须保留完整目录结构和所有内部文件,否则 GDAL 无法打开。
  3. 编码问题— 中国 Spatial Data 常用 GBK 编码,Shapefile 的.dbf属性文件可能是 GBK。GeoTools 自动降级:UTF-8 失败后自动切 GBK。
  4. 坐标转换— 中国常用坐标系(Xian80 / Beijing54 / CGCS2000)需配置 EPSG 参数才能正确转为 WGS84。CRS.AxisOrder.NORTH_EAST(如 EPSG:4326)需设置true参数启用强制 XY 顺序。
  5. GDAL 内存释放— GDAL Java 绑定的 Feature 和 DataSource 对象必须显式调用delete()释放,否则会导致内存泄漏。
  6. 大文件事务@Transactional标注在整个方法上,GDB 数据量大时可能导致事务超时,建议在processLayerData内部分段批量提交。
http://www.jsqmd.com/news/965016/

相关文章:

  • `:如何被提取并用于浏览器标签页、历史记录? - `<meta charset=“...“>`:字符集的早期(或重新)解析 - `<meta name=“viewport“>`:视口设置与布局的关联
  • NS25CL直线导轨技术规格与应用指南
  • 如何用FModel轻松提取游戏资源:3个步骤开启MOD创作之旅
  • CSDN博主必看:如何优雅地在Markdown和评论区插入最新emoji表情(附懒人包)
  • 到底为什么PHP要有匿名函数?
  • 去头屑洗发水哪个效果好?2026年测评去屑洗发水排行榜TOP1 - 新闻快传
  • 2026年小包团价格,甘肃嘉恒国旅费用透明 - myqiye
  • CSDN推广链接批量修改全链路解析,从Token鉴权失败到URL Schema自动校验的7层防御机制
  • 2026年无人机海关编码查询平台排行:新能源汽车海关编码/旧机电海关编码/玩具海关编码/生鲜食品海关编码/美国加征关税/选择指南 - 优质品牌商家
  • 大模型 API 成本优化:从月账单十万到三万的架构演进
  • 低资源语言语义关系构建:土耳其语语料库混合方法
  • MySQL 执行引擎深度解密:基于 AST 解析器定制与 Optimizer 执行计划干预的 SQL 性能调优实战
  • MySQL知识点综合详解_01
  • Docker、firewalld和iptables的“三角关系”捋不清?一张图看懂流量到底怎么走的
  • GPU显存稳定性终极检测:用memtest_vulkan快速诊断显卡故障的完整指南
  • GPT-4V核心架构
  • 解锁大屏视界:手机视频投屏全攻略
  • 素颜霜哪款好用?2026全肤质素颜霜实测:清透自然打造原生肌 - 新闻快传
  • 苏州塑胶模具定制厂选购有哪些要点 - myqiye
  • 从传播入口看《你笑的时候》:一个歌名如何留住听众
  • 过来人血泪经验|2026年6月上海嘉定区值得信赖的老银元回收+老银锭回收门店 - 沪上贵金属口碑推荐官
  • **L_mask**(掩码损失)是什么
  • 3步上手Windows自动化神器:Pulover‘s Macro Creator新手完全指南
  • G-Helper终极指南:如何让华硕笔记本性能翻倍的轻量级控制工具
  • CSE-CIC-IDS2018数据集深度解析:除了下载,你更应该知道的文件结构与实战用途
  • Moneta外汇体验细节路径流畅吗?
  • Git小白避坑实录:手把手教你解决‘ahead by N commits’并理解origin/master到底是个啥
  • 上海海臻味供应链有限公司知识图谱 - 新闻快传
  • 2026年当下万寿宫酒店哪家好?这份价值与体验并重的选型指南请查收 - 2026年企业资讯
  • 服务器迁移后,NetBackup 8.1.2客户端报socket(25)错误?手把手教你排查1556端口监听问题