第12章 - 扩展开发指南
第12章 - 扩展开发指南
12.1 源码结构分析
12.1.1 包结构
com.esri.core.geometry
├── 核心几何类
│ ├── Geometry.java # 几何抽象基类
│ ├── Point.java # 点
│ ├── MultiPoint.java # 多点
│ ├── Polyline.java # 折线
│ ├── Polygon.java # 多边形
│ ├── Envelope.java # 包围盒
│ ├── Line.java # 线段
│ └── Segment.java # 线段基类
├── 内部实现类
│ ├── MultiVertexGeometryImpl.java # 多顶点实现
│ ├── MultiPathImpl.java # 多路径实现
│ └── VertexDescriptionDesignerImpl.java # 顶点描述
├── 算子类
│ ├── Operator.java # 算子基类
│ ├── OperatorFactory.java # 算子工厂接口
│ ├── OperatorFactoryLocal.java # 本地算子工厂
│ └── Operator*.java # 各种算子
├── 工具类
│ ├── GeometryEngine.java # 便捷 API
│ ├── NumberUtils.java # 数值工具
│ ├── Point2D.java # 2D 坐标
│ ├── Point3D.java # 3D 坐标
│ └── Envelope2D.java # 2D 包围盒
├── 空间索引
│ ├── QuadTree.java # 四叉树
│ ├── QuadTreeImpl.java # 四叉树实现
│ ├── Envelope2DIntersector.java # 包围盒相交器
│ └── IntervalTree.java # 区间树
├── 格式处理
│ ├── OperatorImport*.java # 导入算子
│ ├── OperatorExport*.java # 导出算子
│ ├── JsonReader.java # JSON 读取
│ └── WktParser.java # WKT 解析
└── ogc # OGC 兼容类
├── OGCGeometry.java
└── OGC*.java
12.1.2 核心设计模式
1. 工厂模式 (Factory Pattern)
// 算子工厂
public abstract class OperatorFactory {
public abstract Operator getOperator(Operator.Type type);
public abstract boolean isOperatorSupported(Operator.Type type);
}
// 本地实现
public class OperatorFactoryLocal extends OperatorFactory {
private static final OperatorFactoryLocal INSTANCE = new OperatorFactoryLocal();
private static final HashMap<Type, Operator> st_supportedOperators;
static {
st_supportedOperators.put(Type.Buffer, new OperatorBufferLocal());
// ...
}
public static OperatorFactoryLocal getInstance() {
return INSTANCE;
}
}
2. 迭代器模式 (Iterator Pattern)
// GeometryCursor 作为迭代器
public abstract class GeometryCursor {
public abstract Geometry next();
public abstract int getGeometryID();
}
// 使用示例
GeometryCursor cursor = operator.execute(input, sr, params);
Geometry geom;
while ((geom = cursor.next()) != null) {
// 处理几何
}
3. 模板方法模式 (Template Method)
// Operator 基类定义模板
public abstract class Operator {
public abstract Type getType();
// 可选的加速方法
public boolean accelerateGeometry(...) { return false; }
public boolean canAccelerateGeometry(...) { return false; }
}
// 具体实现
public class OperatorContainsLocal extends OperatorContains {
@Override
public boolean execute(...) {
// 具体实现
}
@Override
public boolean accelerateGeometry(...) {
// 实现加速
}
}
4. 策略模式 (Strategy Pattern)
// 不同的简化策略
public class OperatorSimplify extends Operator { ... }
public class OperatorSimplifyOGC extends Operator { ... }
public class OperatorGeneralize extends Operator { ... }
12.1.3 内存管理
// 几何对象的内存估算
public abstract class Geometry {
public abstract long estimateMemorySize();
}
// 实现示例(Point)
@Override
public long estimateMemorySize() {
return SIZE_OF_POINT + estimateMemorySize(m_attributes);
}
// 尺寸常量定义
public class SizeOf {
public static final int SIZE_OF_POINT = 40;
public static final int SIZE_OF_ENVELOPE = 80;
public static final int SIZE_OF_POLYLINE = 24;
public static final int SIZE_OF_POLYGON = 24;
}
12.2 自定义算子开发
12.2.1 算子开发步骤
- 继承
Operator基类 - 实现抽象方法
- 在
OperatorFactoryLocal中注册 - 提供便捷的静态方法
12.2.2 示例:实现中心线提取算子
/**
* 中心线提取算子
*/
public abstract class OperatorCenterLine extends Operator {
@Override
public Type getType() {
return Type.CenterLine; // 需要在 Type 枚举中添加
}
/**
* 从多边形提取中心线
*/
public abstract Polyline execute(Polygon polygon,
SpatialReference sr, ProgressTracker progress);
/**
* 批量处理
*/
public abstract GeometryCursor execute(GeometryCursor polygons,
SpatialReference sr, ProgressTracker progress);
/**
* 获取本地实例
*/
public static OperatorCenterLine local() {
return (OperatorCenterLine) OperatorFactoryLocal.getInstance()
.getOperator(Type.CenterLine);
}
}
/**
* 中心线提取算子本地实现
*/
public class OperatorCenterLineLocal extends OperatorCenterLine {
@Override
public Polyline execute(Polygon polygon, SpatialReference sr,
ProgressTracker progress) {
if (polygon.isEmpty()) {
return new Polyline();
}
// 简化实现:使用骨架化算法
// 实际实现需要更复杂的算法
// 1. 获取多边形边界
Polyline boundary = (Polyline) polygon.getBoundary();
// 2. 计算 Voronoi 图或使用其他算法
// ... 算法实现 ...
// 3. 返回中心线
return computeCenterLine(polygon, sr);
}
@Override
public GeometryCursor execute(GeometryCursor polygons,
SpatialReference sr, ProgressTracker progress) {
return new CenterLineCursor(polygons, sr, progress);
}
private Polyline computeCenterLine(Polygon polygon, SpatialReference sr) {
// 简化实现:连接质心
Polyline result = new Polyline();
// 获取各环的质心
for (int i = 0; i < polygon.getPathCount(); i++) {
if (polygon.isExteriorRing(i)) {
Point2D centroid = computeRingCentroid(polygon, i);
if (result.isEmpty()) {
result.startPath(centroid.x, centroid.y);
} else {
result.lineTo(centroid.x, centroid.y);
}
}
}
return result;
}
private Point2D computeRingCentroid(Polygon polygon, int ringIndex) {
// 计算环的质心
int start = polygon.getPathStart(ringIndex);
int end = polygon.getPathEnd(ringIndex);
double sumX = 0, sumY = 0;
for (int i = start; i < end; i++) {
Point2D pt = polygon.getXY(i);
sumX += pt.x;
sumY += pt.y;
}
int count = end - start;
return new Point2D(sumX / count, sumY / count);
}
/**
* 中心线游标
*/
private class CenterLineCursor extends GeometryCursor {
private GeometryCursor inputCursor;
private SpatialReference sr;
private ProgressTracker progress;
public CenterLineCursor(GeometryCursor input, SpatialReference sr,
ProgressTracker progress) {
this.inputCursor = input;
this.sr = sr;
this.progress = progress;
}
@Override
public Geometry next() {
Geometry geom = inputCursor.next();
if (geom == null) {
return null;
}
if (!(geom instanceof Polygon)) {
return new Polyline(); // 非多边形返回空
}
return execute((Polygon) geom, sr, progress);
}
@Override
public int getGeometryID() {
return inputCursor.getGeometryID();
}
}
}
12.2.3 注册自定义算子
// 方式一:修改 OperatorFactoryLocal(需要修改源码)
static {
st_supportedOperators.put(Type.CenterLine, new OperatorCenterLineLocal());
}
// 方式二:创建自定义工厂
public class CustomOperatorFactory extends OperatorFactoryLocal {
private static final CustomOperatorFactory INSTANCE = new CustomOperatorFactory();
private final Map<Operator.Type, Operator> customOperators = new HashMap<>();
public CustomOperatorFactory() {
customOperators.put(Type.CenterLine, new OperatorCenterLineLocal());
}
public static CustomOperatorFactory getInstance() {
return INSTANCE;
}
@Override
public Operator getOperator(Operator.Type type) {
if (customOperators.containsKey(type)) {
return customOperators.get(type);
}
return super.getOperator(type);
}
}
12.3 自定义几何类型
12.3.1 扩展 Geometry 类
/**
* 圆形几何类型
*/
public class Circle extends Geometry {
private Point center;
private double radius;
private VertexDescription description;
public Circle() {
this.description = VertexDescriptionDesignerImpl.getDefaultDescriptor2D();
this.center = new Point();
this.radius = 0;
}
public Circle(Point center, double radius) {
this();
this.center = (Point) center.copy();
this.radius = radius;
}
public Circle(double x, double y, double radius) {
this(new Point(x, y), radius);
}
// Getters/Setters
public Point getCenter() { return center; }
public double getRadius() { return radius; }
public void setCenter(Point center) {
this.center = (Point) center.copy();
}
public void setRadius(double radius) {
this.radius = radius;
}
// 实现抽象方法
@Override
public Type getType() {
return Type.Unknown; // 自定义类型
}
@Override
public int getDimension() {
return 2; // 面状
}
@Override
public boolean isEmpty() {
return center.isEmpty() || radius <= 0;
}
@Override
public void setEmpty() {
center.setEmpty();
radius = 0;
}
@Override
public void queryEnvelope(Envelope env) {
if (isEmpty()) {
env.setEmpty();
return;
}
env.setCoords(
center.getX() - radius, center.getY() - radius,
center.getX() + radius, center.getY() + radius);
}
@Override
public void queryEnvelope2D(Envelope2D env) {
if (isEmpty()) {
env.setEmpty();
return;
}
env.setCoords(
center.getX() - radius, center.getY() - radius,
center.getX() + radius, center.getY() + radius);
}
@Override
void queryEnvelope3D(Envelope3D env) {
if (isEmpty()) {
env.setEmpty();
return;
}
double z = center.hasZ() ? center.getZ() : 0;
env.setCoords(
center.getX() - radius, center.getY() - radius, z,
center.getX() + radius, center.getY() + radius, z);
}
@Override
public Envelope1D queryInterval(int semantics, int ordinate) {
Envelope1D env = new Envelope1D();
if (isEmpty()) {
env.setEmpty();
return env;
}
if (semantics == VertexDescription.Semantics.POSITION) {
if (ordinate == 0) {
env.setCoords(center.getX() - radius, center.getX() + radius);
} else {
env.setCoords(center.getY() - radius, center.getY() + radius);
}
} else {
double val = center.getAttributeAsDbl(semantics, ordinate);
env.setCoords(val, val);
}
return env;
}
@Override
public Geometry createInstance() {
return new Circle();
}
@Override
public void copyTo(Geometry dst) {
if (!(dst instanceof Circle)) {
throw new IllegalArgumentException();
}
Circle other = (Circle) dst;
other.center = (Point) center.copy();
other.radius = radius;
other.description = description;
}
@Override
public long estimateMemorySize() {
return 64 + center.estimateMemorySize();
}
@Override
public Geometry getBoundary() {
// 圆的边界是圆周
return toPolygon(64).getBoundary();
}
@Override
public void applyTransformation(Transformation2D transform) {
center.applyTransformation(transform);
// 注意:缩放会影响半径
}
@Override
protected void _assignVertexDescriptionImpl(VertexDescription newDescription) {
center.assignVertexDescription(newDescription);
this.description = newDescription;
}
// 自定义方法
/**
* 转换为多边形(近似)
*/
public Polygon toPolygon(int numPoints) {
Polygon polygon = new Polygon();
if (isEmpty()) {
return polygon;
}
double angleStep = 2 * Math.PI / numPoints;
polygon.startPath(
center.getX() + radius,
center.getY());
for (int i = 1; i < numPoints; i++) {
double angle = i * angleStep;
polygon.lineTo(
center.getX() + radius * Math.cos(angle),
center.getY() + radius * Math.sin(angle));
}
polygon.closePathWithLine();
return polygon;
}
/**
* 计算面积
*/
public double calculateArea() {
return Math.PI * radius * radius;
}
/**
* 计算周长
*/
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}
/**
* 判断点是否在圆内
*/
public boolean contains(Point point) {
double dx = point.getX() - center.getX();
double dy = point.getY() - center.getY();
return dx * dx + dy * dy <= radius * radius;
}
/**
* 判断两圆是否相交
*/
public boolean intersects(Circle other) {
double dx = other.center.getX() - center.getX();
double dy = other.center.getY() - center.getY();
double dist = Math.sqrt(dx * dx + dy * dy);
return dist <= radius + other.radius;
}
}
12.4 格式扩展
12.4.1 自定义格式导入
/**
* 自定义格式导入器(示例:简化的 CSV 格式)
*/
public class OperatorImportFromCSV {
/**
* 从 CSV 导入点几何
* 格式: id,x,y[,z][,m]
*/
public static List<Point> importPoints(String csv) {
List<Point> points = new ArrayList<>();
String[] lines = csv.split("\n");
for (String line : lines) {
if (line.trim().isEmpty() || line.startsWith("#")) {
continue;
}
String[] parts = line.split(",");
if (parts.length < 3) {
continue;
}
try {
double x = Double.parseDouble(parts[1].trim());
double y = Double.parseDouble(parts[2].trim());
Point point;
if (parts.length >= 4) {
double z = Double.parseDouble(parts[3].trim());
point = new Point(x, y, z);
} else {
point = new Point(x, y);
}
points.add(point);
} catch (NumberFormatException e) {
// 跳过格式错误的行
}
}
return points;
}
/**
* 从 CSV 导入折线
* 格式: path_id,point_seq,x,y
*/
public static Polyline importPolyline(String csv) {
Polyline polyline = new Polyline();
String[] lines = csv.split("\n");
int currentPath = -1;
for (String line : lines) {
if (line.trim().isEmpty() || line.startsWith("#")) {
continue;
}
String[] parts = line.split(",");
if (parts.length < 4) {
continue;
}
try {
int pathId = Integer.parseInt(parts[0].trim());
double x = Double.parseDouble(parts[2].trim());
double y = Double.parseDouble(parts[3].trim());
if (pathId != currentPath) {
polyline.startPath(x, y);
currentPath = pathId;
} else {
polyline.lineTo(x, y);
}
} catch (NumberFormatException e) {
// 跳过
}
}
return polyline;
}
}
12.4.2 自定义格式导出
/**
* 自定义格式导出器
*/
public class OperatorExportToCSV {
/**
* 导出点为 CSV
*/
public static String exportPoints(List<Point> points, boolean includeZ) {
StringBuilder sb = new StringBuilder();
sb.append("# id,x,y");
if (includeZ) {
sb.append(",z");
}
sb.append("\n");
for (int i = 0; i < points.size(); i++) {
Point p = points.get(i);
sb.append(i).append(",");
sb.append(p.getX()).append(",");
sb.append(p.getY());
if (includeZ && p.hasZ()) {
sb.append(",").append(p.getZ());
}
sb.append("\n");
}
return sb.toString();
}
/**
* 导出多边形为 CSV(边界点)
*/
public static String exportPolygon(Polygon polygon) {
StringBuilder sb = new StringBuilder();
sb.append("# ring_id,point_seq,x,y\n");
for (int ring = 0; ring < polygon.getPathCount(); ring++) {
int start = polygon.getPathStart(ring);
int end = polygon.getPathEnd(ring);
for (int i = start; i < end; i++) {
Point2D pt = polygon.getXY(i);
sb.append(ring).append(",");
sb.append(i - start).append(",");
sb.append(pt.x).append(",");
sb.append(pt.y).append("\n");
}
}
return sb.toString();
}
}
12.5 性能监控扩展
12.5.1 操作统计
/**
* 几何操作统计器
*/
public class GeometryOperationStats {
private static final GeometryOperationStats INSTANCE = new GeometryOperationStats();
private final Map<String, AtomicLong> operationCounts = new ConcurrentHashMap<>();
private final Map<String, AtomicLong> operationTimes = new ConcurrentHashMap<>();
public static GeometryOperationStats getInstance() {
return INSTANCE;
}
/**
* 记录操作
*/
public void record(String operation, long durationNanos) {
operationCounts.computeIfAbsent(operation, k -> new AtomicLong())
.incrementAndGet();
operationTimes.computeIfAbsent(operation, k -> new AtomicLong())
.addAndGet(durationNanos);
}
/**
* 获取统计报告
*/
public String getReport() {
StringBuilder sb = new StringBuilder();
sb.append("几何操作统计报告\n");
sb.append("================\n");
for (String op : operationCounts.keySet()) {
long count = operationCounts.get(op).get();
long totalTime = operationTimes.get(op).get();
double avgTime = count > 0 ? totalTime / (double) count / 1_000_000 : 0;
sb.append(String.format("%s: 调用 %d 次, 平均 %.2f ms%n",
op, count, avgTime));
}
return sb.toString();
}
/**
* 重置统计
*/
public void reset() {
operationCounts.clear();
operationTimes.clear();
}
}
/**
* 带统计的几何引擎
*/
public class InstrumentedGeometryEngine {
private static final GeometryOperationStats stats =
GeometryOperationStats.getInstance();
public static Polygon buffer(Geometry geometry, SpatialReference sr,
double distance) {
long start = System.nanoTime();
try {
return GeometryEngine.buffer(geometry, sr, distance);
} finally {
stats.record("buffer", System.nanoTime() - start);
}
}
public static boolean contains(Geometry container, Geometry contained,
SpatialReference sr) {
long start = System.nanoTime();
try {
return GeometryEngine.contains(container, contained, sr);
} finally {
stats.record("contains", System.nanoTime() - start);
}
}
// ... 其他方法类似
}
12.6 与其他库集成
12.6.1 与 JTS 互操作
/**
* geometry-api-java 与 JTS 互操作工具
*/
public class JtsInterop {
private static final GeometryFactory jtsFactory = new GeometryFactory();
/**
* ESRI Geometry → JTS Geometry
*/
public static org.locationtech.jts.geom.Geometry toJts(Geometry esriGeom) {
String wkt = GeometryEngine.geometryToWkt(esriGeom, 0);
WKTReader reader = new WKTReader(jtsFactory);
try {
return reader.read(wkt);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* JTS Geometry → ESRI Geometry
*/
public static Geometry fromJts(org.locationtech.jts.geom.Geometry jtsGeom) {
WKTWriter writer = new WKTWriter();
String wkt = writer.write(jtsGeom);
return GeometryEngine.geometryFromWkt(wkt, 0, Geometry.Type.Unknown);
}
/**
* 使用 JTS 进行复杂操作
*/
public static Geometry jtsOperation(Geometry esriGeom,
Function<org.locationtech.jts.geom.Geometry,
org.locationtech.jts.geom.Geometry> operation) {
org.locationtech.jts.geom.Geometry jtsGeom = toJts(esriGeom);
org.locationtech.jts.geom.Geometry result = operation.apply(jtsGeom);
return fromJts(result);
}
}
// 使用示例
Polygon esriPolygon = createPolygon();
// 使用 JTS 计算 Voronoi 图
Geometry voronoi = JtsInterop.jtsOperation(esriPolygon, jtsGeom -> {
VoronoiDiagramBuilder builder = new VoronoiDiagramBuilder();
builder.setSites(jtsGeom);
return builder.getDiagram(jtsFactory);
});
12.6.2 与 GeoTools 集成
/**
* GeoTools 集成工具
*/
public class GeoToolsIntegration {
/**
* 从 Shapefile 读取几何
*/
public static List<Geometry> readShapefile(File shpFile) throws Exception {
List<Geometry> geometries = new ArrayList<>();
FileDataStore store = FileDataStoreFinder.getDataStore(shpFile);
SimpleFeatureCollection collection = store.getFeatureSource().getFeatures();
try (SimpleFeatureIterator iterator = collection.features()) {
while (iterator.hasNext()) {
SimpleFeature feature = iterator.next();
org.locationtech.jts.geom.Geometry jtsGeom =
(org.locationtech.jts.geom.Geometry) feature.getDefaultGeometry();
Geometry esriGeom = JtsInterop.fromJts(jtsGeom);
geometries.add(esriGeom);
}
}
store.dispose();
return geometries;
}
/**
* 写入 Shapefile
*/
public static void writeShapefile(File shpFile, List<Geometry> geometries,
int srid) throws Exception {
// GeoTools Shapefile 写入实现
// ...
}
}
12.7 本章小结
本章介绍了 geometry-api-java 的扩展开发:
- 源码结构:包组织和核心设计模式
- 自定义算子:开发和注册新的空间操作
- 自定义几何:扩展几何类型(如 Circle)
- 格式扩展:自定义导入/导出格式
- 性能监控:操作统计和性能分析
- 库集成:与 JTS、GeoTools 互操作
扩展开发要点:
- 遵循现有的设计模式
- 保持 API 风格一致性
- 注意线程安全和内存管理
- 提供完整的单元测试

浙公网安备 33010602011771号