第03章 - 几何对象详解

第03章 - 几何对象详解

3.1 Geometry 类层次结构

3.1.1 类继承关系

geometry-api-java 的几何对象采用清晰的类层次设计:

                        ┌─────────────────────┐
                        │     Geometry        │
                        │    (abstract)       │
                        └──────────┬──────────┘
                                   │
          ┌────────────────────────┼────────────────────────┐
          │                        │                        │
          ▼                        ▼                        ▼
    ┌───────────┐           ┌───────────┐           ┌───────────────────┐
    │   Point   │           │ Envelope  │           │MultiVertexGeometry│
    │           │           │           │           │    (abstract)     │
    └───────────┘           └───────────┘           └─────────┬─────────┘
                                                              │
                                   ┌──────────────────────────┼────────────┐
                                   │                          │            │
                                   ▼                          ▼            ▼
                            ┌───────────┐              ┌───────────┐ ┌───────────┐
                            │ MultiPoint│              │ MultiPath │ │   Line    │
                            │           │              │(abstract) │ │ (Segment) │
                            └───────────┘              └─────┬─────┘ └───────────┘
                                                             │
                                                    ┌────────┴────────┐
                                                    │                 │
                                                    ▼                 ▼
                                             ┌───────────┐     ┌───────────┐
                                             │ Polyline  │     │  Polygon  │
                                             │           │     │           │
                                             └───────────┘     └───────────┘

3.1.2 几何类型枚举

Geometry.Type 枚举定义了所有支持的几何类型:

public enum Type {
    Unknown,    // 未知类型
    Point,      // 点
    Line,       // 线段
    Envelope,   // 包围盒
    MultiPoint, // 多点
    Polyline,   // 折线
    Polygon     // 多边形
}

几何类型属性判断

// 判断几何维度
int dim = Geometry.getDimensionFromType(type.value());
// Point/MultiPoint: 0
// Line/Polyline/Envelope: 1
// Polygon: 2

// 判断几何特征
boolean isPoint = Geometry.isPoint(type.value());
boolean isLinear = Geometry.isLinear(type.value());
boolean isArea = Geometry.isArea(type.value());
boolean isSegment = Geometry.isSegment(type.value());
boolean isMultiVertex = Geometry.isMultiVertex(type.value());
boolean isMultiPath = Geometry.isMultiPath(type.value());

3.1.3 Geometry 基类方法

Geometry 抽象类定义了所有几何对象的公共接口:

public abstract class Geometry {
    // 获取几何类型
    public abstract Geometry.Type getType();
    
    // 获取维度(0-点,1-线,2-面)
    public abstract int getDimension();
    
    // 判断是否为空
    public abstract boolean isEmpty();
    
    // 设置为空
    public abstract void setEmpty();
    
    // 查询边界框
    public abstract void queryEnvelope(Envelope env);
    public abstract void queryEnvelope2D(Envelope2D env);
    
    // 应用变换
    public abstract void applyTransformation(Transformation2D transform);
    
    // 创建空实例
    public abstract Geometry createInstance();
    
    // 复制到目标
    public abstract void copyTo(Geometry dst);
    
    // 复制自身
    public Geometry copy();
    
    // 获取边界
    public abstract Geometry getBoundary();
    
    // 计算面积和长度
    public double calculateArea2D();
    public double calculateLength2D();
    
    // 估算内存大小
    public abstract long estimateMemorySize();
    
    // 获取状态标记(用于缓存失效判断)
    public int getStateFlag();
    
    // 属性管理
    public boolean hasZ();
    public boolean hasM();
    public boolean hasID();
    public void addAttribute(int semantics);
    public void dropAttribute(int semantics);
}

3.2 Point - 点几何

3.2.1 Point 基础

Point 是最基本的几何类型,表示二维或三维空间中的一个位置:

// 创建方式
Point p1 = new Point();                    // 空点
Point p2 = new Point(116.4, 39.9);         // 2D 点
Point p3 = new Point(116.4, 39.9, 100);    // 3D 点
Point p4 = new Point(new Point2D(116.4, 39.9)); // 从 Point2D 创建

3.2.2 坐标操作

Point point = new Point(116.4, 39.9);

// 获取坐标
double x = point.getX();
double y = point.getY();
double z = point.getZ();  // 需要先添加 Z 属性

