第07章 - 几何处理与空间分析

第07章 - 几何处理与空间分析

7.1 GeometryUtil概述

7.1.1 设计理念

GeometryUtil是OGU4J的几何处理核心类,基于以下设计理念:

  1. 双重API:同时提供基于JTS Geometry对象和基于WKT字符串的方法
  2. 功能全面:覆盖格式转换、空间关系、空间分析、几何属性等
  3. 易于使用:静态方法,无需实例化
  4. 线程安全:所有方法都是无状态的

7.1.2 API分类

GeometryUtil
├── 格式转换
│   ├── WKT ↔ Geometry
│   ├── GeoJSON ↔ Geometry
│   ├── ESRI JSON ↔ Geometry
│   └── WKT ↔ GeoJSON ↔ ESRI JSON
├── 空间关系(基于JTS)
│   ├── intersects, contains, within
│   ├── touches, crosses, overlaps
│   ├── disjoint, equals
│   └── relate, relatePattern
├── 空间关系(基于WKT/ESRI)
│   └── xxxWkt 方法
├── 空间分析
│   ├── buffer, intersection, union
│   ├── difference, symDifference
│   ├── convexHull, concaveHull
│   └── splitPolygon, polygonize
├── 几何属性
│   ├── area, length
│   ├── centroid, interiorPoint
│   ├── dimension, numPoints
│   └── boundary, envelope
└── 几何验证
    ├── isValid, isSimple
    ├── validate, simplify
    └── densify

7.2 格式转换

7.2.1 WKT与Geometry互转

import com.znlgis.ogu4j.geometry.GeometryUtil;
import org.locationtech.jts.geom.Geometry;

// WKT转Geometry
String wkt = "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))";
Geometry geom = GeometryUtil.wkt2Geometry(wkt);

// Geometry转WKT
String wktOutput = GeometryUtil.geometry2Wkt(geom);

// 各种几何类型的WKT示例
String pointWkt = "POINT(116.4 39.9)";
String lineWkt = "LINESTRING(0 0, 10 10, 20 0)";
String polygonWkt = "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))";
String multiPointWkt = "MULTIPOINT((0 0), (10 10), (20 20))";
String multiLineWkt = "MULTILINESTRING((0 0, 10 10), (20 20, 30 30))";
String multiPolygonWkt = "MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), " +
                         "((20 20, 20 30, 30 30, 30 20, 20 20)))";

// 带洞多边形
String polygonWithHoleWkt = "POLYGON((0 0, 0 100, 100 100, 100 0, 0 0), " +
                            "(20 20, 20 80, 80 80, 80 20, 20 20))";

7.2.2 GeoJSON与Geometry互转

// GeoJSON转Geometry
String geojson = "{\"type\":\"Point\",\"coordinates\":[116.4,39.9]}";
Geometry geom = GeometryUtil.geojson2Geometry(geojson);

// Geometry转GeoJSON
String output = GeometryUtil.geometry2Geojson(geom);

// 复杂几何的GeoJSON
String polygonGeojson = "{" +
    "\"type\":\"Polygon\"," +
    "\"coordinates\":[[[0,0],[0,10],[10,10],[10,0],[0,0]]]" +
    "}";
Geometry polygon = GeometryUtil.geojson2Geometry(polygonGeojson);

7.2.3 ESRI JSON与其他格式互转

// WKT转ESRI JSON
int wkid = 4490;  // CGCS2000
String wkt = "POLYGON((116 39, 116 40, 117 40, 117 39, 116 39))";
String esriJson = GeometryUtil.wkt2EsriJson(wkt, wkid);

// ESRI JSON转WKT
String wktBack = GeometryUtil.esriJson2Wkt(esriJson);

// GeoJSON与ESRI JSON互转
String geojson = "{\"type\":\"Point\",\"coordinates\":[116.4,39.9]}";
String esriJsonFromGeo = GeometryUtil.geoJson2EsriJson(wkid, geojson);
String geojsonBack = GeometryUtil.esriJson2GeoJson(esriJsonFromGeo);

// ESRI JSON与Geometry互转
Geometry geom = GeometryUtil.esriJson2Geometry(esriJson);
String esriJsonFromGeom = GeometryUtil.geometry2EsriJson(geom, wkid);

