第04章 - 空间操作详解

第04章 - 空间操作详解

4.1 算子模式详解

4.1.1 Operator 基类

所有空间操作都继承自 Operator 抽象类:

public abstract class Operator {
    // 算子类型枚举
    public enum Type {
        Project,
        ExportToJson, ImportFromJson,
        ExportToESRIShape, ImportFromESRIShape,
        Union, Difference,
        Proximity2D, Centroid2D,
        Relate, Equals, Disjoint, Intersects, Within, Contains, Crosses, Touches, Overlaps,
        Buffer, Distance, Intersection, Clip, Cut, DensifyByLength, DensifyByAngle, LabelPoint,
        GeodesicBuffer, GeodeticDensifyByLength, ShapePreservingDensify, GeodeticLength, GeodeticArea,
        Simplify, SimplifyOGC, Offset, Generalize,
        ExportToWkb, ImportFromWkb, ExportToWkt, ImportFromWkt, 
        ImportFromGeoJson, ExportToGeoJson, 
        SymmetricDifference, ConvexHull, Boundary
    }
    
    // 获取算子类型
    public abstract Type getType();
    
    // 加速几何(用于重复操作的优化)
    public boolean accelerateGeometry(Geometry geometry, 
            SpatialReference sr, GeometryAccelerationDegree degree);
    
    // 判断是否可加速
    public boolean canAccelerateGeometry(Geometry geometry);
    
    // 移除加速
    public static void deaccelerateGeometry(Geometry geometry);
}

4.1.2 获取算子实例

// 方式一:通过工厂获取
OperatorBuffer buffer = (OperatorBuffer) OperatorFactoryLocal
    .getInstance()
    .getOperator(Operator.Type.Buffer);

// 方式二:使用静态方法(推荐)
OperatorBuffer buffer = OperatorBuffer.local();
OperatorUnion union = OperatorUnion.local();
OperatorIntersection intersection = OperatorIntersection.local();

4.1.3 GeometryCursor 流式处理

// 创建几何游标
Geometry[] geometries = {polygon1, polygon2, polygon3};
SimpleGeometryCursor inputCursor = new SimpleGeometryCursor(geometries);

// 执行批量操作
GeometryCursor resultCursor = OperatorBuffer.local().execute(
    inputCursor, 
    spatialReference, 
    new double[]{10.0},  // 缓冲距离
    true,                 // 是否合并结果
    null                  // 进度追踪器
);

// 读取结果
Geometry result;
while ((result = resultCursor.next()) != null) {
    // 处理每个结果
}

4.2 缓冲区操作 (Buffer)

4.2.1 基本缓冲区

// 创建点的缓冲区
Point point = new Point(116.4, 39.9);
SpatialReference sr = SpatialReference.create(4326);

// 使用 GeometryEngine
Polygon buffer = GeometryEngine.buffer(point, sr, 0.1);

// 使用 Operator
OperatorBuffer op = OperatorBuffer.local();
Polygon buffer2 = (Polygon) op.execute(point, sr, 0.1, null);

4.2.2 自定义缓冲区参数

// 控制缓冲区精度
OperatorBuffer op = OperatorBuffer.local();

// 参数说明:
// max_deviation: 最大偏差(与真实缓冲区的最大误差)
// max_vertices_in_full_circle: 完整圆的最大顶点数

double[] distances = {10.0};
double maxDeviation = 0.1;
int maxVertices = 96;  // 默认值

GeometryCursor result = op.execute(
    new SimpleGeometryCursor(geometry),
    sr,
    distances,
    maxDeviation,
    maxVertices,
    true,  // 是否合并
    null   // 进度追踪器
);

4.2.3 负缓冲区

// 负缓冲区用于多边形内缩
Polygon polygon = createPolygon();
SpatialReference sr = SpatialReference.create(4326);

// 负数表示内缩
Polygon shrunk = GeometryEngine.buffer(polygon, sr, -0.01);

// 注意:如果内缩过多,可能返回空几何
if (shrunk.isEmpty()) {
    System.out.println("几何完全被内缩了");
}

4.2.4 批量缓冲区

// 对多个几何同时计算缓冲区
Geometry[] geometries = {point1, point2, line1, polygon1};
double[] distances = {10, 20, 15, 5};  // 各自的缓冲距离

Polygon[] buffers = GeometryEngine.buffer(geometries, sr, distances, false);
// false = 不合并结果,返回各自的缓冲区

// 合并所有缓冲区
Polygon[] unionedBuffer = GeometryEngine.buffer(geometries, sr, distances, true);
// true = 合并结果,返回单个多边形

4.3 合并操作 (Union)

4.3.1 基本合并