// 设置坐标
point.setX(117.0);
point.setY(40.0);
point.setZ(150.0);        // 自动添加 Z 属性

// 批量操作
point.setXY(118.0, 41.0);
Point2D xy = point.getXY();
Point3D xyz = point.getXYZ();

3.2.3 属性操作

Point point = new Point(116.4, 39.9);

// M 值(测量值)
point.setM(100.5);
double m = point.getM();

// ID 值(标识符)
point.setID(1001);
int id = point.getID();

// 通用属性操作
point.setAttribute(VertexDescription.Semantics.Z, 0, 100.0);
double z = point.getAttributeAsDbl(VertexDescription.Semantics.Z, 0);

3.2.4 Point 判断与操作

Point point = new Point(116.4, 39.9);

// 判断是否为空
boolean isEmpty = point.isEmpty();

// 判断是否有属性
boolean hasZ = point.hasZ();
boolean hasM = point.hasM();
boolean hasID = point.hasID();

// 清空点
point.setEmpty();

// 复制点
Point copy = (Point) point.copy();

// 判断相等
boolean equals = point.equals(copy);

3.2.5 Point2D 和 Point3D

这两个类是轻量级的坐标点,主要用于内部计算和临时存储:

// Point2D - 2D 坐标
Point2D p2d = new Point2D(116.4, 39.9);
p2d.setCoords(117.0, 40.0);
double dist = p2d.distance(new Point2D(118.0, 41.0));
double length = p2d.length(); // 到原点距离

// Point3D - 3D 坐标
Point3D p3d = new Point3D();
p3d.setCoords(116.4, 39.9, 100);
double len3d = p3d.length();

Point 与 Point2D/Point3D 的区别

特性 Point Point2D/Point3D
继承 Geometry
用途 完整几何对象 临时计算
属性 支持 Z/M/ID 仅坐标
序列化 支持 不支持
开销 较大 极小

3.3 MultiPoint - 多点几何

3.3.1 MultiPoint 创建

// 创建空多点
MultiPoint mp = new MultiPoint();

// 添加点
mp.add(116.4, 39.9);
mp.add(117.0, 40.0);
mp.add(118.0, 41.0);

// 从 Point 数组创建
Point[] points = new Point[] {
    new Point(116.4, 39.9),
    new Point(117.0, 40.0),
    new Point(118.0, 41.0)
};
MultiPoint mp2 = new MultiPoint();
for (Point p : points) {
    mp2.add(p);
}

3.3.2 点的访问与修改

MultiPoint mp = new MultiPoint();
mp.add(116.4, 39.9);
mp.add(117.0, 40.0);
mp.add(118.0, 41.0);

// 获取点数量
int count = mp.getPointCount();

// 获取指定点
Point p = mp.getPoint(0);  // 返回新 Point 对象
Point2D p2d = mp.getXY(1); // 返回坐标
double x = mp.getAttributeAsDbl(VertexDescription.Semantics.POSITION, 0, 0);

// 设置坐标
mp.setXY(0, 120.0, 42.0);

// 移除点
mp.removePoint(0);

// 插入点
mp.insertPoint(1, new Point(119.0, 41.5));

3.3.3 批量操作

MultiPoint mp = new MultiPoint();

// 从另一个 MultiPoint 添加所有点
MultiPoint other = new MultiPoint();
other.add(116.4, 39.9);
other.add(117.0, 40.0);
mp.add(other, 0, other.getPointCount());

// 查询边界
Envelope2D env = new Envelope2D();
mp.queryEnvelope2D(env);

3.4 Polyline - 折线几何

3.4.1 Polyline 结构

Polyline 由一个或多个路径(Path)组成,每个路径是一系列连续的线段:

Polyline
├── Path 0: Point0 → Point1 → Point2
├── Path 1: Point3 → Point4 → Point5 → Point6
└── Path 2: Point7 → Point8

3.4.2 创建 Polyline

// 创建空折线
Polyline polyline = new Polyline();

// 开始一条路径
polyline.startPath(116.4, 39.9);
polyline.lineTo(117.0, 40.0);
polyline.lineTo(118.0, 41.0);

// 开始另一条路径
polyline.startPath(120.0, 31.0);
polyline.lineTo(121.0, 31.5);
polyline.lineTo(122.0, 32.0);

