代码提交
This commit is contained in:
@ -34,7 +34,7 @@ public class CodeUtil {
|
||||
}
|
||||
|
||||
// 传入需要生成代码的表名
|
||||
Generation("web_source");
|
||||
Generation("pbf_info");
|
||||
}
|
||||
|
||||
public static void Generation(String... tableName) {
|
||||
|
||||
470
src/main/java/com/yj/earth/common/util/GdalJsonConverter.java
Normal file
470
src/main/java/com/yj/earth/common/util/GdalJsonConverter.java
Normal file
@ -0,0 +1,470 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import org.gdal.gdal.gdal;
|
||||
import org.gdal.ogr.*;
|
||||
import org.gdal.osr.CoordinateTransformation;
|
||||
import org.gdal.osr.SpatialReference;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.gdal.ogr.ogrConstants.*;
|
||||
|
||||
/**
|
||||
* GDAL地理数据转JSON工具类
|
||||
*/
|
||||
import org.gdal.gdal.gdal;
|
||||
import org.gdal.ogr.*;
|
||||
import org.gdal.osr.SpatialReference;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* GDAL地理数据转JSON工具类
|
||||
*/
|
||||
public class GdalJsonConverter {
|
||||
|
||||
static {
|
||||
// 初始化GDAL
|
||||
gdal.AllRegister();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将地理数据文件转换为指定结构的JSON
|
||||
* @param filePath 文件绝对路径
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String convertToJson(String filePath) {
|
||||
return convertToJson(filePath, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将地理数据文件转换为指定结构的JSON
|
||||
* @param filePath 文件绝对路径
|
||||
* @param targetCrs 目标坐标系(如:"EPSG:4326"),为空则不转换
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public static String convertToJson(String filePath, String targetCrs) {
|
||||
try {
|
||||
// 打开数据源
|
||||
DataSource dataSource = ogr.Open(filePath, 0);
|
||||
if (dataSource == null) {
|
||||
throw new RuntimeException("无法打开文件: " + filePath);
|
||||
}
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
JSONArray listArray = new JSONArray();
|
||||
|
||||
// 获取所有图层
|
||||
int layerCount = dataSource.GetLayerCount();
|
||||
for (int i = 0; i < layerCount; i++) {
|
||||
Layer layer = dataSource.GetLayer(i);
|
||||
String layerName = layer.GetName();
|
||||
|
||||
JSONObject featureCollection = createFeatureCollection(layer, layerName, targetCrs);
|
||||
if (featureCollection != null) {
|
||||
listArray.put(featureCollection);
|
||||
}
|
||||
}
|
||||
|
||||
result.put("list", listArray);
|
||||
dataSource.delete();
|
||||
|
||||
return result.toString(2); // 缩进2个空格,美化输出
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("转换失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建FeatureCollection对象
|
||||
*/
|
||||
private static JSONObject createFeatureCollection(Layer layer, String layerName, String targetCrs) throws JSONException {
|
||||
JSONObject featureCollection = new JSONObject();
|
||||
featureCollection.put("type", "FeatureCollection");
|
||||
featureCollection.put("name", layerName);
|
||||
|
||||
// 获取源坐标系
|
||||
String crsSrc = getLayerCrs(layer);
|
||||
featureCollection.put("crs_src", crsSrc != null ? crsSrc : "");
|
||||
|
||||
// 设置目标坐标系
|
||||
featureCollection.put("crs_dst", targetCrs != null ? targetCrs : "");
|
||||
|
||||
JSONArray featuresArray = new JSONArray();
|
||||
Feature feature;
|
||||
|
||||
// 重置图层读取位置
|
||||
layer.ResetReading();
|
||||
|
||||
// 获取图层定义,用于字段信息
|
||||
FeatureDefn layerDefn = layer.GetLayerDefn();
|
||||
|
||||
// 如果需要坐标转换,创建坐标转换对象
|
||||
SpatialReference sourceSrs = layer.GetSpatialRef();
|
||||
SpatialReference targetSrs = null;
|
||||
CoordinateTransformation coordTrans = null;
|
||||
|
||||
if (targetCrs != null && !targetCrs.isEmpty() && sourceSrs != null) {
|
||||
try {
|
||||
targetSrs = new SpatialReference();
|
||||
if (targetCrs.startsWith("EPSG:")) {
|
||||
int epsgCode = Integer.parseInt(targetCrs.substring(5));
|
||||
targetSrs.ImportFromEPSG(epsgCode);
|
||||
} else {
|
||||
targetSrs.ImportFromProj4(targetCrs);
|
||||
}
|
||||
coordTrans = new CoordinateTransformation(sourceSrs, targetSrs);
|
||||
} catch (Exception e) {
|
||||
System.err.println("坐标转换初始化失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
while ((feature = layer.GetNextFeature()) != null) {
|
||||
JSONObject featureObj = createFeature(feature, layerDefn, coordTrans, targetSrs != null);
|
||||
if (featureObj != null) {
|
||||
featuresArray.put(featureObj);
|
||||
}
|
||||
feature.delete();
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
if (coordTrans != null) {
|
||||
coordTrans.delete();
|
||||
}
|
||||
if (targetSrs != null) {
|
||||
targetSrs.delete();
|
||||
}
|
||||
|
||||
featureCollection.put("features", featuresArray);
|
||||
return featureCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图层的坐标系信息
|
||||
*/
|
||||
private static String getLayerCrs(Layer layer) {
|
||||
try {
|
||||
SpatialReference srs = layer.GetSpatialRef();
|
||||
if (srs != null) {
|
||||
// 优先尝试获取PROJ4字符串
|
||||
String proj4 = srs.ExportToProj4();
|
||||
if (proj4 != null && !proj4.trim().isEmpty()) {
|
||||
return proj4.trim();
|
||||
}
|
||||
|
||||
// 如果PROJ4为空,尝试获取WKT
|
||||
String wkt = srs.ExportToWkt();
|
||||
if (wkt != null && !wkt.trim().isEmpty()) {
|
||||
return wkt.trim();
|
||||
}
|
||||
|
||||
// 最后尝试获取EPSG代码
|
||||
String authorityCode = srs.GetAuthorityCode(null);
|
||||
String authorityName = srs.GetAuthorityName(null);
|
||||
if (authorityName != null && authorityCode != null) {
|
||||
return authorityName + ":" + authorityCode;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取坐标系信息失败: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单个Feature对象
|
||||
*/
|
||||
private static JSONObject createFeature(Feature feature, FeatureDefn layerDefn,
|
||||
CoordinateTransformation coordTrans, boolean transformCoords) throws JSONException {
|
||||
JSONObject featureObj = new JSONObject();
|
||||
featureObj.put("type", "Feature");
|
||||
|
||||
// 处理属性字段
|
||||
JSONObject properties = new JSONObject();
|
||||
int fieldCount = layerDefn.GetFieldCount();
|
||||
|
||||
for (int i = 0; i < fieldCount; i++) {
|
||||
FieldDefn fieldDefn = layerDefn.GetFieldDefn(i);
|
||||
String fieldName = fieldDefn.GetName();
|
||||
Object fieldValue = getFieldValue(feature, fieldDefn, i);
|
||||
|
||||
if (fieldValue != null) {
|
||||
properties.put(fieldName, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
featureObj.put("properties", properties);
|
||||
|
||||
// 处理几何图形
|
||||
Geometry geometry = feature.GetGeometryRef();
|
||||
if (geometry != null && !geometry.IsEmpty()) {
|
||||
Geometry geomToUse = geometry;
|
||||
|
||||
// 如果需要坐标转换
|
||||
if (transformCoords && coordTrans != null) {
|
||||
try {
|
||||
Geometry transformedGeom = geometry.Clone();
|
||||
transformedGeom.Transform(coordTrans);
|
||||
geomToUse = transformedGeom;
|
||||
} catch (Exception e) {
|
||||
System.err.println("坐标转换失败: " + e.getMessage());
|
||||
// 如果转换失败,使用原始几何
|
||||
geomToUse = geometry;
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject geometryObj = convertGeometryToJson(geomToUse);
|
||||
featureObj.put("geometry", geometryObj);
|
||||
|
||||
// 清理临时几何对象
|
||||
if (geomToUse != geometry) {
|
||||
geomToUse.delete();
|
||||
}
|
||||
} else {
|
||||
// 如果没有几何信息,创建空的MultiPolygon
|
||||
featureObj.put("geometry", createEmptyMultiPolygon());
|
||||
}
|
||||
|
||||
return featureObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段值
|
||||
*/
|
||||
private static Object getFieldValue(Feature feature, FieldDefn fieldDefn, int fieldIndex) {
|
||||
int fieldType = fieldDefn.GetFieldType();
|
||||
|
||||
if (feature.IsFieldSet(fieldIndex)) {
|
||||
switch (fieldType) {
|
||||
case ogr.OFTInteger:
|
||||
return feature.GetFieldAsInteger(fieldIndex);
|
||||
case ogr.OFTInteger64:
|
||||
return feature.GetFieldAsInteger64(fieldIndex);
|
||||
case ogr.OFTReal:
|
||||
return feature.GetFieldAsDouble(fieldIndex);
|
||||
case ogr.OFTString:
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
case ogr.OFTDate:
|
||||
case ogr.OFTTime:
|
||||
case ogr.OFTDateTime:
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
default:
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将几何图形转换为JSON
|
||||
*/
|
||||
private static JSONObject convertGeometryToJson(Geometry geometry) throws JSONException {
|
||||
JSONObject geometryObj = new JSONObject();
|
||||
String geometryType = geometry.GetGeometryName();
|
||||
|
||||
// 根据几何类型处理
|
||||
switch (geometry.GetGeometryType()) {
|
||||
case wkbPoint:
|
||||
case wkbPoint25D:
|
||||
geometryObj.put("type", "Point");
|
||||
geometryObj.put("coordinates", getPointCoordinates(geometry));
|
||||
break;
|
||||
case wkbLineString:
|
||||
case wkbLineString25D:
|
||||
geometryObj.put("type", "LineString");
|
||||
geometryObj.put("coordinates", getLineStringCoordinates(geometry));
|
||||
break;
|
||||
case wkbPolygon:
|
||||
case wkbPolygon25D:
|
||||
geometryObj.put("type", "Polygon");
|
||||
geometryObj.put("coordinates", getPolygonCoordinates(geometry));
|
||||
break;
|
||||
case wkbMultiPoint:
|
||||
case wkbMultiPoint25D:
|
||||
geometryObj.put("type", "MultiPoint");
|
||||
geometryObj.put("coordinates", getMultiPointCoordinates(geometry));
|
||||
break;
|
||||
case wkbMultiLineString:
|
||||
case wkbMultiLineString25D:
|
||||
geometryObj.put("type", "MultiLineString");
|
||||
geometryObj.put("coordinates", getMultiLineStringCoordinates(geometry));
|
||||
break;
|
||||
case wkbMultiPolygon:
|
||||
case wkbMultiPolygon25D:
|
||||
geometryObj.put("type", "MultiPolygon");
|
||||
geometryObj.put("coordinates", getMultiPolygonCoordinates(geometry));
|
||||
break;
|
||||
case wkbGeometryCollection:
|
||||
case wkbGeometryCollection25D:
|
||||
geometryObj.put("type", "GeometryCollection");
|
||||
geometryObj.put("geometries", getGeometryCollection(geometry));
|
||||
break;
|
||||
default:
|
||||
// 默认使用WKT格式
|
||||
geometryObj.put("type", geometryType);
|
||||
geometryObj.put("wkt", geometry.ExportToWkt());
|
||||
break;
|
||||
}
|
||||
|
||||
return geometryObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取点坐标
|
||||
*/
|
||||
private static JSONArray getPointCoordinates(Geometry geometry) throws JSONException {
|
||||
JSONArray coords = new JSONArray();
|
||||
coords.put(geometry.GetX());
|
||||
coords.put(geometry.GetY());
|
||||
if (geometry.GetGeometryType() == wkbPoint25D) {
|
||||
coords.put(geometry.GetZ());
|
||||
}
|
||||
return coords;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取线坐标
|
||||
*/
|
||||
private static JSONArray getLineStringCoordinates(Geometry geometry) throws JSONException {
|
||||
JSONArray coords = new JSONArray();
|
||||
int pointCount = geometry.GetPointCount();
|
||||
for (int i = 0; i < pointCount; i++) {
|
||||
JSONArray point = new JSONArray();
|
||||
point.put(geometry.GetX(i));
|
||||
point.put(geometry.GetY(i));
|
||||
if (geometry.GetGeometryType() == wkbLineString25D) {
|
||||
point.put(geometry.GetZ(i));
|
||||
}
|
||||
coords.put(point);
|
||||
}
|
||||
return coords;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多边形坐标
|
||||
*/
|
||||
private static JSONArray getPolygonCoordinates(Geometry geometry) throws JSONException {
|
||||
JSONArray rings = new JSONArray();
|
||||
|
||||
// 外环
|
||||
Geometry exteriorRing = geometry.GetGeometryRef(0);
|
||||
rings.put(getLineStringCoordinates(exteriorRing));
|
||||
|
||||
// 内环(如果有)
|
||||
int ringCount = geometry.GetGeometryCount();
|
||||
for (int i = 1; i < ringCount; i++) {
|
||||
Geometry interiorRing = geometry.GetGeometryRef(i);
|
||||
rings.put(getLineStringCoordinates(interiorRing));
|
||||
}
|
||||
|
||||
return rings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多点坐标
|
||||
*/
|
||||
private static JSONArray getMultiPointCoordinates(Geometry geometry) throws JSONException {
|
||||
JSONArray points = new JSONArray();
|
||||
int geometryCount = geometry.GetGeometryCount();
|
||||
for (int i = 0; i < geometryCount; i++) {
|
||||
Geometry point = geometry.GetGeometryRef(i);
|
||||
points.put(getPointCoordinates(point));
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多线坐标
|
||||
*/
|
||||
private static JSONArray getMultiLineStringCoordinates(Geometry geometry) throws JSONException {
|
||||
JSONArray lines = new JSONArray();
|
||||
int geometryCount = geometry.GetGeometryCount();
|
||||
for (int i = 0; i < geometryCount; i++) {
|
||||
Geometry line = geometry.GetGeometryRef(i);
|
||||
lines.put(getLineStringCoordinates(line));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多多边形坐标
|
||||
*/
|
||||
private static JSONArray getMultiPolygonCoordinates(Geometry geometry) throws JSONException {
|
||||
JSONArray polygons = new JSONArray();
|
||||
int geometryCount = geometry.GetGeometryCount();
|
||||
for (int i = 0; i < geometryCount; i++) {
|
||||
Geometry polygon = geometry.GetGeometryRef(i);
|
||||
polygons.put(getPolygonCoordinates(polygon));
|
||||
}
|
||||
return polygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取几何集合
|
||||
*/
|
||||
private static JSONArray getGeometryCollection(Geometry geometry) throws JSONException {
|
||||
JSONArray geometries = new JSONArray();
|
||||
int geometryCount = geometry.GetGeometryCount();
|
||||
for (int i = 0; i < geometryCount; i++) {
|
||||
Geometry geom = geometry.GetGeometryRef(i);
|
||||
geometries.put(convertGeometryToJson(geom));
|
||||
}
|
||||
return geometries;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空的MultiPolygon几何
|
||||
*/
|
||||
private static JSONObject createEmptyMultiPolygon() throws JSONException {
|
||||
JSONObject geometry = new JSONObject();
|
||||
geometry.put("type", "MultiPolygon");
|
||||
geometry.put("coordinates", new JSONArray());
|
||||
return geometry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息(辅助方法)
|
||||
*/
|
||||
public static Map<String, Object> getFileInfo(String filePath) {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
try {
|
||||
DataSource dataSource = ogr.Open(filePath, 0);
|
||||
if (dataSource != null) {
|
||||
info.put("driver", dataSource.GetDriver().GetName());
|
||||
info.put("layerCount", dataSource.GetLayerCount());
|
||||
|
||||
List<Map<String, Object>> layers = new ArrayList<>();
|
||||
for (int i = 0; i < dataSource.GetLayerCount(); i++) {
|
||||
Layer layer = dataSource.GetLayer(i);
|
||||
Map<String, Object> layerInfo = new HashMap<>();
|
||||
layerInfo.put("name", layer.GetName());
|
||||
layerInfo.put("featureCount", layer.GetFeatureCount());
|
||||
layerInfo.put("geometryType", ogr.GeometryTypeToName(layer.GetGeomType()));
|
||||
|
||||
// 添加坐标系信息
|
||||
String crs = getLayerCrs(layer);
|
||||
layerInfo.put("crs", crs != null ? crs : "未知");
|
||||
|
||||
layers.add(layerInfo);
|
||||
}
|
||||
info.put("layers", layers);
|
||||
dataSource.delete();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
info.put("error", e.getMessage());
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@ -1,247 +0,0 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import org.gdal.gdal.gdal;
|
||||
import org.gdal.ogr.*;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GdalUtil {
|
||||
|
||||
// 静态初始化ObjectMapper,避免重复创建
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
// 静态初始化GDAL
|
||||
static {
|
||||
// 可以考虑设置缓存目录来提升性能
|
||||
gdal.SetConfigOption("GDAL_CACHEMAX", "1024"); // 设置1GB缓存
|
||||
gdal.SetConfigOption("PROJ_LIB", "./lib");
|
||||
gdal.AllRegister();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取矢量文件、将每个图层转换为符合指定格式的 GeoJSON
|
||||
*/
|
||||
public static String readVectorToSpecifiedGeoJson(String filePath) {
|
||||
DataSource dataSource = null;
|
||||
try {
|
||||
// 打开矢量文件,使用只读模式
|
||||
dataSource = ogr.Open(filePath, 0);
|
||||
if (dataSource == null) {
|
||||
throw new RuntimeException("无法打开矢量文件: " + filePath + ", 错误信息: " + gdal.GetLastErrorMsg());
|
||||
}
|
||||
|
||||
// 创建结果结构,预先估计容量
|
||||
Map<String, Object> result = new HashMap<>(2);
|
||||
int layerCount = dataSource.GetLayerCount();
|
||||
List<Map<String, Object>> layerList = new ArrayList<>(layerCount);
|
||||
|
||||
// 处理每个图层
|
||||
for (int i = 0; i < layerCount; i++) {
|
||||
Layer layer = dataSource.GetLayer(i);
|
||||
if (layer == null) continue;
|
||||
|
||||
// 为当前图层创建符合格式的 GeoJSON
|
||||
Map<String, Object> layerGeoJson = convertLayerToSpecifiedFormat(layer);
|
||||
if (layerGeoJson != null) {
|
||||
layerList.add(layerGeoJson);
|
||||
}
|
||||
// 提前释放图层资源
|
||||
layer.delete();
|
||||
}
|
||||
|
||||
result.put("list", layerList);
|
||||
|
||||
// 使用单例ObjectMapper转换为JSON字符串
|
||||
return OBJECT_MAPPER.writeValueAsString(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("处理矢量文件时发生错误: " + e.getMessage() + ", 详细错误: " + gdal.GetLastErrorMsg(), e);
|
||||
} finally {
|
||||
// 确保数据源被正确释放
|
||||
if (dataSource != null) {
|
||||
dataSource.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将单个图层转换为符合指定格式的 GeoJSON
|
||||
*/
|
||||
private static Map<String, Object> convertLayerToSpecifiedFormat(Layer layer) {
|
||||
try {
|
||||
// 获取要素数量,预分配集合大小
|
||||
long featureCount = layer.GetFeatureCount();
|
||||
if (featureCount <= 0) {
|
||||
return null; // 跳过空图层
|
||||
}
|
||||
|
||||
// 创建符合格式的 GeoJSON FeatureCollection
|
||||
Map<String, Object> geoJson = new HashMap<>(3);
|
||||
geoJson.put("type", "FeatureCollection");
|
||||
geoJson.put("name", layer.GetName());
|
||||
|
||||
// 预分配要素列表容量
|
||||
List<Map<String, Object>> features = new ArrayList<>((int) Math.min(featureCount, Integer.MAX_VALUE));
|
||||
|
||||
// 获取字段定义
|
||||
FeatureDefn featureDefn = layer.GetLayerDefn();
|
||||
int fieldCount = featureDefn.GetFieldCount();
|
||||
|
||||
// 缓存字段信息,避免重复获取
|
||||
String[] fieldNames = new String[fieldCount];
|
||||
int[] fieldTypes = new int[fieldCount];
|
||||
for (int j = 0; j < fieldCount; j++) {
|
||||
FieldDefn fieldDef = featureDefn.GetFieldDefn(j);
|
||||
fieldNames[j] = fieldDef.GetName();
|
||||
fieldTypes[j] = fieldDef.GetFieldType();
|
||||
}
|
||||
|
||||
// 读取图层中的所有要素
|
||||
layer.ResetReading();
|
||||
Feature feature;
|
||||
|
||||
while ((feature = layer.GetNextFeature()) != null) {
|
||||
Map<String, Object> featureMap = new HashMap<>(3);
|
||||
featureMap.put("type", "Feature");
|
||||
|
||||
// 处理属性
|
||||
Map<String, Object> properties = new HashMap<>(fieldCount);
|
||||
for (int j = 0; j < fieldCount; j++) {
|
||||
properties.put(fieldNames[j], getFieldValue(feature, j, fieldTypes[j]));
|
||||
}
|
||||
featureMap.put("properties", properties);
|
||||
|
||||
// 处理几何信息 - 直接构建Map而不是先转JSON再解析
|
||||
Geometry geometry = feature.GetGeometryRef();
|
||||
featureMap.put("geometry", convertGeometryToMap(geometry));
|
||||
|
||||
features.add(featureMap);
|
||||
feature.delete(); // 及时释放要素资源
|
||||
}
|
||||
|
||||
geoJson.put("features", features);
|
||||
return geoJson;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("转换图层 " + layer.GetName() + " 时发生错误: " + e.getMessage() + ", 详细错误: " + gdal.GetLastErrorMsg());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Geometry直接转换为Map,避免JSON序列化/反序列化开销
|
||||
*/
|
||||
private static Map<String, Object> convertGeometryToMap(Geometry geometry) {
|
||||
if (geometry == null) {
|
||||
return createEmptyMultiPolygon();
|
||||
}
|
||||
|
||||
try {
|
||||
String geometryType = geometry.GetGeometryName();
|
||||
Map<String, Object> geometryMap = new HashMap<>(2);
|
||||
|
||||
// 如果不是MultiPolygon,尝试转换
|
||||
if (!"MULTIPOLYGON".equals(geometryType)) {
|
||||
// 转换为MultiPolygon
|
||||
Geometry multiPolygon = geometry.MakeValid();
|
||||
if (!"MULTIPOLYGON".equals(multiPolygon.GetGeometryName())) {
|
||||
multiPolygon = geometry.Buffer(0); // 修复几何问题
|
||||
}
|
||||
if (!"MULTIPOLYGON".equals(multiPolygon.GetGeometryName())) {
|
||||
// 如果仍然不是MultiPolygon,创建空的
|
||||
return createEmptyMultiPolygon();
|
||||
}
|
||||
geometry = multiPolygon;
|
||||
}
|
||||
|
||||
geometryMap.put("type", "MultiPolygon");
|
||||
geometryMap.put("coordinates", getCoordinates(geometry));
|
||||
|
||||
return geometryMap;
|
||||
} catch (Exception e) {
|
||||
return createEmptyMultiPolygon();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接获取几何坐标,避免JSON转换
|
||||
*/
|
||||
private static List<?> getCoordinates(Geometry geometry) {
|
||||
List<Object> coordinates = new ArrayList<>();
|
||||
|
||||
if (geometry == null) {
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
int geometryCount = geometry.GetGeometryCount();
|
||||
for (int i = 0; i < geometryCount; i++) {
|
||||
Geometry polygon = geometry.GetGeometryRef(i);
|
||||
if (polygon == null) continue;
|
||||
|
||||
List<Object> polygonCoords = new ArrayList<>();
|
||||
int ringCount = polygon.GetGeometryCount();
|
||||
for (int j = 0; j < ringCount; j++) {
|
||||
Geometry ring = polygon.GetGeometryRef(j);
|
||||
if (ring == null) continue;
|
||||
|
||||
List<List<Double>> ringCoords = new ArrayList<>();
|
||||
int pointCount = ring.GetPointCount();
|
||||
for (int k = 0; k < pointCount; k++) {
|
||||
double[] point = ring.GetPoint(k);
|
||||
List<Double> pointCoords = new ArrayList<>(2);
|
||||
pointCoords.add(point[0]);
|
||||
pointCoords.add(point[1]);
|
||||
ringCoords.add(pointCoords);
|
||||
}
|
||||
polygonCoords.add(ringCoords);
|
||||
}
|
||||
coordinates.add(polygonCoords);
|
||||
}
|
||||
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段值、根据字段类型处理
|
||||
*/
|
||||
private static Object getFieldValue(Feature feature, int fieldIndex, int fieldType) {
|
||||
try {
|
||||
switch (fieldType) {
|
||||
case ogr.OFTInteger:
|
||||
return feature.GetFieldAsInteger(fieldIndex);
|
||||
case ogr.OFTInteger64:
|
||||
return feature.GetFieldAsInteger64(fieldIndex);
|
||||
case ogr.OFTReal:
|
||||
return feature.GetFieldAsDouble(fieldIndex);
|
||||
case ogr.OFTString:
|
||||
case ogr.OFTStringList:
|
||||
case ogr.OFTWideString:
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
case ogr.OFTDate:
|
||||
case ogr.OFTTime:
|
||||
case ogr.OFTDateTime:
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
default:
|
||||
// 对于其他类型,统一返回字符串表示
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return feature.GetFieldAsString(fieldIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空的 MultiPolygon 几何对象
|
||||
*/
|
||||
private static Map<String, Object> createEmptyMultiPolygon() {
|
||||
Map<String, Object> geometry = new HashMap<>(2);
|
||||
geometry.put("type", "MultiPolygon");
|
||||
geometry.put("coordinates", new ArrayList<>());
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
@ -1,245 +0,0 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
|
||||
import java.sql.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SQLiteConverter {
|
||||
private static final String JDBC_DRIVER = "org.sqlite.JDBC";
|
||||
// 源数据库和目标数据库路径
|
||||
private String sourceDbPath;
|
||||
private String targetDbPath;
|
||||
// 批处理大小、可根据内存情况调整
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
public SQLiteConverter(String sourceDbPath, String targetDbPath) {
|
||||
this.sourceDbPath = sourceDbPath;
|
||||
this.targetDbPath = targetDbPath;
|
||||
}
|
||||
|
||||
public void convert() {
|
||||
Connection sourceConn = null;
|
||||
Connection targetConn = null;
|
||||
try {
|
||||
// 加载驱动
|
||||
Class.forName(JDBC_DRIVER);
|
||||
// 连接源数据库和目标数据库
|
||||
sourceConn = DriverManager.getConnection("jdbc:sqlite:" + sourceDbPath);
|
||||
targetConn = DriverManager.getConnection("jdbc:sqlite:" + targetDbPath);
|
||||
// 禁用自动提交、以便在出现错误时可以回滚
|
||||
targetConn.setAutoCommit(false);
|
||||
// 创建目标表结构
|
||||
createTargetTables(targetConn);
|
||||
// 复制并转换数据
|
||||
copyModeTypesData(sourceConn, targetConn);
|
||||
copyModelsData(sourceConn, targetConn);
|
||||
// 为model表添加索引
|
||||
createModelTableIndexes(targetConn);
|
||||
// 提交事务
|
||||
targetConn.commit();
|
||||
System.out.println("数据库转换成功!");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
if (targetConn != null) {
|
||||
targetConn.rollback();
|
||||
System.out.println("转换失败、已回滚操作!");
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
// 关闭连接
|
||||
try {
|
||||
if (sourceConn != null) sourceConn.close();
|
||||
if (targetConn != null) targetConn.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createTargetTables(Connection conn) throws SQLException {
|
||||
System.out.println("开始创建目标表结构...");
|
||||
Statement stmt = conn.createStatement();
|
||||
String sql = """
|
||||
CREATE TABLE "model_type" (
|
||||
"id" TEXT,
|
||||
"name" TEXT,
|
||||
"parent_id" TEXT,
|
||||
"tree_index" INTEGER,
|
||||
"created_at" TEXT,
|
||||
"updated_at" TEXT,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
""";
|
||||
stmt.execute(sql);
|
||||
sql = """
|
||||
CREATE TABLE "model" (
|
||||
"id" TEXT,
|
||||
"model_type_id" TEXT,
|
||||
"model_name" TEXT,
|
||||
"model_type" TEXT,
|
||||
"model_data" BLOB,
|
||||
"poster_type" TEXT,
|
||||
"poster_data" BLOB,
|
||||
"created_at" TEXT,
|
||||
"updated_at" TEXT,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
""";
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
System.out.println("目标表结构创建完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 model 表的每个字段创建索引
|
||||
*/
|
||||
private void createModelTableIndexes(Connection conn) throws SQLException {
|
||||
System.out.println("开始为创建索引...");
|
||||
Statement stmt = conn.createStatement();
|
||||
|
||||
String sql = """
|
||||
CREATE INDEX idx_model_covering ON model(
|
||||
model_type_id,
|
||||
id,
|
||||
model_name,
|
||||
model_type,
|
||||
poster_type,
|
||||
created_at,
|
||||
updated_at
|
||||
);
|
||||
""";
|
||||
stmt.execute(sql);
|
||||
|
||||
stmt.close();
|
||||
System.out.println("model表索引创建完成");
|
||||
}
|
||||
|
||||
private int getTotalRecords(Connection conn, String tableName) throws SQLException {
|
||||
PreparedStatement stmt = conn.prepareStatement("SELECT COUNT(*) AS total FROM " + tableName);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
int total = rs.next() ? rs.getInt("total") : 0;
|
||||
rs.close();
|
||||
stmt.close();
|
||||
return total;
|
||||
}
|
||||
|
||||
private void copyModeTypesData(Connection sourceConn, Connection targetConn) throws SQLException {
|
||||
int totalRecords = getTotalRecords(sourceConn, "mode_types");
|
||||
System.out.println("开始转换 mode_types 表数据、共" + totalRecords + "条记录");
|
||||
PreparedStatement sourceStmt = sourceConn.prepareStatement("SELECT * FROM mode_types");
|
||||
ResultSet rs = sourceStmt.executeQuery();
|
||||
PreparedStatement targetStmt = targetConn.prepareStatement(
|
||||
"INSERT INTO model_type (id, name, parent_id, tree_index, created_at, updated_at) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
int count = 0;
|
||||
|
||||
while (rs.next()) {
|
||||
targetStmt.setString(1, rs.getString("type_id"));
|
||||
targetStmt.setString(2, rs.getString("type_name"));
|
||||
targetStmt.setString(3, rs.getString("p_id"));
|
||||
targetStmt.setInt(4, 0);
|
||||
targetStmt.setObject(5, LocalDateTime.now());
|
||||
targetStmt.setObject(6, LocalDateTime.now());
|
||||
|
||||
// 添加到批处理
|
||||
targetStmt.addBatch();
|
||||
count++;
|
||||
|
||||
// 每达到批处理大小或最后一条记录时执行批处理
|
||||
if (count % BATCH_SIZE == 0 || count == totalRecords) {
|
||||
targetStmt.executeBatch();
|
||||
displayProgress(count, totalRecords, "mode_types 表");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("\n成功转换 mode_types 表数据:" + count + "条记录");
|
||||
rs.close();
|
||||
sourceStmt.close();
|
||||
targetStmt.close();
|
||||
}
|
||||
|
||||
private void copyModelsData(Connection sourceConn, Connection targetConn) throws SQLException {
|
||||
int totalRecords = getTotalRecords(sourceConn, "models");
|
||||
System.out.println("开始转换 models 表数据、共" + totalRecords + "条记录");
|
||||
|
||||
// 对于大字段、使用向前滚动的结果集、避免缓存所有数据
|
||||
PreparedStatement sourceStmt = sourceConn.prepareStatement(
|
||||
"SELECT * FROM models",
|
||||
ResultSet.TYPE_FORWARD_ONLY,
|
||||
ResultSet.CONCUR_READ_ONLY
|
||||
);
|
||||
sourceStmt.setFetchSize(100);
|
||||
ResultSet rs = sourceStmt.executeQuery();
|
||||
PreparedStatement targetStmt = targetConn.prepareStatement(
|
||||
"INSERT INTO model (id, model_type_id, model_name, model_type, model_data, " +
|
||||
"poster_type, poster_data, created_at, updated_at) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
|
||||
int count = 0;
|
||||
while (rs.next()) {
|
||||
targetStmt.setString(1, rs.getString("id"));
|
||||
targetStmt.setString(2, rs.getString("p_id"));
|
||||
targetStmt.setString(3, rs.getString("model_name"));
|
||||
targetStmt.setString(4, rs.getString("model_type"));
|
||||
byte[] dataBytes = rs.getBytes("data");
|
||||
if (dataBytes != null) {
|
||||
targetStmt.setBytes(5, dataBytes);
|
||||
} else {
|
||||
targetStmt.setNull(5, Types.BLOB);
|
||||
}
|
||||
targetStmt.setString(6, rs.getString("poster_type"));
|
||||
byte[] posterBytes = rs.getBytes("poster");
|
||||
if (posterBytes != null) {
|
||||
targetStmt.setBytes(7, posterBytes);
|
||||
}
|
||||
targetStmt.setObject(8, LocalDateTime.now());
|
||||
targetStmt.setObject(9, LocalDateTime.now());
|
||||
|
||||
// 添加到批处理
|
||||
targetStmt.addBatch();
|
||||
count++;
|
||||
// 执行批处理
|
||||
if (count % BATCH_SIZE == 0 || count == totalRecords) {
|
||||
targetStmt.executeBatch();
|
||||
displayProgress(count, totalRecords, "models 表");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("\n成功转换 models 表数据:" + count + "条记录");
|
||||
rs.close();
|
||||
sourceStmt.close();
|
||||
targetStmt.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示进度信息
|
||||
*/
|
||||
private void displayProgress(int current, int total, String tableName) {
|
||||
double percentage = (double) current / total * 100;
|
||||
// 清除当前行并显示进度
|
||||
System.out.printf("\r%s: 已完成 %.1f%% (%d/%d条)", tableName, percentage, current, total);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 源数据库路径
|
||||
String sourcePath = "F:\\公司通用模型库.model";
|
||||
// 目标数据库路径
|
||||
String targetPath = "F:\\通用模型库.model";
|
||||
System.out.println("开始数据库转换...");
|
||||
System.out.println("源数据库: " + sourcePath);
|
||||
System.out.println("目标数据库: " + targetPath);
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 创建转换器并执行转换
|
||||
SQLiteConverter converter = new SQLiteConverter(sourcePath, targetPath);
|
||||
converter.convert();
|
||||
long endTime = System.currentTimeMillis();
|
||||
double elapsedTime = (endTime - startTime) / 1000.0;
|
||||
System.out.printf("转换完成、耗时: %.2f秒%n", elapsedTime);
|
||||
}
|
||||
}
|
||||
@ -89,7 +89,8 @@ public class SQLiteUtil {
|
||||
stmt.execute("PRAGMA temp_store=MEMORY;"); // 临时表/索引存内存(减少磁盘IO)
|
||||
stmt.execute("PRAGMA busy_timeout=2000;"); // 忙等待超时:2秒(避免瞬时并发锁等待)
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("初始化SQLite数据源失败(路径:" + dbFilePath + ")", e);
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("初始化SQLite数据源失败(路径:" + dbFilePath + ")+ 原因是:" + e.getMessage());
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
|
||||
Reference in New Issue
Block a user