// 合并两个几何
Polygon p1 = createPolygon1();
Polygon p2 = createPolygon2();
SpatialReference sr = SpatialReference.create(4326);

// 使用数组合并
Geometry[] geometries = {p1, p2};
Geometry result = GeometryEngine.union(geometries, sr);

4.3.2 使用 Operator 合并

OperatorUnion op = OperatorUnion.local();

// 合并多个几何
Geometry[] geometries = {polygon1, polygon2, polygon3};
SimpleGeometryCursor cursor = new SimpleGeometryCursor(geometries);

GeometryCursor resultCursor = op.execute(cursor, sr, null);
Geometry unionResult = resultCursor.next();

4.3.3 增量合并

对于大量几何的合并,建议使用增量方式:

public Geometry unionMany(List<Geometry> geometries, SpatialReference sr) {
    if (geometries.isEmpty()) {
        return new Polygon();
    }
    
    OperatorUnion op = OperatorUnion.local();
    
    // 每次合并一批
    int batchSize = 100;
    List<Geometry> current = new ArrayList<>(geometries);
    
    while (current.size() > 1) {
        List<Geometry> next = new ArrayList<>();
        
        for (int i = 0; i < current.size(); i += batchSize) {
            int end = Math.min(i + batchSize, current.size());
            Geometry[] batch = current.subList(i, end).toArray(new Geometry[0]);
            
            SimpleGeometryCursor cursor = new SimpleGeometryCursor(batch);
            GeometryCursor result = op.execute(cursor, sr, null);
            next.add(result.next());
        }
        
        current = next;
    }
    
    return current.get(0);
}

4.4 求交操作 (Intersection)

4.4.1 基本求交

Polygon p1 = createPolygon1();
Polygon p2 = createPolygon2();
SpatialReference sr = SpatialReference.create(4326);

// 计算交集
Geometry intersection = GeometryEngine.intersect(p1, p2, sr);

// 结果类型取决于输入几何
// 两个多边形的交集可能是:多边形、线、点或空

4.4.2 指定结果维度

OperatorIntersection op = OperatorIntersection.local();

// dimension 参数:
// 1 = 只返回点
// 2 = 只返回线
// 4 = 只返回面
// 7 = 返回所有 (1+2+4)

GeometryCursor result = op.execute(
    new SimpleGeometryCursor(geometry1),
    new SimpleGeometryCursor(geometry2),
    sr,
    null,
    7  // 返回所有维度的结果
);

// 遍历不同维度的结果
Geometry g;
while ((g = result.next()) != null) {
    System.out.println("类型: " + g.getType() + ", 维度: " + g.getDimension());
}

4.4.3 与包围盒求交

// 使用 Clip 操作与矩形求交更高效
Envelope clipEnvelope = new Envelope(0, 0, 100, 100);
Geometry clipped = GeometryEngine.clip(geometry, clipEnvelope, sr);

4.5 差集操作 (Difference)

4.5.1 基本差集

Polygon p1 = createPolygon1();
Polygon p2 = createPolygon2();
SpatialReference sr = SpatialReference.create(4326);

// 计算 p1 - p2(从 p1 中减去与 p2 相交的部分)
Geometry difference = GeometryEngine.difference(p1, p2, sr);

4.5.2 对称差集

// 对称差集:(A - B) ∪ (B - A)
// 即两个几何不重叠的部分

Geometry symDiff = GeometryEngine.symmetricDifference(p1, p2, sr);

// 使用 Operator
OperatorSymmetricDifference op = OperatorSymmetricDifference.local();
Geometry result = op.execute(p1, p2, sr, null);

4.5.3 擦除操作示例

/**
 * 从地图区域中擦除湖泊区域
 */
public Polygon eraseLakes(Polygon landArea, List<Polygon> lakes, SpatialReference sr) {
    Polygon result = landArea;
    
    for (Polygon lake : lakes) {
        Geometry diff = GeometryEngine.difference(result, lake, sr);
        if (!diff.isEmpty() && diff instanceof Polygon) {
            result = (Polygon) diff;
        }
    }
    
    return result;
}

4.6 裁剪操作 (Clip)

4.6.1 基本裁剪

// Clip 专门用于矩形裁剪,比 Intersection 更高效
Geometry geometry = createComplexPolygon();
Envelope clipRect = new Envelope(10, 10, 50, 50);
SpatialReference sr = SpatialReference.create(4326);

Geometry clipped = GeometryEngine.clip(geometry, clipRect, sr);

4.6.2 批量裁剪

OperatorClip op = OperatorClip.local();

Geometry[] geometries = {polygon1, polygon2, polygon3};
SimpleGeometryCursor cursor = new SimpleGeometryCursor(geometries);

