第04章 - 统一图层模型详解
4.1 模型设计理念
4.1.1 为什么需要统一模型
在GIS开发中,不同的库使用不同的数据模型:
| 库 | 要素类 | 要素 | 几何 |
|---|---|---|---|
| GeoTools | SimpleFeatureType | SimpleFeature | Geometry |
| GDAL/OGR | Layer | Feature | Geometry |
| ESRI | FeatureClass | Feature | Geometry |
这种差异带来的问题:
- 代码不可移植:为GeoTools写的代码不能用于GDAL
- 学习成本高:每个库都有自己的概念体系
- 维护困难:需要熟悉多套API
OGU4J的统一模型解决了这些问题,提供了与底层库无关的数据抽象。
4.1.2 模型设计原则
- 简洁性:只保留必要的属性和方法
- 通用性:适用于所有GIS数据格式
- 可序列化:支持JSON序列化/反序列化
- 可扩展性:支持元数据扩展
4.1.3 模型类图
┌─────────────────────────────────────────────────────────────────────┐
│ OguLayer │
│ (统一图层定义) │
├─────────────────────────────────────────────────────────────────────┤
│ - name: String 图层名称 │
│ - wkid: Integer 坐标系WKID │
│ - geometryType: GeometryType 几何类型 │
│ - fields: List<OguField> 字段列表 │
│ - features: List<OguFeature> 要素列表 │
│ - metadata: OguLayerMetadata 元数据 │
├─────────────────────────────────────────────────────────────────────┤
│ + validate() 验证图层完整性 │
│ + filter(OguFeatureFilter) 过滤要素 │
│ + getFeatureCount() 获取要素数量 │
│ + toJSON() / fromJSON() JSON序列化 │
└─────────────────────────────────────────────────────────────────────┘
│ │ │
│ 1:n │ 1:n │ 1:1
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ OguField │ │ OguFeature │ │ OguLayerMetadata │
│ (字段定义) │ │ (要素类) │ │ (图层元数据) │
├─────────────────┤ ├─────────────────────┤ ├─────────────────────┤
│ - name │ │ - id │ │ - dataSource │
│ - alias │ │ - geometry │ │ - coordinateSystem │
│ - dataType │ │ - attributes: List │ │ - zoneNumber │
│ - length │ │ <OguFieldValue> │ │ - projectionType │
│ - description │ │ - coordinates │ │ - measureUnit │
│ - nullable │ │ - rawValues │ │ - extendedInfos │
├─────────────────┤ ├─────────────────────┤ └─────────────────────┘
│ + 标准getter │ │ + getValue() │
│ + 标准setter │ │ + setValue() │
└─────────────────┘ │ + getAttribute() │
└─────────────────────┘
│
│ 1:n
▼
┌─────────────────────┐
│ OguFieldValue │
│ (字段值容器) │
├─────────────────────┤
│ - field: OguField │
│ - value: Object │
├─────────────────────┤
│ + getStringValue() │
│ + getIntValue() │
│ + getDoubleValue() │
│ + getFieldName() │
└─────────────────────┘
4.2 OguLayer - 图层类
4.2.1 类定义
/**
* 统一的GIS图层定义
* 代表一个完整的GIS图层,包含图层名称、坐标系、几何类型、字段定义和要素集合
*
* 设计理念:
* - 独立于任何GIS库的数据表示
* - 支持JSON序列化,便于传输和存储
* - 提供便捷的操作方法
*/
@Data
public class OguLayer {
/**
* 图层名称
*/
private String name;
/**
* 坐标系WKID(Well-Known ID)
* 例如:4490 = CGCS2000地理坐标系
* 4527 = CGCS2000 / 3-degree Gauss-Kruger zone 39
*/
private Integer wkid;
/**
* 几何类型
*/
private GeometryType geometryType;
/**
* 字段定义列表
*/
private List<OguField> fields;
/**
* 要素列表
*/
private List<OguFeature> features;
/**
* 图层元数据(可选)
*/
private OguLayerMetadata metadata;
// ... 方法定义
}
4.2.2 创建图层
方式1:代码构建
// 创建图层
OguLayer layer = new OguLayer();
layer.setName("行政区划");
layer.setWkid(4490);
layer.setGeometryType(GeometryType.POLYGON);
// 定义字段
List<OguField> fields = new ArrayList<>();
OguField codeField = new OguField();
codeField.setName("CODE");
codeField.setAlias("行政区划代码");
codeField.setDataType(FieldDataType.STRING);
codeField.setLength(12);
OguField nameField = new OguField();
nameField.setName("NAME");
nameField.setAlias("名称");
nameField.setDataType(FieldDataType.STRING);
nameField.setLength(100);
fields.add(codeField);
fields.add(nameField);
layer.setFields(fields);
// 添加要素
List<OguFeature> features = new ArrayList<>();
OguFeature feature = new OguFeature();
feature.setId("1");
feature.setGeometry("POLYGON((116 39, 116 40, 117 40, 117 39, 116 39))");
// 属性值以List<OguFieldValue>承载,setValue只会更新已存在的字段,不会新增
feature.setAttributes(new ArrayList<>(List.of(
new OguFieldValue(codeField, "110000"),
new OguFieldValue(nameField, "北京市"))));
features.add(feature);
layer.setFeatures(features);
方式2:从JSON创建
String json = "{" +
"\"name\":\"行政区划\"," +
"\"wkid\":4490," +
"\"geometryType\":\"POLYGON\"," +
"\"fields\":[...]," +
"\"features\":[...]" +
"}";
OguLayer layer = OguLayer.fromJSON(json);
方式3:从数据源读取
OguLayer layer = OguLayerUtil.readLayer(
DataFormatType.SHP,
"D:/data/districts.shp",
null, null, null,
GisEngineType.GEOTOOLS
);
4.2.3 图层验证
/**
* 验证图层数据完整性
* 检查项包括:
* - 图层名称不能为空
* - 几何类型必须设置
* - 字段列表不能为null
* - 要素的几何类型必须与图层一致
*/
layer.validate();
// 可能抛出的异常
try {
layer.validate();
} catch (LayerValidationException e) {
System.out.println("验证失败: " + e.getMessage());
}
4.2.4 要素过滤
// 使用Lambda表达式过滤要素
List<OguFeature> beijingFeatures = layer.filter(
feature -> "北京市".equals(feature.getValue("NAME"))
);
// 复杂过滤条件
List<OguFeature> largeAreas = layer.filter(feature -> {
Double area = feature.getAttribute("AREA").getDoubleValue();
return area != null && area > 10000;
});
// 组合过滤
List<OguFeature> result = layer.filter(feature -> {
String name = feature.getAttribute("NAME").getStringValue();
Integer level = feature.getAttribute("LEVEL").getIntValue();
return name != null && name.contains("市") && level != null && level == 1;
});
4.2.5 JSON序列化
// 转换为JSON
String json = layer.toJSON();
System.out.println(json);
// 从JSON恢复
OguLayer restored = OguLayer.fromJSON(json);
// 格式化输出(便于阅读)
String prettyJson = JSON.toJSONString(layer,
JSONWriter.Feature.PrettyFormat);
4.3 OguField - 字段类
4.3.1 类定义
/**
* 统一的字段定义类
* 描述图层中的一个属性字段
*/
@Data
public class OguField {
/**
* 字段名称(必填)
* 建议使用英文,符合数据库命名规范
*/
private String name;
/**
* 字段别名(可选)
* 用于显示的中文名称
*/
private String alias;
/**
* 数据类型(必填)
*/
private FieldDataType dataType;
/**
* 字段长度(字符串类型必填)
*/
private Integer length;
/**
* 字段描述(可选)
*/
private String description;
/**
* 是否允许空值
*/
private Boolean nullable;
/**
* 默认值(可选)
*/
private Object defaultValue;
}
4.3.2 字段数据类型
/**
* 字段数据类型枚举
* 每个枚举携带GDAL字段编码、对应Java类型等信息
*/
public enum FieldDataType {
INTEGER(new int[]{0}, Integer.class, 0); // 整数
DOUBLE(new int[]{2}, Double.class, 2); // 浮点数
STRING(new int[]{1, 3, 4, 6, 5, 7, 13}, String.class, 4); // 字符串
BINARY(new int[]{8}, byte[].class, 8); // 二进制
DATE(new int[]{9}, LocalDate.class, 9); // 日期
TIME(new int[]{10}, LocalTime.class, 10); // 时间
DATETIME(new int[]{11}, LocalDateTime.class, 11); // 日期时间
LONG(new int[]{12}, Long.class, 12); // 长整数
private final int[] gdalCodes;
private final Class<?> typeClass;
private final int defaultGdalCode;
// 类型转换方法
public static FieldDataType fieldDataTypeByGdalCode(int gdalCode) { ... }
public static FieldDataType fieldDataTypeByTypeClass(Class<?> typeClass) { ... }
}
4.3.3 创建字段
// 字符串字段
OguField nameField = new OguField();
nameField.setName("NAME");
nameField.setAlias("名称");
nameField.setDataType(FieldDataType.STRING);
nameField.setLength(100);
nameField.setNullable(false);
// 数值字段
OguField areaField = new OguField();
areaField.setName("AREA");
areaField.setAlias("面积");
areaField.setDataType(FieldDataType.DOUBLE);
areaField.setDefaultValue(0.0);
// 日期字段
OguField dateField = new OguField();
dateField.setName("CREATE_TIME");
dateField.setAlias("创建时间");
dateField.setDataType(FieldDataType.DATETIME);
// 布尔语义字段(FieldDataType无BOOLEAN,用INTEGER存0/1)
OguField activeField = new OguField();
activeField.setName("IS_ACTIVE");
activeField.setAlias("是否有效");
activeField.setDataType(FieldDataType.INTEGER);
activeField.setDefaultValue(1);
4.4 OguFeature - 要素类
4.4.1 类定义
/**
* 统一的要素类
* 代表图层中的一个地理要素,包含ID、几何信息和属性值
*/
@Data
public class OguFeature {
/**
* 要素ID
* 注意:各引擎对FID/OID的定义不同,此处如实返回,不做兼容,
* 不建议当作OBJECTID使用,推荐用属性过滤和查找
*/
private String id;
/**
* 要素几何(WKT格式)
* 例如:"POLYGON((116 39, 116 40, 117 40, 117 39, 116 39))"
*/
private String geometry;
/**
* 属性值集合
* 每个OguFieldValue包含字段定义(field)与具体值(value)
*/
private List<OguFieldValue> attributes;
/**
* 坐标列表(可选,用于国土TXT格式)
*/
private List<OguCoordinate> coordinates;
/**
* 原始属性值列表(可选,用于国土TXT格式)
*/
private List<String> rawValues;
// ... 方法定义
}
4.4.2 属性操作
获取属性值
OguFeature feature = layer.getFeatures().get(0);
// 方式1:直接获取原始值
Object value = feature.getValue("NAME");
String name = (String) value;
// 方式2:通过OguFieldValue获取(推荐)
OguFieldValue fieldValue = feature.getAttribute("NAME");
String name = fieldValue.getStringValue();
Integer code = feature.getAttribute("CODE").getIntValue();
Double area = feature.getAttribute("AREA").getDoubleValue();
// 安全获取(处理null值)
String safeName = Optional.ofNullable(feature.getAttribute("NAME"))
.map(OguFieldValue::getStringValue)
.orElse("未知");
设置属性值
// 设置单个属性
feature.setValue("NAME", "北京市");
feature.setValue("AREA", 16410.54);
feature.setValue("POPULATION", 21540000);
// 批量设置(setValue返回boolean,字段不存在时返回false,不会新增字段)
Map<String, Object> values = new HashMap<>();
values.put("NAME", "上海市");
values.put("AREA", 6340.5);
values.put("POPULATION", 24280000);
values.forEach(feature::setValue);
4.4.3 几何操作
// 获取WKT
String wkt = feature.getGeometry();
System.out.println(wkt);
// 输出: POLYGON((116 39, 116 40, 117 40, 117 39, 116 39))
// 设置WKT
feature.setGeometry("POLYGON((120 30, 120 31, 121 31, 121 30, 120 30))");
// 转换为JTS Geometry进行操作
Geometry geom = GeometryUtil.wkt2Geometry(feature.getGeometry());
double area = geom.getArea();
Geometry centroid = geom.getCentroid();
// 更新几何
Geometry buffered = geom.buffer(0.01);
feature.setGeometry(GeometryUtil.geometry2Wkt(buffered));
4.5 OguFieldValue - 字段值容器
4.5.1 类定义
/**
* 字段值容器
* 封装字段定义与原始值,提供类型安全的值获取方法
*
* 设计理念:
* - 统一处理null值
* - 提供类型转换
* - 避免ClassCastException
*/
@Data
@AllArgsConstructor
public class OguFieldValue implements Serializable {
/**
* 字段定义
*/
private OguField field;
/**
* 字段值
*/
private Object value;
public OguFieldValue() {}
/**
* 获取字段名称(field为null时返回null)
*/
public String getFieldName() {
return field == null ? null : field.getName();
}
/**
* 获取字符串值
*/
public String getStringValue() {
return value == null ? null : String.valueOf(value);
}
/**
* 获取整数值(解析失败返回null)
*/
public Integer getIntValue() {
if (value == null) return null;
if (value instanceof Integer) return (Integer) value;
try {
return Integer.parseInt(String.valueOf(value));
} catch (NumberFormatException e) {
return null;
}
}
/**
* 获取双精度浮点值(解析失败返回null)
*/
public Double getDoubleValue() {
if (value == null) return null;
if (value instanceof Double) return (Double) value;
try {
return Double.parseDouble(String.valueOf(value));
} catch (NumberFormatException e) {
return null;
}
}
}
注意:
OguFieldValue仅提供getStringValue/getIntValue/getDoubleValue三个类型转换方法,没有getLongValue/getBooleanValue/getDateValue;需要Long/Boolean/日期时请直接使用getValue()自行转换(值类型由对应FieldDataType决定,日期为LocalDate/LocalTime/LocalDateTime)。
4.5.2 使用示例
OguFeature feature = layer.getFeatures().get(0);
// 安全地获取各类型值
String name = feature.getAttribute("NAME").getStringValue();
Integer level = feature.getAttribute("LEVEL").getIntValue();
Double area = feature.getAttribute("AREA").getDoubleValue();
// 长整数/布尔/日期等类型需自行从getValue()转换
Long population = ((Number) feature.getAttribute("POPULATION").getValue()).longValue();
// 处理可能为null的情况
OguFieldValue areaValue = feature.getAttribute("AREA");
if (areaValue != null && areaValue.getDoubleValue() != null) {
double area = areaValue.getDoubleValue();
System.out.println("面积: " + area);
}
// 类型转换
OguFieldValue codeValue = feature.getAttribute("CODE");
// 原始数据可能是整数或字符串
String codeStr = codeValue.getStringValue(); // 转为字符串
Integer codeInt = codeValue.getIntValue(); // 转为整数
4.6 OguCoordinate - 坐标类
4.6.1 类定义
/**
* 坐标类
* 支持二维/三维坐标,以及国土TXT格式的点号和圈号
*/
@Data
public class OguCoordinate {
/**
* X坐标(经度或东坐标)
*/
private Double x;
/**
* Y坐标(纬度或北坐标)
*/
private Double y;
/**
* Z坐标(高程,可选)
*/
private Double z;
/**
* 点号(用于国土TXT格式)
*/
private String pointNumber;
/**
* 圈号(用于国土TXT格式,区分内外环)
*/
private Integer ringNumber;
// 构造函数
public OguCoordinate() {}
public OguCoordinate(Double x, Double y) {
this.x = x;
this.y = y;
}
public OguCoordinate(Double x, Double y, Double z) {
this.x = x;
this.y = y;
this.z = z;
}
}
4.6.2 使用场景
主要用于国土TXT格式的界址点管理:
// 创建带点号的坐标
OguCoordinate coord = new OguCoordinate();
coord.setX(500000.123);
coord.setY(4000000.456);
coord.setPointNumber("J1");
coord.setRingNumber(1); // 外环
// 批量创建坐标点
List<OguCoordinate> coords = new ArrayList<>();
coords.add(new OguCoordinate(500000.0, 4000000.0) {{ setPointNumber("J1"); setRingNumber(1); }});
coords.add(new OguCoordinate(500100.0, 4000000.0) {{ setPointNumber("J2"); setRingNumber(1); }});
coords.add(new OguCoordinate(500100.0, 4000100.0) {{ setPointNumber("J3"); setRingNumber(1); }});
coords.add(new OguCoordinate(500000.0, 4000100.0) {{ setPointNumber("J4"); setRingNumber(1); }});
coords.add(new OguCoordinate(500000.0, 4000000.0) {{ setPointNumber("J1"); setRingNumber(1); }});
feature.setCoordinates(coords);
4.7 OguLayerMetadata - 图层元数据
4.7.1 类定义
/**
* 图层元数据
* 存储图层的扩展信息,主要用于国土TXT格式
*/
@Data
public class OguLayerMetadata {
/**
* 数据来源
* 例如:"自然资源部"
*/
private String dataSource;
/**
* 坐标系名称
* 例如:"2000国家大地坐标系"
*/
private String coordinateSystemName;
/**
* 分带方式
* 例如:"3" 表示3度分带
*/
private String zoneDivision;
/**
* 投影类型
* 例如:"高斯克吕格"
*/
private String projectionType;
/**
* 计量单位
* 例如:"米"
*/
private String measureUnit;
/**
* 格式版本 / 数据日期 / 带号 / 精度 / 转换参数(可选)
*/
private String formatVersion;
private String dataDate;
private String zoneNumber;
private String precision;
private String transformParams;
/**
* 扩展信息列表(每项包含名称与properties)
*/
private List<ExtendedInfo> extendedInfos;
@Data
public static class ExtendedInfo implements Serializable {
private String name;
private LinkedHashMap<String, String> properties;
}
}
4.7.2 使用示例
// 创建元数据
OguLayerMetadata metadata = new OguLayerMetadata();
metadata.setDataSource("自然资源部");
metadata.setCoordinateSystemName("2000国家大地坐标系");
metadata.setZoneDivision("3");
metadata.setProjectionType("高斯克吕格");
metadata.setMeasureUnit("米");
// 添加扩展信息(List<ExtendedInfo>,每项含名称与properties)
OguLayerMetadata.ExtendedInfo extended = new OguLayerMetadata.ExtendedInfo();
extended.setName("说明");
LinkedHashMap<String, String> extInfo = new LinkedHashMap<>();
extInfo.put("version", "1.0");
extInfo.put("createDate", "2024-01-01");
extInfo.put("author", "张三");
extended.setProperties(extInfo);
metadata.setExtendedInfos(List.of(extended));
// 设置到图层
layer.setMetadata(metadata);
// 保存为国土TXT格式时使用
GtTxtUtil.saveTxt(layer, txtPath, metadata, null, 39);
4.8 OguFeatureFilter - 要素过滤器
4.8.1 接口定义
/**
* 要素过滤器(函数式接口)
* 用于过滤图层中的要素
*/
@FunctionalInterface
public interface OguFeatureFilter {
/**
* 测试要素是否满足条件
* @param feature 待测试的要素
* @return true表示保留,false表示过滤掉
*/
boolean apply(OguFeature feature);
}
4.8.2 使用示例
// 使用Lambda表达式
List<OguFeature> result = layer.filter(f ->
f.getValue("NAME").toString().contains("市"));
// 使用方法引用
List<OguFeature> result = layer.filter(this::isValid);
private boolean isValid(OguFeature feature) {
String wkt = feature.getGeometry();
if (wkt == null) return false;
TopologyValidationResult validResult =
GeometryUtil.isValid(GeometryUtil.wkt2Geometry(wkt));
return validResult.isValid();
}
// 组合过滤器
OguFeatureFilter filter1 = f -> f.getAttribute("LEVEL").getIntValue() == 1;
OguFeatureFilter filter2 = f -> f.getAttribute("AREA").getDoubleValue() > 1000;
List<OguFeature> result = layer.filter(f -> filter1.apply(f) && filter2.apply(f));
4.9 GeometryType - 几何类型枚举
4.9.1 枚举定义
/**
* 几何类型枚举
* 定义所有支持的几何类型(typeCode为库内编码,wkbGeometryType为WKB标准编码)
*/
public enum GeometryType {
POINT(0, "Point", Point.class, 1),
MULTIPOINT(1, "MultiPoint", MultiPoint.class, 4),
LINESTRING(2, "LineString", LineString.class, 2),
LINEARRING(3, "LinearRing", LinearRing.class, 101),
MULTILINESTRING(4, "MultiLineString", MultiLineString.class, 5),
POLYGON(5, "Polygon", Polygon.class, 3),
MULTIPOLYGON(6, "MultiPolygon", MultiPolygon.class, 6),
GEOMETRYCOLLECTION(7, "GeometryCollection", GeometryCollection.class, 7);
private final int typeCode;
private final String typeName;
private final Class<?> typeClass;
private final int wkbGeometryType;
/**
* 按类型名称获取枚举(忽略大小写)
*/
public static GeometryType valueOfByTypeName(String typeName);
/**
* 按库内编码获取枚举
*/
public static GeometryType valueOfByTypeCode(int typeCode);
/**
* 按JTS Geometry绑定类获取枚举
*/
public static GeometryType valueOfByTypeClass(Class<?> typeClass);
/**
* 按WKB几何类型编码获取枚举
*/
public static GeometryType valueOfByWkbGeometryType(int wkbGeometryType);
}
4.9.2 使用示例
// 设置图层几何类型
layer.setGeometryType(GeometryType.POLYGON);
// 从类型名称判断
GeometryType type = GeometryType.valueOfByTypeName("Polygon");
// 从Geometry的绑定类判断
Geometry geom = GeometryUtil.wkt2Geometry(wkt);
GeometryType type = GeometryType.valueOfByTypeClass(geom.getClass());
// 类型判断
if (layer.getGeometryType() == GeometryType.POLYGON ||
layer.getGeometryType() == GeometryType.MULTIPOLYGON) {
// 处理面状数据
processPolygonLayer(layer);
}
4.10 实战案例
4.10.1 创建完整的图层
public class LayerExample {
public static OguLayer createCityLayer() {
// 1. 创建图层
OguLayer layer = new OguLayer();
layer.setName("中国城市");
layer.setWkid(4490);
layer.setGeometryType(GeometryType.POINT);
// 2. 定义字段
List<OguField> fields = new ArrayList<>();
fields.add(createField("CODE", "城市代码", FieldDataType.STRING, 6));
fields.add(createField("NAME", "城市名称", FieldDataType.STRING, 50));
fields.add(createField("PROVINCE", "所属省份", FieldDataType.STRING, 50));
fields.add(createField("POPULATION", "人口(万)", FieldDataType.INTEGER, null));
fields.add(createField("GDP", "GDP(亿元)", FieldDataType.DOUBLE, null));
fields.add(createField("LEVEL", "城市等级", FieldDataType.INTEGER, null));
layer.setFields(fields);
// 3. 添加要素
List<OguFeature> features = new ArrayList<>();
features.add(createCity(layer, "1", "110000", "北京", "北京", 2154, 40269.6, 1,
"POINT(116.407 39.904)"));
features.add(createCity(layer, "2", "310000", "上海", "上海", 2428, 43214.9, 1,
"POINT(121.473 31.230)"));
features.add(createCity(layer, "3", "440100", "广州", "广东", 1881, 28839.0, 2,
"POINT(113.264 23.129)"));
features.add(createCity(layer, "4", "440300", "深圳", "广东", 1756, 32387.7, 2,
"POINT(114.057 22.543)"));
layer.setFeatures(features);
// 4. 验证
layer.validate();
return layer;
}
private static OguField createField(String name, String alias,
FieldDataType dataType, Integer length) {
OguField field = new OguField();
field.setName(name);
field.setAlias(alias);
field.setDataType(dataType);
field.setLength(length);
return field;
}
private static OguFeature createCity(OguLayer layer, String id, String code, String name,
String province, int population, double gdp, int level, String wkt) {
Map<String, Object> values = new LinkedHashMap<>();
values.put("CODE", code);
values.put("NAME", name);
values.put("PROVINCE", province);
values.put("POPULATION", population);
values.put("GDP", gdp);
values.put("LEVEL", level);
OguFeature feature = new OguFeature();
feature.setId(id);
feature.setGeometry(wkt);
// setValue不会新增属性,构造要素时需按fields装配OguFieldValue
List<OguFieldValue> attributes = new ArrayList<>();
for (OguField field : layer.getFields()) {
attributes.add(new OguFieldValue(field, values.get(field.getName())));
}
feature.setAttributes(attributes);
return feature;
}
}
4.10.2 图层数据分析
public class LayerAnalysis {
public static void analyze(OguLayer layer) {
System.out.println("=== 图层分析报告 ===");
System.out.println("图层名称: " + layer.getName());
System.out.println("坐标系: WKID " + layer.getWkid());
System.out.println("几何类型: " + layer.getGeometryType());
System.out.println("要素数量: " + layer.getFeatureCount());
// 字段统计
System.out.println("\n=== 字段信息 ===");
for (OguField field : layer.getFields()) {
System.out.printf("%s (%s) - %s%n",
field.getName(),
field.getDataType(),
field.getAlias());
}
// 数值字段统计
System.out.println("\n=== 数值统计 ===");
for (OguField field : layer.getFields()) {
if (field.getDataType() == FieldDataType.DOUBLE ||
field.getDataType() == FieldDataType.INTEGER) {
DoubleSummaryStatistics stats = layer.getFeatures().stream()
.map(f -> f.getAttribute(field.getName()))
.filter(v -> v != null && v.getDoubleValue() != null)
.mapToDouble(OguFieldValue::getDoubleValue)
.summaryStatistics();
System.out.printf("%s: 最小=%.2f, 最大=%.2f, 平均=%.2f, 总和=%.2f%n",
field.getName(),
stats.getMin(),
stats.getMax(),
stats.getAverage(),
stats.getSum());
}
}
// 几何范围
System.out.println("\n=== 空间范围 ===");
double minX = Double.MAX_VALUE, minY = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE, maxY = Double.MIN_VALUE;
for (OguFeature feature : layer.getFeatures()) {
if (feature.getGeometry() != null) {
Geometry geom = GeometryUtil.wkt2Geometry(feature.getGeometry());
Envelope env = geom.getEnvelopeInternal();
minX = Math.min(minX, env.getMinX());
minY = Math.min(minY, env.getMinY());
maxX = Math.max(maxX, env.getMaxX());
maxY = Math.max(maxY, env.getMaxY());
}
}
System.out.printf("范围: (%.4f, %.4f) - (%.4f, %.4f)%n",
minX, minY, maxX, maxY);
}
}