// 使用 Point 创建
polyline.startPath(new Point(115.0, 38.0));
polyline.lineTo(new Point(116.0, 39.0));

3.4.3 路径操作

Polyline polyline = new Polyline();
polyline.startPath(0, 0);
polyline.lineTo(10, 0);
polyline.lineTo(10, 10);
polyline.startPath(20, 20);
polyline.lineTo(30, 30);

// 获取路径数
int pathCount = polyline.getPathCount();

// 获取总点数
int pointCount = polyline.getPointCount();

// 获取指定路径的起始点索引
int pathStart = polyline.getPathStart(0);

// 获取指定路径的结束点索引
int pathEnd = polyline.getPathEnd(0);

// 获取指定路径的点数
int pathSize = polyline.getPathSize(0);

// 遍历路径
for (int path = 0; path < polyline.getPathCount(); path++) {
    int start = polyline.getPathStart(path);
    int end = polyline.getPathEnd(path);
    System.out.println("路径 " + path + ": 点 " + start + " 到 " + (end - 1));
    
    for (int i = start; i < end; i++) {
        Point2D pt = polyline.getXY(i);
        System.out.printf("  (%f, %f)%n", pt.x, pt.y);
    }
}

3.4.4 几何属性

Polyline polyline = new Polyline();
polyline.startPath(0, 0);
polyline.lineTo(10, 0);
polyline.lineTo(10, 10);

// 计算长度
double length = polyline.calculateLength2D();

// 获取边界
Envelope2D env = new Envelope2D();
polyline.queryEnvelope2D(env);

// 反转路径方向
polyline.reverseAllPaths();

// 添加线段
Line segment = new Line(new Point(10, 10), new Point(20, 20));
polyline.addSegment(segment, false); // false = 继续当前路径

3.4.5 使用 SegmentIterator 遍历

Polyline polyline = new Polyline();
polyline.startPath(0, 0);
polyline.lineTo(10, 0);
polyline.lineTo(10, 10);

// 创建线段迭代器
SegmentIterator iter = polyline.querySegmentIterator();

while (iter.nextPath()) {
    while (iter.hasNextSegment()) {
        Segment seg = iter.nextSegment();
        Point2D start = seg.getStartXY();
        Point2D end = seg.getEndXY();
        System.out.printf("线段: (%f,%f) -> (%f,%f)%n", 
            start.x, start.y, end.x, end.y);
    }
}

3.5 Polygon - 多边形几何

3.5.1 Polygon 结构

Polygon 由一个或多个环(Ring)组成,分为外环(Exterior Ring)和内环(Interior Ring,即孔洞):

Polygon
├── Ring 0 (Exterior): 外边界
├── Ring 1 (Interior): 孔洞 1(属于 Ring 0)
├── Ring 2 (Interior): 孔洞 2(属于 Ring 0)
└── Ring 3 (Exterior): 另一个外边界(多部分多边形)

环的方向规则

  • 外环:逆时针方向(面积为正)
  • 内环:顺时针方向(面积为负)

3.5.2 创建 Polygon

// 创建简单多边形(矩形)
Polygon polygon = new Polygon();
polygon.startPath(0, 0);
polygon.lineTo(10, 0);
polygon.lineTo(10, 10);
polygon.lineTo(0, 10);
polygon.closePathWithLine(); // 闭合路径

// 创建带孔洞的多边形
Polygon polygonWithHole = new Polygon();
// 外环(逆时针)
polygonWithHole.startPath(0, 0);
polygonWithHole.lineTo(20, 0);
polygonWithHole.lineTo(20, 20);
polygonWithHole.lineTo(0, 20);
polygonWithHole.closePathWithLine();
// 内环/孔洞(顺时针)
polygonWithHole.startPath(5, 5);
polygonWithHole.lineTo(5, 15);
polygonWithHole.lineTo(15, 15);
polygonWithHole.lineTo(15, 5);
polygonWithHole.closePathWithLine();

// 从 Envelope 创建
Envelope env = new Envelope(0, 0, 10, 10);
Polygon rectPolygon = new Polygon();
rectPolygon.addEnvelope(env, false);

3.5.3 环的操作