Envelope2D clipEnv = Envelope2D.construct(0, 0, 100, 100);
GeometryCursor result = op.execute(cursor, clipEnv, sr, null);

Geometry g;
while ((g = result.next()) != null) {
    // 处理裁剪结果
}

4.7 切割操作 (Cut)

4.7.1 使用线切割多边形

Polygon polygon = createPolygon();
Polyline cutter = new Polyline();
cutter.startPath(-10, 50);
cutter.lineTo(110, 50);  // 水平线

SpatialReference sr = SpatialReference.create(4326);

// 切割多边形
Geometry[] pieces = GeometryEngine.cut(polygon, cutter, sr);

// 结果包含切割后的各个部分
for (Geometry piece : pieces) {
    System.out.println("切割片面积: " + piece.calculateArea2D());
}

4.7.2 切割折线

Polyline polyline = createPolyline();
Polyline cutter = createCutterLine();
SpatialReference sr = SpatialReference.create(4326);

OperatorCut op = OperatorCut.local();
GeometryCursor result = op.execute(true, polyline, cutter, sr, null);

// true = 考虑左右两侧
// 结果按左侧/右侧分组

4.8 凸包操作 (ConvexHull)

4.8.1 单个几何的凸包

MultiPoint points = new MultiPoint();
points.add(0, 0);
points.add(10, 0);
points.add(5, 8);
points.add(3, 3);
points.add(7, 3);

// 计算凸包
Geometry hull = GeometryEngine.convexHull(points);
// 结果是包含所有点的最小凸多边形

4.8.2 多个几何的合并凸包

Geometry[] geometries = {polygon1, polygon2, multiPoint};

// merge = true: 计算所有几何的合并凸包
Geometry[] hulls = GeometryEngine.convexHull(geometries, true);
// 结果只有一个几何

// merge = false: 分别计算每个几何的凸包
Geometry[] individualHulls = GeometryEngine.convexHull(geometries, false);

4.9 简化操作 (Simplify)

4.9.1 ESRI 简化

// ESRI 简化规则
Geometry geometry = createComplexGeometry();
SpatialReference sr = SpatialReference.create(4326);

Geometry simplified = GeometryEngine.simplify(geometry, sr);

4.9.2 OGC 简化

// OGC 简化规则(更严格)
OperatorSimplifyOGC op = OperatorSimplifyOGC.local();

Geometry simplified = op.execute(geometry, sr, true, null);
// true = 强制执行

// 检查是否需要简化
boolean isSimple = op.isSimpleOGC(geometry, sr, true, null, null);

4.9.3 简化检查结果

NonSimpleResult result = new NonSimpleResult();
boolean isSimple = OperatorSimplify.local().isSimpleAsFeature(
    geometry, sr, true, result, null);

if (!isSimple) {
    System.out.println("不简单的原因: " + result.m_reason);
    System.out.println("问题点索引: " + result.m_vertexIndex1);
}

4.10 泛化操作 (Generalize)

4.10.1 道格拉斯-普克算法

Polyline complexLine = createComplexPolyline();

// 泛化参数:最大偏差距离
double maxDeviation = 1.0;

OperatorGeneralize op = OperatorGeneralize.local();
Geometry simplified = op.execute(
    complexLine, 
    maxDeviation, 
    true,  // 移除退化部分
    null
);

System.out.println("原始点数: " + ((Polyline)complexLine).getPointCount());
System.out.println("简化后点数: " + ((Polyline)simplified).getPointCount());

4.10.2 批量泛化

Geometry[] geometries = {line1, line2, polygon1};
SimpleGeometryCursor cursor = new SimpleGeometryCursor(geometries);

GeometryCursor result = OperatorGeneralize.local().execute(
    cursor, 
    1.0,   // 最大偏差
    true,  // 移除退化
    null
);

Geometry g;
while ((g = result.next()) != null) {
    // 处理简化后的几何
}

4.11 偏移操作 (Offset)

4.11.1 线偏移

Polyline line = new Polyline();
line.startPath(0, 0);
line.lineTo(100, 0);
line.lineTo(100, 100);

SpatialReference sr = SpatialReference.create(4326);

OperatorOffset op = OperatorOffset.local();

// 正数 = 左偏移,负数 = 右偏移
double offsetDistance = 10.0;

// 连接类型
OperatorOffset.JoinType joinType = OperatorOffset.JoinType.Round;
// Round = 圆角连接
// Miter = 尖角连接
// Bevel = 斜角连接

// 斜接限制(仅 Miter 类型有效)
double bevelRatio = 2.0;
// 扁平度(仅 Round 类型有效)
double flatness = 0.0;

