2012-JTS-Performance-Improvements
JTS 性能改进 - 空间索引优化
原文:Performance Improvements in JTS
作者:Martin Davis
日期:2012年4月
概述
2012 年的 JTS 版本包含了一系列重要的性能改进,特别是针对空间索引结构的优化。这些改进通过优化矩形相交测试,在某些情况下实现了高达 100 倍的查询速度提升。
主要优化领域
1. RectangleIntersects 优化
这是本次性能改进的核心。矩形相交测试是空间索引中最频繁调用的操作之一。
旧方法
原有的实现使用 Envelope#intersects 方法:
// 旧的实现(有中间开销)
public boolean intersects(Envelope other) {
if (isNull() || other.isNull()) {
return false;
}
return !(other.minX > maxX ||
other.maxX < minX ||
other.minY > maxY ||
other.maxY < minY);
}
新方法
优化后使用无状态的直接算术检查:
// 优化后的实现(内联算术检查)
public static boolean intersects(
double minX1, double maxX1, double minY1, double maxY1,
double minX2, double maxX2, double minY2, double maxY2) {
return minX1 <= maxX2 && maxX1 >= minX2 &&
minY1 <= maxY2 && maxY1 >= minY2;
}
为什么更快?
- 避免对象创建:不需要创建中间 Envelope 对象
- 直接数值比较:使用原始类型的直接比较
- 方法内联:简短的方法更容易被 JIT 编译器内联
- 减少方法调用开销:静态方法调用更轻量
2. AbstractSTRtree 改进
AbstractSTRtree 是 JTS 中 STRtree(Sort-Tile-Recursive 树)和其他空间索引的基类。
STRtree 遍历优化
// 优化前
private void query(Object searchBounds, AbstractNode node, ItemVisitor visitor) {
for (Boundable childBoundable : node.getChildBoundables()) {
if (!getIntersectsOp().intersects(
childBoundable.getBounds(), searchBounds)) {
continue;
}
// 处理子节点...
}
}
// 优化后
private void query(Envelope searchBounds, AbstractNode node, ItemVisitor visitor) {
double minX = searchBounds.getMinX();
double maxX = searchBounds.getMaxX();
double minY = searchBounds.getMinY();
double maxY = searchBounds.getMaxY();
for (Boundable childBoundable : node.getChildBoundables()) {
Envelope childEnv = (Envelope) childBoundable.getBounds();
// 使用内联的相交检查
if (minX > childEnv.getMaxX() || maxX < childEnv.getMinX() ||
minY > childEnv.getMaxY() || maxY < childEnv.getMinY()) {
continue;
}
// 处理子节点...
}
}
3. RandomPointsInGridBuilder 优化
用于生成随机点的构建器也得到了优化:
import org.locationtech.jts.shape.random.RandomPointsInGridBuilder;
import org.locationtech.jts.geom.*;
public class RandomPointsExample {
public static void main(String[] args) {
GeometryFactory factory = new GeometryFactory();
// 创建随机点生成器
RandomPointsInGridBuilder builder = new RandomPointsInGridBuilder(factory);
// 设置边界
Envelope extent = new Envelope(0, 100, 0, 100);
builder.setExtent(extent);
// 设置点数量
builder.setNumPoints(10000);
// 生成随机点
Geometry points = builder.getGeometry();
}
}
性能基准测试
测试场景
对包含 100,000 个几何图形的 STRtree 进行 10,000 次查询:
public class PerformanceBenchmark {
public static void main(String[] args) {
// 构建索引
STRtree tree = new STRtree();
for (int i = 0; i < 100000; i++) {
Envelope env = createRandomEnvelope();
tree.insert(env, Integer.valueOf(i));
}
tree.build();
// 执行查询基准测试
long startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
Envelope searchEnv = createRandomSearchEnvelope();
tree.query(searchEnv);
}
long endTime = System.nanoTime();
System.out.printf("查询耗时: %.2f ms%n", (endTime - startTime) / 1e6);
}
}
测试结果
| 测试项 | 优化前 | 优化后 | 提升倍数 |
|---|---|---|---|
| 小范围查询 | 1000 ms | 10 ms | ~100x |
| 大范围查询 | 500 ms | 50 ms | ~10x |
| 全范围查询 | 200 ms | 100 ms | ~2x |
代码示例
优化的边界框检查
/**
* 优化的边界框相交检查工具类
*/
public class EnvelopeUtil {
/**
* 检查两个边界框是否相交(优化版本)
*/
public static boolean intersects(Envelope a, Envelope b) {
return a.getMinX() <= b.getMaxX() &&
a.getMaxX() >= b.getMinX() &&
a.getMinY() <= b.getMaxY() &&
a.getMaxY() >= b.getMinY();
}
/**
* 检查点是否在边界框内(优化版本)
*/
public static boolean contains(Envelope env, double x, double y) {
return x >= env.getMinX() && x <= env.getMaxX() &&
y >= env.getMinY() && y <= env.getMaxY();
}
/**
* 计算两个边界框的交集(仅当相交时)
*/
public static Envelope intersection(Envelope a, Envelope b) {
if (!intersects(a, b)) {
return null;
}
return new Envelope(
Math.max(a.getMinX(), b.getMinX()),
Math.min(a.getMaxX(), b.getMaxX()),
Math.max(a.getMinY(), b.getMinY()),
Math.min(a.getMaxY(), b.getMaxY())
);
}
}
使用优化的空间查询
import org.locationtech.jts.index.strtree.STRtree;
import org.locationtech.jts.geom.*;
public class OptimizedSpatialQuery {
private STRtree index;
private GeometryFactory factory;
public OptimizedSpatialQuery() {
this.index = new STRtree();
this.factory = new GeometryFactory();
}
public void buildIndex(List<Geometry> geometries) {
for (Geometry geom : geometries) {
index.insert(geom.getEnvelopeInternal(), geom);
}
// 构建索引(使索引不可变并优化结构)
index.build();
}
public List<Geometry> query(Envelope searchEnv) {
@SuppressWarnings("unchecked")
List<Geometry> candidates = index.query(searchEnv);
// 精确过滤(候选几何图形可能只是边界框相交)
List<Geometry> result = new ArrayList<>();
Geometry searchBox = factory.toGeometry(searchEnv);
for (Geometry candidate : candidates) {
if (candidate.intersects(searchBox)) {
result.add(candidate);
}
}
return result;
}
}
优化原则总结
1. 减少对象分配
// 避免
Coordinate c = new Coordinate(x, y);
return envelope.contains(c);
// 推荐
return envelope.contains(x, y);
2. 使用原始类型
// 避免
Double minX = envelope.getMinX();
// 推荐
double minX = envelope.getMinX();
3. 方法内联友好
// JIT 更容易内联的简短方法
public static boolean intersects(double ax1, double ax2, double bx1, double bx2) {
return ax1 <= bx2 && ax2 >= bx1;
}
4. 缓存计算结果
// 在循环外缓存不变的值
double searchMinX = searchEnv.getMinX();
double searchMaxX = searchEnv.getMaxX();
// 然后在循环内使用缓存值
影响和应用
这些优化已经被应用到:
- JTS 1.12+ 版本
- GEOS (C++ 移植版本)
- NetTopologySuite (.NET 移植版本)
- PostGIS (通过 GEOS)
- GeoTools (使用 JTS)
总结
JTS 2012 年的性能改进展示了低级别优化的强大影响。通过优化最频繁调用的操作——矩形相交测试,空间索引查询性能得到了数量级的提升。这些改进现在已成为 JTS 及其派生库的标准组成部分。

浙公网安备 33010602011771号