Polygon polygon = new Polygon();
// 添加外环
polygon.startPath(0, 0);
polygon.lineTo(100, 0);
polygon.lineTo(100, 100);
polygon.lineTo(0, 100);
polygon.closePathWithLine();
// 添加孔洞
polygon.startPath(20, 20);
polygon.lineTo(20, 80);
polygon.lineTo(80, 80);
polygon.lineTo(80, 20);
polygon.closePathWithLine();

// 获取路径(环)数
int pathCount = polygon.getPathCount();

// 判断是否为外环
boolean isExterior = polygon.isExteriorRing(0);

// 获取外环数量
int exteriorCount = polygon.getExteriorRingCount();

// 计算单个环的面积
double ringArea = polygon.calculateRingArea2D(0);

// 计算总面积(外环 - 内环)
double totalArea = polygon.calculateArea2D();

3.5.4 Polygon 常用操作

Polygon polygon = new Polygon();
polygon.startPath(0, 0);
polygon.lineTo(10, 0);
polygon.lineTo(10, 10);
polygon.lineTo(0, 10);
polygon.closePathWithLine();

// 计算面积
double area = polygon.calculateArea2D();

// 计算周长
double perimeter = polygon.calculateLength2D();

// 获取边界
Envelope2D env = new Envelope2D();
polygon.queryEnvelope2D(env);

// 设置填充规则
polygon.setFillRule(Polygon.FillRule.enumFillRuleOddEven);  // 奇偶规则
polygon.setFillRule(Polygon.FillRule.enumFillRuleWinding);  // 非零规则

// 获取边界线
Geometry boundary = polygon.getBoundary();  // 返回 Polyline

3.5.5 填充规则详解

填充规则影响自相交多边形的内部区域判定:

// 奇偶规则(Odd-Even)
// 从点向外画射线,穿过奇数条边则在内部
polygon.setFillRule(Polygon.FillRule.enumFillRuleOddEven);

// 非零规则(Winding/Non-Zero)
// 计算射线穿过边的方向加减,非零则在内部
polygon.setFillRule(Polygon.FillRule.enumFillRuleWinding);

规则对比

对于一个自相交的"8"字形多边形:

  • 奇偶规则:中心交叉区域被视为外部
  • 非零规则:中心区域视情况而定

3.6 Envelope - 包围盒

3.6.1 Envelope 概述

Envelope 表示矩形包围盒,是最简单的面状几何:

// 创建包围盒
Envelope env = new Envelope();                    // 空包围盒
Envelope env2 = new Envelope(0, 0, 10, 10);       // 指定范围
Envelope env3 = new Envelope(new Point(5, 5));    // 从点创建

// 设置范围
env.setCoords(0, 0, 100, 100);

// 获取范围
double xmin = env.getXMin();
double ymin = env.getYMin();
double xmax = env.getXMax();
double ymax = env.getYMax();

// 获取尺寸
double width = env.getWidth();
double height = env.getHeight();

// 获取中心点
Point center = env.getCenter();
double cx = env.getCenterX();
double cy = env.getCenterY();

3.6.2 Envelope 操作

Envelope env1 = new Envelope(0, 0, 10, 10);
Envelope env2 = new Envelope(5, 5, 15, 15);

// 合并(扩展以包含另一个)
env1.merge(env2);

// 合并点
env1.merge(new Point(20, 20));

// 相交
Envelope intersection = new Envelope();
env1.intersect(env2); // 修改 env1

// 判断相交
boolean intersects = env1.isIntersecting(env2);

// 判断包含
boolean contains = env1.contains(new Point(5, 5));
boolean containsEnv = env1.contains(env2);

// 偏移
env1.offset(10, 10);

// 缩放
env1.inflate(5, 5);    // 各方向扩大 5
env1.inflate(-2, -2);  // 各方向收缩 2

3.6.3 Envelope2D 和 Envelope3D

轻量级包围盒类,用于内部计算:

// Envelope2D
Envelope2D env2d = new Envelope2D();
env2d.setCoords(0, 0, 10, 10);
double area = env2d.getArea();
boolean contains = env2d.contains(5, 5);

// Envelope3D
Envelope3D env3d = new Envelope3D();
env3d.setCoords(0, 0, 0, 10, 10, 10);
double volume = env3d.getVolume();

3.7 Line - 线段

3.7.1 Line 概述

Line 表示由两点定义的线段,属于 Segment 类型:

// 创建线段
Line line = new Line();
line.setStart(new Point(0, 0));
line.setEnd(new Point(10, 10));