7.2.4 WKT与GeoJSON直接转换

// WKT转GeoJSON(无需先转Geometry)
String wkt = "POINT(116.4 39.9)";
String geojson = GeometryUtil.wkt2Geojson(wkt);

// GeoJSON转WKT
String wktBack = GeometryUtil.geojson2Wkt(geojson);

7.3 空间关系判断

7.3.1 基于JTS Geometry的方法

// 创建测试几何
Geometry geomA = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
Geometry geomB = GeometryUtil.wkt2Geometry(
    "POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))");
Geometry geomC = GeometryUtil.wkt2Geometry(
    "POLYGON((20 20, 20 30, 30 30, 30 20, 20 20))");

// 相交判断 - 两个几何是否有公共点
boolean intersects = GeometryUtil.intersects(geomA, geomB);  // true
boolean intersectsC = GeometryUtil.intersects(geomA, geomC); // false

// 包含判断 - A是否完全包含B
Geometry smallGeom = GeometryUtil.wkt2Geometry(
    "POLYGON((2 2, 2 8, 8 8, 8 2, 2 2))");
boolean contains = GeometryUtil.contains(geomA, smallGeom);  // true

// 在内判断 - A是否完全在B内部
boolean within = GeometryUtil.within(smallGeom, geomA);      // true

// 相接判断 - 只在边界相接
Geometry touchGeom = GeometryUtil.wkt2Geometry(
    "POLYGON((10 0, 10 10, 20 10, 20 0, 10 0))");
boolean touches = GeometryUtil.touches(geomA, touchGeom);    // true

// 交叉判断 - 线穿过面
Geometry line = GeometryUtil.wkt2Geometry("LINESTRING(5 -5, 5 15)");
boolean crosses = GeometryUtil.crosses(line, geomA);         // true

// 重叠判断 - 部分重叠
boolean overlaps = GeometryUtil.overlaps(geomA, geomB);      // true

// 分离判断 - 完全不相交
boolean disjoint = GeometryUtil.disjoint(geomA, geomC);      // true

// 相等判断
Geometry geomACopy = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
boolean equals = GeometryUtil.equalsTopo(geomA, geomACopy);  // true

7.3.2 各种相等判断

