第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 中的各种几何对象:
- 类层次结构:Geometry → Point/MultiPoint/Polyline/Polygon/Envelope
- Point:单点,支持 Z/M/ID 属性
- MultiPoint:点集合,支持批量操作
- Polyline:由多条路径组成的折线
- Polygon:由外环和内环组成的面
- Envelope:矩形包围盒
- Line:线段
- VertexDescription:顶点属性描述
关键要点:
- 理解几何类型的维度和特征
- 掌握路径和环的操作方法
- 了解顶点属性的添加和管理
- 熟悉几何对象的序列化方式
下一章,我们将深入学习空间操作和算子的使用。

浙公网安备 33010602011771号