// 或使用构造函数
Line line2 = new Line(new Point(0, 0), new Point(10, 10));

3.7.2 Line 属性

Line line = new Line(new Point(0, 0), new Point(10, 10));

// 获取端点
Point start = line.getStartPoint();
Point end = line.getEndPoint();
Point2D startXY = line.getStartXY();
Point2D endXY = line.getEndXY();

// 计算长度
double length = line.calculateLength2D();

// 获取中点
Point2D midPoint = new Point2D();
line.getCoord2D(0.5, midPoint);  // t=0.5 表示中点

// 获取边界
Envelope2D env = new Envelope2D();
line.queryEnvelope2D(env);

3.8 VertexDescription - 顶点描述

3.8.1 语义枚举

VertexDescription.Semantics 定义了顶点可以拥有的属性:

public interface Semantics {
    int POSITION = 0;  // 位置 (X, Y)
    int Z = 1;         // 高程
    int M = 2;         // 测量值
    int ID = 3;        // 标识符
    int NORMAL = 4;    // 法向量
    int TEXTURE = 5;   // 纹理坐标
}

3.8.2 使用示例

// 获取几何的顶点描述
Point point = new Point(116.4, 39.9);
VertexDescription vd = point.getDescription();

// 检查属性
boolean hasZ = vd.hasAttribute(VertexDescription.Semantics.Z);
int attrCount = vd.getAttributeCount();

// 添加属性
point.addAttribute(VertexDescription.Semantics.Z);
point.addAttribute(VertexDescription.Semantics.M);

// 删除属性
point.dropAttribute(VertexDescription.Semantics.M);

// 创建带属性的几何
VertexDescription vdWithZ = VertexDescriptionDesignerImpl
    .getVertexDescription(VertexDescription.Semantics.Z);
Polygon polygon = new Polygon(vdWithZ);

3.9 几何对象的序列化

3.9.1 Java 序列化

geometry-api-java 的几何对象实现了 Serializable 接口:

import java.io.*;

// 序列化
Polygon polygon = new Polygon();
polygon.startPath(0, 0);
polygon.lineTo(10, 0);
polygon.lineTo(10, 10);
polygon.closePathWithLine();

try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("polygon.ser"))) {
    oos.writeObject(polygon);
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("polygon.ser"))) {
    Polygon loaded = (Polygon) ois.readObject();
}

3.9.2 WKT/WKB 序列化

更推荐使用标准格式进行序列化:

Polygon polygon = new Polygon();
polygon.startPath(0, 0);
polygon.lineTo(10, 0);
polygon.lineTo(10, 10);
polygon.lineTo(0, 10);
polygon.closePathWithLine();

// 导出为 WKT
String wkt = GeometryEngine.geometryToWkt(polygon, 0);
// 输出: POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))

// 从 WKT 导入
Geometry imported = GeometryEngine.geometryFromWkt(wkt, 0, Geometry.Type.Unknown);

// 导出为 WKB
OperatorExportToWkb op = (OperatorExportToWkb) OperatorFactoryLocal
    .getInstance().getOperator(Operator.Type.ExportToWkb);
ByteBuffer wkb = op.execute(0, polygon, null);

// 从 WKB 导入
OperatorImportFromWkb importOp = (OperatorImportFromWkb) OperatorFactoryLocal
    .getInstance().getOperator(Operator.Type.ImportFromWkb);
Geometry fromWkb = importOp.execute(0, Geometry.Type.Unknown, wkb, null);

3.10 本章小结

本章详细介绍了 geometry-api-java 中的各种几何对象:

  1. 类层次结构:Geometry → Point/MultiPoint/Polyline/Polygon/Envelope
  2. Point:单点,支持 Z/M/ID 属性
  3. MultiPoint:点集合,支持批量操作
  4. Polyline:由多条路径组成的折线
  5. Polygon:由外环和内环组成的面
  6. Envelope:矩形包围盒
  7. Line:线段
  8. VertexDescription:顶点属性描述

关键要点

  • 理解几何类型的维度和特征
  • 掌握路径和环的操作方法
  • 了解顶点属性的添加和管理
  • 熟悉几何对象的序列化方式

下一章,我们将深入学习空间操作和算子的使用。


← 上一章:快速入门与环境配置 | 下一章:空间操作详解 →

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