Geometry g1 = GeometryUtil.wkt2Geometry("POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
Geometry g2 = GeometryUtil.wkt2Geometry("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))");

// 精确相等 - 坐标顺序完全相同
boolean exactEquals = GeometryUtil.equalsExact(g1, g2);  // false

// 精确相等(带容差)
boolean exactTol = GeometryUtil.equalsExactTolerance(g1, g2, 0.001);

// 规范化相等 - 规范化后比较
boolean normEquals = GeometryUtil.equalsNorm(g1, g2);    // true

// 拓扑相等 - 表示相同的点集
boolean topoEquals = GeometryUtil.equalsTopo(g1, g2);    // true

7.3.3 DE-9IM关系模式

Geometry geomA = GeometryUtil.wkt2Geometry("POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
Geometry geomB = GeometryUtil.wkt2Geometry("POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))");

// 获取DE-9IM矩阵
String matrix = GeometryUtil.relate(geomA, geomB);
System.out.println("DE-9IM: " + matrix);  // 输出: 212101212

// 使用模式匹配
boolean matches = GeometryUtil.relatePattern(geomA, geomB, "T*T***FF*");

7.3.4 基于WKT的方法(使用ESRI API)

String wktA = "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))";
String wktB = "POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))";
int wkid = 4490;

// 空间关系判断
boolean intersects = GeometryUtil.intersectsWkt(wktA, wktB, wkid);
boolean contains = GeometryUtil.containsWkt(wktA, wktB, wkid);
boolean within = GeometryUtil.withinWkt(wktA, wktB, wkid);
boolean disjoint = GeometryUtil.disjointWkt(wktA, wktB, wkid);
boolean touches = GeometryUtil.touchesWkt(wktA, wktB, wkid);
boolean crosses = GeometryUtil.crossesWkt(wktA, wktB, wkid);
boolean overlaps = GeometryUtil.overlapsWkt(wktA, wktB, wkid);
boolean equals = GeometryUtil.equalsWkt(wktA, wktB, wkid);

// 模式匹配
boolean pattern = GeometryUtil.relatePatternWkt(wktA, wktB, wkid, "T*T***FF*");

7.4 空间分析

7.4.1 缓冲区分析

// 基于Geometry
Geometry point = GeometryUtil.wkt2Geometry("POINT(116.4 39.9)");
Geometry buffer = GeometryUtil.buffer(point, 0.01);  // 缓冲距离

Geometry line = GeometryUtil.wkt2Geometry("LINESTRING(0 0, 10 10)");
Geometry lineBuffer = GeometryUtil.buffer(line, 2.0);

// 基于WKT
String wkt = "POINT(116.4 39.9)";
String bufferWkt = GeometryUtil.bufferWkt(wkt, 4490, 0.01);

7.4.2 交集/并集/差集

Geometry geomA = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
Geometry geomB = GeometryUtil.wkt2Geometry(
    "POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))");

// 交集 - 公共部分
Geometry intersection = GeometryUtil.intersection(geomA, geomB);
System.out.println("交集: " + intersection.toText());
// POLYGON ((5 5, 5 10, 10 10, 10 5, 5 5))

// 并集 - 合并
Geometry union = GeometryUtil.union(geomA, geomB);
System.out.println("并集: " + union.toText());

// 差集 - A减去B
Geometry difference = GeometryUtil.difference(geomA, geomB);
System.out.println("差集: " + difference.toText());

// 对称差 - 两个几何的非公共部分
Geometry symDiff = GeometryUtil.symDifference(geomA, geomB);
System.out.println("对称差: " + symDiff.toText());

7.4.3 基于WKT的空间分析

String wktA = "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))";
String wktB = "POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))";
int wkid = 4490;

// 交集
String intersection = GeometryUtil.intersectionWkt(wktA, wktB, wkid);

// 差集
String difference = GeometryUtil.differenceWkt(wktA, wktB, wkid);

// 对称差
String symDiff = GeometryUtil.symDifferenceWkt(wktA, wktB, wkid);

// 并集(支持多个几何)
List<String> wktList = Arrays.asList(wktA, wktB);
String union = GeometryUtil.unionWkt(wktList, wkid);

// 缓冲区
String buffer = GeometryUtil.bufferWkt(wktA, wkid, 1.0);

7.4.4 凸包与凹包

// 凸包 - 最小凸多边形
Geometry points = GeometryUtil.wkt2Geometry(
    "MULTIPOINT((0 0), (0 10), (10 10), (10 0), (5 5))");
Geometry convexHull = GeometryUtil.convexHull(points);
System.out.println("凸包: " + convexHull.toText());

// 凹包 - 考虑凹入部分的外包
Geometry concaveHull = GeometryUtil.concaveHull(points);
System.out.println("凹包: " + concaveHull.toText());

// 基于WKT的凸包
String wkt = "MULTIPOINT((0 0), (0 10), (10 10), (10 0), (5 5))";
String convexHullWkt = GeometryUtil.convexHullWkt(wkt);

7.4.5 多边形分割

// 使用线分割多边形
Geometry polygon = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
Geometry splitLine = GeometryUtil.wkt2Geometry(
    "LINESTRING(5 -1, 5 11)");

Geometry result = GeometryUtil.splitPolygon(polygon, splitLine);
System.out.println("分割结果: " + result.toText());
// GEOMETRYCOLLECTION (POLYGON(...), POLYGON(...))

7.4.6 多边形化

// 从线构建多边形
Geometry lines = GeometryUtil.wkt2Geometry(
    "MULTILINESTRING(" +
    "(0 0, 0 10), (0 10, 10 10), (10 10, 10 0), (10 0, 0 0)" +
    ")");

Geometry polygons = GeometryUtil.polygonize(lines);
System.out.println("多边形化: " + polygons.toText());

7.5 几何属性

7.5.1 面积与长度

// 面积计算
Geometry polygon = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
double area = GeometryUtil.area(polygon);
System.out.println("面积: " + area);  // 100.0

// 基于WKT
double areaWkt = GeometryUtil.areaWkt(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");

// 长度计算
Geometry line = GeometryUtil.wkt2Geometry(
    "LINESTRING(0 0, 10 0, 10 10)");
double length = GeometryUtil.length(line);
System.out.println("长度: " + length);  // 20.0

// 多边形周长
double perimeter = GeometryUtil.length(polygon);
System.out.println("周长: " + perimeter);  // 40.0

// 基于WKT
double lengthWkt = GeometryUtil.lengthWkt("LINESTRING(0 0, 10 0, 10 10)");

7.5.2 质心与内点

Geometry polygon = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");

// 质心 - 几何中心
Geometry centroid = GeometryUtil.centroid(polygon);
System.out.println("质心: " + centroid.toText());  // POINT (5 5)

// 内点 - 保证在几何内部
Geometry interiorPoint = GeometryUtil.interiorPoint(polygon);
System.out.println("内点: " + interiorPoint.toText());

// 基于WKT
String centroidWkt = GeometryUtil.centroidWkt(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");

7.5.3 维度与点数

Geometry point = GeometryUtil.wkt2Geometry("POINT(0 0)");
Geometry line = GeometryUtil.wkt2Geometry("LINESTRING(0 0, 10 10)");
Geometry polygon = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");

// 维度
int dimPoint = GeometryUtil.dimension(point);      // 0
int dimLine = GeometryUtil.dimension(line);        // 1
int dimPolygon = GeometryUtil.dimension(polygon);  // 2

// 点数
int numPoints = GeometryUtil.numPoints(polygon);
System.out.println("点数: " + numPoints);  // 5

// 几何类型
GeometryType type = GeometryUtil.geometryType(polygon);
System.out.println("类型: " + type);  // POLYGON

// 是否为空
boolean isEmpty = GeometryUtil.isEmpty(polygon);  // false

// 基于WKT
int dimWkt = GeometryUtil.dimensionWkt("POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
boolean isEmptyWkt = GeometryUtil.isEmptyWkt("POINT EMPTY");
GeometryType typeWkt = GeometryUtil.geometryTypeWkt("LINESTRING(0 0, 10 10)");

7.5.4 边界与外包矩形

Geometry polygon = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");

// 边界 - 几何的边界
Geometry boundary = GeometryUtil.boundary(polygon);
System.out.println("边界: " + boundary.toText());
// LINESTRING (0 0, 0 10, 10 10, 10 0, 0 0)

// 外包矩形
Geometry envelope = GeometryUtil.envelope(polygon);
System.out.println("外包矩形: " + envelope.toText());
// POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))

// 基于WKT
String boundaryWkt = GeometryUtil.boundaryWkt(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");

7.5.5 距离计算

Geometry point1 = GeometryUtil.wkt2Geometry("POINT(0 0)");
Geometry point2 = GeometryUtil.wkt2Geometry("POINT(10 0)");

// 计算距离
double distance = GeometryUtil.distance(point1, point2);
System.out.println("距离: " + distance);  // 10.0

// 判断是否在指定距离内
boolean withinDist = GeometryUtil.isWithinDistance(point1, point2, 15);
System.out.println("距离小于15: " + withinDist);  // true

// 基于WKT
double distWkt = GeometryUtil.distanceWkt("POINT(0 0)", "POINT(10 0)", 4490);

7.6 几何验证与修复

7.6.1 有效性验证

import com.znlgis.ogu4j.engine.model.TopologyValidationResult;

// 有效几何
Geometry validPolygon = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))");
TopologyValidationResult result1 = GeometryUtil.isValid(validPolygon);
System.out.println("有效: " + result1.isValid());  // true

// 无效几何(自相交)
Geometry invalidPolygon = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 10 10, 0 10, 10 0, 0 0))");  // 8字形
TopologyValidationResult result2 = GeometryUtil.isValid(invalidPolygon);
System.out.println("有效: " + result2.isValid());  // false
System.out.println("错误类型: " + result2.getErrorType().getDesc());
System.out.println("错误位置: " + result2.getCoordinate());
System.out.println("错误信息: " + result2.getMessage());

7.6.2 简单性验证

import com.znlgis.ogu4j.engine.model.SimpleGeometryResult;

// 检查几何是否简单(无自相交)
Geometry line = GeometryUtil.wkt2Geometry(
    "LINESTRING(0 0, 10 10, 0 10, 10 0)");  // 自相交线
SimpleGeometryResult result = GeometryUtil.isSimple(line);
System.out.println("简单: " + result.isSimple());  // false
System.out.println("非简单点: " + result.getNonSimplePts());

// 基于WKT
boolean isSimple = GeometryUtil.isSimpleWkt(
    "LINESTRING(0 0, 10 10)", 4490);

7.6.3 几何修复

// 无效几何
Geometry invalid = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 10 10, 0 10, 10 0, 0 0))");