GeometryCursor result = op.execute(
    new SimpleGeometryCursor(line),
    sr,
    offsetDistance,
    joinType,
    bevelRatio,
    flatness,
    null
);

Geometry offsetLine = result.next();

4.11.2 多边形偏移

Polygon polygon = createPolygon();

// 正偏移 = 外扩,负偏移 = 内缩
Geometry outer = OperatorOffset.local().execute(
    new SimpleGeometryCursor(polygon),
    sr, 5.0, 
    OperatorOffset.JoinType.Round, 
    2.0, 0.0, null
).next();

Geometry inner = OperatorOffset.local().execute(
    new SimpleGeometryCursor(polygon),
    sr, -3.0,
    OperatorOffset.JoinType.Round,
    2.0, 0.0, null
).next();

4.12 加密操作 (Densify)

4.12.1 按长度加密

Polyline sparseLine = new Polyline();
sparseLine.startPath(0, 0);
sparseLine.lineTo(100, 0);

SpatialReference sr = SpatialReference.create(4326);

// 每 10 单位插入一个点
OperatorDensifyByLength op = OperatorDensifyByLength.local();
GeometryCursor result = op.execute(
    new SimpleGeometryCursor(sparseLine),
    10.0,  // 最大线段长度
    null
);

Polyline densified = (Polyline) result.next();
System.out.println("原始点数: " + sparseLine.getPointCount());
System.out.println("加密后点数: " + densified.getPointCount());

4.12.2 测地加密

// 按大地测量距离加密(考虑地球曲率)
OperatorGeodeticDensifyByLength op = 
    (OperatorGeodeticDensifyByLength) OperatorFactoryLocal
    .getInstance()
    .getOperator(Operator.Type.GeodeticDensifyByLength);

GeometryCursor result = op.execute(
    new SimpleGeometryCursor(geometry),
    sr,
    1000.0,  // 1000 米
    null     // 使用默认曲线类型
);

4.13 边界操作 (Boundary)

4.13.1 获取边界

// 多边形的边界是折线
Polygon polygon = createPolygon();
Geometry boundary = polygon.getBoundary();
System.out.println("边界类型: " + boundary.getType()); // Polyline

// 折线的边界是端点(多点)
Polyline polyline = createPolyline();
Geometry endpoints = polyline.getBoundary();
System.out.println("端点类型: " + endpoints.getType()); // MultiPoint

// 点没有边界
Point point = new Point(0, 0);
Geometry pointBoundary = point.getBoundary();
System.out.println("点边界: " + pointBoundary); // null

4.13.2 使用 Operator

OperatorBoundary op = OperatorBoundary.local();
Geometry boundary = op.execute(geometry, null);

4.14 几何加速

4.14.1 加速原理

对于重复的空间关系测试,可以预先加速几何对象:

Polygon largePolygon = createLargePolygon();
SpatialReference sr = SpatialReference.create(4326);

// 预先加速几何
OperatorContains op = OperatorContains.local();
boolean canAccelerate = op.canAccelerateGeometry(largePolygon);

if (canAccelerate) {
    op.accelerateGeometry(
        largePolygon, 
        sr, 
        Geometry.GeometryAccelerationDegree.enumMedium
    );
}

// 现在对大量点进行包含测试会更快
for (Point point : thousandsOfPoints) {
    boolean contains = op.execute(largePolygon, point, sr, null);
}

// 完成后移除加速(释放内存)
Operator.deaccelerateGeometry(largePolygon);

4.14.2 加速级别

public enum GeometryAccelerationDegree {
    enumMild,    // 轻度加速,内存占用最小
    enumMedium,  // 中度加速,平衡
    enumHot      // 高度加速,最快但内存最大
}

4.15 本章小结

本章详细介绍了 geometry-api-java 的空间操作:

  1. 算子模式:所有操作通过 Operator 类执行
  2. 流式处理:GeometryCursor 支持大数据处理
  3. 缓冲区:Buffer 操作,支持正负距离
  4. 布尔运算:Union、Intersection、Difference、SymmetricDifference
  5. 裁剪切割:Clip(矩形裁剪)、Cut(线切割)
  6. 几何处理:ConvexHull、Simplify、Generalize、Offset、Densify
  7. 边界操作:获取几何边界
  8. 几何加速:优化重复操作性能

最佳实践

  • 简单操作使用 GeometryEngine
  • 批量操作使用 Operator + Cursor
  • 重复判断使用几何加速
  • 大数据使用流式处理

← 上一章:几何对象详解 | 下一章:空间关系判断 →

posted @ 2025-12-03 15:15  我才是银古  阅读(3)  评论(0)    收藏  举报