当前位置: 首页 > 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.cnnetsun.cn/news/2799248.html

相关文章:

  • BilibiliDown终极指南:三分钟掌握B站视频下载神器
  • 8类果树病害检测数据集(炭疽病/白粉病/根腐病等)| 6000张YOLO智慧农业病虫害监测数据集 适用于果园智能监测、病害识别与目标检测研究
  • 怎么监控对标账号更新,5款作者监控工具横评实测
  • G-Helper终极指南:如何让华硕笔记本性能翻倍的轻量级控制工具
  • K210人脸识别门禁实战:如何用MaixPy实现口罩检测与特征学习
  • 从dBi到隔离度:一文读懂天线数据手册里的那些‘黑话’,让你的产品射频性能不再玄学
  • 用Python和PuLP搞定选址问题:从消防站到外卖配送点的实战建模指南
  • MATLAB旁瓣分析工具集:一键计算雷达波形PSLR与ISLR
  • 终极指南:如何用Warcraft Helper彻底修复魔兽争霸3在Win10/Win11的兼容性问题
  • 基于STM32的智能抽水装置设计:从传感器融合到电机驱动的完整实现
  • 北京出租车GPS轨迹分析包:2014年单日数据+上下车热点自动识别+交互地图一键生成
  • 大模型与深度学习确定性控制:基于 PyTorch 的随机种子(Seed)全局锚定与 CUDA 算子确定性配置规避精度抖动实战
  • ABot-Claw——改进OpenClaw以驱动双足机器人自主干活的三个关键点:统一具身接口、视觉多模态记忆、基于奖励模型的执行反馈模块
  • Forza Mods AIO终极指南:3分钟掌握免费开源游戏修改工具
  • 151.高通深度救砖脚本|9008 EDL模式Sahara协议传输,黑砖设备强制恢复
  • 编程教育的新篇章:AI工具如何改变教学方式
  • 基于IEEE14节点的电力系统碳流追踪MATLAB仿真包(含潮流计算与碳责任分配核心函数)
  • 实战指南:基于YOLOv8与快马平台构建工地安全智能监控系统
  • ARGIS制图效果展示
  • 从源码层看CSDN AI渲染引擎:为什么你的<code>标签被自动过滤?(2024 Q2平台内核逆向笔记)
  • CSDN AI标题优化算法深度拆解(BERT+用户意图权重+搜索热力图融合模型曝光)
  • YOLO11轻量化魔改 | 替换Backbone为ShuffleNetV2+SE模块,极致通道打乱,低算力平台首选
  • AI 记忆助手设计手记:帮老人整理一生故事的温暖工程
  • 提升开发效率:用快马平台自动生成散热器软件的通用数据采集与报警模块
  • 利用快马平台快速构建货物皮重(tare)计算管理原型
  • 遗传算法工程实战:选择算子、交叉变异与早熟诊断
  • 从零到日增237精准粉丝,我靠CSDN这张AI卡片爆了!手把手复刻全流程,含配置避坑清单
  • 第 2 关:为什么软件工程需要 AI,从个人效率到团队战斗力
  • 引言与动力学回顾
  • x.com 提示:启用 JavaScript 或切换浏览器,禁用隐私扩展程序再试!