// 修复几何
Geometry validated = GeometryUtil.validate(invalid);
System.out.println("修复后: " + validated.toText());

// 验证修复结果
TopologyValidationResult result = GeometryUtil.isValid(validated);
System.out.println("现在有效: " + result.isValid());  // true

7.6.4 几何简化

// 复杂多边形
Geometry complex = GeometryUtil.wkt2Geometry(
    "POLYGON((0 0, 1 1, 2 0.5, 3 1, 4 0, 4 10, 0 10, 0 0))");

// 简化(Douglas-Peucker算法)
double tolerance = 1.0;
Geometry simplified = GeometryUtil.simplify(complex, tolerance);
System.out.println("简化后: " + simplified.toText());

// 基于WKT
String simplifiedWkt = GeometryUtil.simplifyWkt(
    "POLYGON((0 0, 1 1, 2 0.5, 3 1, 4 0, 4 10, 0 10, 0 0))", 4490);

7.6.5 几何加密

// 为几何添加更多点
Geometry line = GeometryUtil.wkt2Geometry("LINESTRING(0 0, 100 0)");

// 加密,每隔10单位添加一个点
Geometry densified = GeometryUtil.densify(line, 10);
System.out.println("加密后点数: " + GeometryUtil.numPoints(densified));

7.7 错误类型详解

7.7.1 TopologyValidationErrorType枚举

/**
 * 拓扑验证错误类型
 */
public enum TopologyValidationErrorType {
    
    ERROR("拓扑检查错误"),
    REPEATED_POINT("点重叠"),
    HOLE_OUTSIDE_SHELL("洞在图形外"),
    NESTED_HOLES("洞重叠"),
    DISCONNECTED_INTERIOR("图形内部不连通"),
    SELF_INTERSECTION("自相交"),
    RING_SELF_INTERSECTION("环自相交"),
    NESTED_SHELLS("图形重叠"),
    DUPLICATE_RINGS("环重复"),
    TOO_FEW_POINTS("点太少无法构成有效几何"),
    INVALID_COORDINATE("无效坐标"),
    RING_NOT_CLOSED("环未闭合");
    
    private final String desc;
    
    // ...
}

7.7.2 常见错误示例

// 自相交
String selfIntersect = "POLYGON((0 0, 10 10, 0 10, 10 0, 0 0))";

// 环未闭合
String notClosed = "POLYGON((0 0, 0 10, 10 10, 10 0))";  // 首尾不同

// 点太少
String tooFew = "POLYGON((0 0, 5 5))";  // 少于3个点

// 洞在外面
String holeOutside = "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0), " +
                     "(20 20, 20 25, 25 25, 25 20, 20 20))";

// 重复点
String repeated = "POLYGON((0 0, 0 0, 0 10, 10 10, 10 0, 0 0))";

7.8 实战案例

7.8.1 面积统计

public class AreaStatistics {
    
    public static void calculateAreas(OguLayer layer) {
        if (layer.getGeometryType() != GeometryType.POLYGON &&
            layer.getGeometryType() != GeometryType.MULTIPOLYGON) {
            System.out.println("只支持面状图层");
            return;
        }
        
        double totalArea = 0;
        double maxArea = 0;
        double minArea = Double.MAX_VALUE;
        
        for (OguFeature feature : layer.getFeatures()) {
            Geometry geom = GeometryUtil.wkt2Geometry(feature.getWkt());
            double area = GeometryUtil.area(geom);
            
            totalArea += area;
            maxArea = Math.max(maxArea, area);
            minArea = Math.min(minArea, area);
            
            // 将面积写入属性
            feature.setValue("CALC_AREA", area);
        }
        
        int count = layer.getFeatureCount();
        System.out.printf("要素数量: %d%n", count);
        System.out.printf("总面积: %.2f%n", totalArea);
        System.out.printf("平均面积: %.2f%n", totalArea / count);
        System.out.printf("最大面积: %.2f%n", maxArea);
        System.out.printf("最小面积: %.2f%n", minArea);
    }
}

7.8.2 空间查询

public class SpatialQuery {
    
    /**
     * 查询与指定范围相交的要素
     */
    public static List<OguFeature> queryByBounds(OguLayer layer, 
            double minX, double minY, double maxX, double maxY) {
        
        String boundsWkt = String.format(
            "POLYGON((%f %f, %f %f, %f %f, %f %f, %f %f))",
            minX, minY, maxX, minY, maxX, maxY, minX, maxY, minX, minY);
        Geometry bounds = GeometryUtil.wkt2Geometry(boundsWkt);
        
        List<OguFeature> result = new ArrayList<>();
        for (OguFeature feature : layer.getFeatures()) {
            Geometry geom = GeometryUtil.wkt2Geometry(feature.getWkt());
            if (GeometryUtil.intersects(bounds, geom)) {
                result.add(feature);
            }
        }
        
        return result;
    }
    
    /**
     * 查询包含指定点的要素
     */
    public static List<OguFeature> queryByPoint(OguLayer layer,
            double x, double y) {
        
        Geometry point = GeometryUtil.wkt2Geometry(
            String.format("POINT(%f %f)", x, y));
        
        List<OguFeature> result = new ArrayList<>();
        for (OguFeature feature : layer.getFeatures()) {
            Geometry geom = GeometryUtil.wkt2Geometry(feature.getWkt());
            if (GeometryUtil.contains(geom, point)) {
                result.add(feature);
            }
        }
        
        return result;
    }
}

7.8.3 几何验证批处理

public class BatchValidator {
    
    /**
     * 批量验证并修复几何
     */
    public static void validateAndFix(OguLayer layer) {
        int valid = 0;
        int fixed = 0;
        int failed = 0;
        
        for (OguFeature feature : layer.getFeatures()) {
            if (feature.getWkt() == null) continue;
            
            Geometry geom = GeometryUtil.wkt2Geometry(feature.getWkt());
            TopologyValidationResult result = GeometryUtil.isValid(geom);
            
            if (result.isValid()) {
                valid++;
            } else {
                System.out.printf("FID %s: %s%n", 
                    feature.getFid(), result.getErrorType().getDesc());
                
                try {
                    Geometry fixedGeom = GeometryUtil.validate(geom);
                    feature.setWkt(GeometryUtil.geometry2Wkt(fixedGeom));
                    fixed++;
                } catch (Exception e) {
                    System.err.println("修复失败: " + e.getMessage());
                    failed++;
                }
            }
        }
        
        System.out.printf("%n验证完成: 有效 %d, 已修复 %d, 失败 %d%n",
            valid, fixed, failed);
    }
}

7.8.4 缓冲区分析

public class BufferAnalysis {
    
    /**
     * 创建缓冲区图层
     */
    public static OguLayer createBufferLayer(OguLayer inputLayer, 
            double distance) {
        
        OguLayer bufferLayer = new OguLayer();
        bufferLayer.setName(inputLayer.getName() + "_Buffer");
        bufferLayer.setWkid(inputLayer.getWkid());
        bufferLayer.setGeometryType(GeometryType.POLYGON);
        bufferLayer.setFields(inputLayer.getFields());
        
        List<OguFeature> features = new ArrayList<>();
        for (OguFeature inputFeature : inputLayer.getFeatures()) {
            Geometry geom = GeometryUtil.wkt2Geometry(inputFeature.getWkt());
            Geometry buffer = GeometryUtil.buffer(geom, distance);
            
            OguFeature bufferFeature = new OguFeature();
            bufferFeature.setFid(inputFeature.getFid());
            bufferFeature.setWkt(GeometryUtil.geometry2Wkt(buffer));
            
            // 复制属性
            for (OguField field : inputLayer.getFields()) {
                Object value = inputFeature.getValue(field.getName());
                bufferFeature.setValue(field.getName(), value);
            }
            
            features.add(bufferFeature);
        }
        
        bufferLayer.setFeatures(features);
        return bufferLayer;
    }
}

← 上一章:数据格式转换实战 | 下一章:坐标系管理与转换 →

posted @ 2025-12-02 10:30  我才是银古  阅读(5)  评论(0)    收藏  举报