空間關係分析
概述
空間關係分析是GIS的核心功能之一。通過判斷幾何對象之間的空間關係,可以實現空間查詢、疊加分析、選址分析等常見GIS功能。
基本空間關係
相交(Intersects)
兩個幾何對象有任意公共部分(點、線或面)時,稱為相交:
/**
* 判斷幾何是否相交
*/
public static boolean intersects(Geometry a, Geometry b) {
return a.intersects(b);
}
應用場景:
- 空間查詢:找出與指定區域相交的所有要素
- 碰撞檢測:判斷兩個對象是否發生空間重疊
- 路線規劃:判斷路線是否經過某個區域
相離(Disjoint)
兩個幾何對象沒有任何公共點時,稱為相離:
/**
* 判斷幾何是否相離
* 一個公共點都沒有
*/
public static boolean disjoint(Geometry a, Geometry b) {
return a.disjoint(b);
}
説明:disjoint與intersects互為反義關係,即:
disjoint(a, b) == !intersects(a, b)
接觸(Touches)
兩個幾何對象有公共點但沒有公共內部區域時,稱為接觸:
/**
* 判斷幾何是否接觸
* 有公共點但沒有公共區域
*/
public static boolean touches(Geometry a, Geometry b) {
return a.touches(b);
}
典型情況:
- 兩個相鄰的地塊共享邊界
- 道路與地塊的邊界相接
- 點落在線或面的邊界上
交叉(Crosses)
兩個幾何對象的內部有公共部分,但既不相等也不包含,稱為交叉:
/**
* 判斷幾何是否交叉
* 有公共區域但不是包含關係
*/
public static boolean crosses(Geometry a, Geometry b) {
return a.crosses(b);
}
典型情況:
- 道路穿過區域(線與面交叉)
- 兩條道路相交(線與線交叉)
包含(Contains)與被包含(Within)
包含關係描述一個幾何完全位於另一個幾何內部:
/**
* 判斷幾何A是否包含幾何B
*/
public static boolean contains(Geometry a, Geometry b) {
return a.contains(b);
}
/**
* 判斷幾何A是否在幾何B內部
*/
public static boolean within(Geometry a, Geometry b) {
return a.within(b);
}
關係説明:
contains(a, b) == within(b, a)
應用場景:
- 判斷點是否在某個區域內
- 判斷某個地塊是否完全位於行政區劃內
- 空間過濾:只保留完全落入指定範圍的要素
重疊(Overlaps)
兩個同維度的幾何對象有公共區域,但又不完全相同,稱為重疊:
/**
* 判斷幾何是否重疊
* 有公共區域且包含關係不成立
*/
public static boolean overlaps(Geometry a, Geometry b) {
return a.overlaps(b);
}
説明:重疊只發生在同維度幾何之間(面與面、線與線)。
距離計算
最短距離
計算兩個幾何對象之間的最短距離:
/**
* 計算幾何之間的距離
* 不論點線面都是幾何間最短距離
*/
public static double distance(Geometry a, Geometry b) {
return a.distance(b);
}
注意:返回值的單位與座標系單位一致。地理座標系返回的是度,投影座標系返回的是米。
距離判斷
快速判斷兩個幾何是否在指定距離內:
/**
* 判斷幾何間最短距離是否小於給定距離
*/
public static boolean isWithinDistance(Geometry a, Geometry b, double distance) {
return a.isWithinDistance(b, distance);
}
應用場景:
- 近鄰搜索:查找指定距離內的設施
- 緩衝區分析:判斷要素是否在某要素的影響範圍內
- 預警系統:判斷兩個移動目標是否過於接近
DE-9IM模型
DE-9IM(Dimensionally Extended 9-Intersection Model)是空間關係的標準描述模型,提供了更精確的空間關係判斷。
關係矩陣
Interior Boundary Exterior
Interior II IB IE
Boundary BI BB BE
Exterior EI EB EE
每個位置的值表示兩個幾何對應部分的交集維度:
T:有交集(任意維度)F:無交集0:交集為點1:交集為線2:交集為面*:任意(不關心)
獲取關係矩陣
/**
* 獲取幾何關係矩陣
*/
public static String relate(Geometry a, Geometry b) {
return a.relate(b).toString();
}
模式匹配
使用模式字符串判斷特定空間關係:
/**
* 判斷幾何是否符合給定關係
* @param pattern 關係模式,如"T*T***FF*"
*/
public static boolean relatePattern(Geometry a, Geometry b, String pattern) {
return a.relate(b, pattern);
}
常用模式:
|
關係
|
模式
|
|
內部相交
|
|
|
邊界相交
|
|
|
A完全包含B
|
|
|
相鄰(共享邊界)
|
|
幾何相等判斷
精確相等
判斷兩個幾何是否結構完全相同:
/**
* 判斷幾何是否對象結構相等
* 必須有相同的節點和相同的節點順序
*/
public static boolean equalsExact(Geometry a, Geometry b) {
return a.equalsExact(b);
}
/**
* 帶容差的結構相等判斷
*/
public static boolean equalsExactTolerance(Geometry a, Geometry b, double tolerance) {
return a.equalsExact(b, tolerance);
}
規範化相等
判斷兩個幾何在規範化後是否相同:
/**
* 判斷幾何是否對象結構相等(不判斷節點順序)
*/
public static boolean equalsNorm(Geometry a, Geometry b) {
return a.equalsNorm(b);
}
拓撲相等
判斷兩個幾何是否表示相同的點集:
/**
* 判斷幾何是否拓撲相等
*/
public static boolean equalsTopo(Geometry a, Geometry b) {
return a.equalsTopo(b);
}
區別説明:
equalsExact:節點順序、數量都必須完全相同equalsNorm:規範化後比較,忽略節點順序equalsTopo:只比較幾何表示的空間是否相同
ESRI幾何引擎
除了JTS,還可以使用ESRI幾何引擎進行空間關係分析,其API基於WKT字符串:
空間關係判斷
/**
* 幾何是否相交
*/
public static boolean intersects(String awkt, String bwkt, Integer wkid) {
Geometry a = EsriUtil.createGeometryByWkt(awkt);
Geometry b = EsriUtil.createGeometryByWkt(bwkt);
SpatialReference sr = SpatialReference.create(wkid);
return OperatorIntersects.local().execute(a, b, sr, null);
}
/**
* 判斷幾何是否相離
*/
public static boolean disjoint(String awkt, String bwkt, Integer wkid) {
Geometry a = EsriUtil.createGeometryByWkt(awkt);
Geometry b = EsriUtil.createGeometryByWkt(bwkt);
SpatialReference sr = SpatialReference.create(wkid);
return OperatorDisjoint.local().execute(a, b, sr, null);
}
/**
* 判斷幾何是否接觸
*/
public static boolean touches(String awkt, String bwkt, Integer wkid) {
Geometry a = EsriUtil.createGeometryByWkt(awkt);
Geometry b = EsriUtil.createGeometryByWkt(bwkt);
SpatialReference sr = SpatialReference.create(wkid);
return OperatorTouches.local().execute(a, b, sr, null);
}
/**
* 判斷幾何是否包含
*/
public static boolean contains(String awkt, String bwkt, Integer wkid) {
Geometry a = EsriUtil.createGeometryByWkt(awkt);
Geometry b = EsriUtil.createGeometryByWkt(bwkt);
SpatialReference sr = SpatialReference.create(wkid);
return OperatorContains.local().execute(a, b, sr, null);
}
選擇建議:
- JTS:內存中的幾何對象操作,性能更好
- ESRI:基於WKT字符串操作,接口更簡潔
實踐案例
案例1:空間查詢
查找與指定區域相交的所有要素:
/**
* 空間查詢:找出與指定區域相交的要素
*/
public List<WktFeature> spatialQuery(WktLayer layer, String queryWkt) {
List<WktFeature> result = new ArrayList<>();
Geometry queryGeom = GeometryConverter.wkt2Geometry(queryWkt);
for (WktFeature feature : layer.getFeatures()) {
Geometry featureGeom = GeometryConverter.wkt2Geometry(feature.getWkt());
if (featureGeom.intersects(queryGeom)) {
result.add(feature);
}
}
return result;
}
案例2:點在面內判斷
判斷一個點是否位於某個多邊形內部:
/**
* 判斷點是否在面內
*/
public boolean isPointInPolygon(double x, double y, String polygonWkt) {
GeometryFactory factory = new GeometryFactory();
Point point = factory.createPoint(new Coordinate(x, y));
Geometry polygon = GeometryConverter.wkt2Geometry(polygonWkt);
// 使用contains判斷(嚴格在內部)
// 或使用intersects判斷(含邊界上的點)
return polygon.contains(point);
}
案例3:相鄰要素查找
查找與指定要素相鄰(共享邊界)的所有要素:
/**
* 查找相鄰要素
*/
public List<WktFeature> findAdjacentFeatures(WktLayer layer, WktFeature target) {
List<WktFeature> result = new ArrayList<>();
Geometry targetGeom = GeometryConverter.wkt2Geometry(target.getWkt());
for (WktFeature feature : layer.getFeatures()) {
if (feature.getWfId().equals(target.getWfId())) {
continue; // 跳過自己
}
Geometry featureGeom = GeometryConverter.wkt2Geometry(feature.getWkt());
// 相鄰:接觸但不重疊
if (featureGeom.touches(targetGeom)) {
result.add(feature);
}
}
return result;
}
案例4:緩衝區範圍查詢
查找指定位置周邊一定距離內的設施:
/**
* 緩衝區範圍查詢
* @param centerWkt 中心點WKT
* @param distance 查詢距離(米)
* @param layer 要素圖層
*/
public List<WktFeature> bufferQuery(String centerWkt, double distance, WktLayer layer) {
List<WktFeature> result = new ArrayList<>();
// 轉換到投影座標系計算緩衝區
int projWkid = CrsUtil.getProjectedWkid(CrsUtil.getDh(centerWkt));
String projWkt = CrsUtil.transform(centerWkt, 4490, projWkid);
// 創建緩衝區
Geometry center = GeometryConverter.wkt2Geometry(projWkt);
Geometry buffer = center.buffer(distance);
// 將緩衝區轉回地理座標系進行查詢
buffer = CrsUtil.transform(buffer, projWkid, layer.getWkid());
// 查詢相交要素
for (WktFeature feature : layer.getFeatures()) {
Geometry featureGeom = GeometryConverter.wkt2Geometry(feature.getWkt());
if (featureGeom.intersects(buffer)) {
result.add(feature);
}
}
return result;
}
案例5:空間關係矩陣分析
使用DE-9IM進行精確的空間關係分析:
/**
* 分析兩個地塊的空間關係
*/
public String analyzeRelation(String wkt1, String wkt2) {
Geometry geom1 = GeometryConverter.wkt2Geometry(wkt1);
Geometry geom2 = GeometryConverter.wkt2Geometry(wkt2);
// 獲取關係矩陣
String matrix = relate(geom1, geom2);
// 解析關係類型
if (geom1.equals(geom2)) {
return "相等";
} else if (geom1.contains(geom2)) {
return "A包含B";
} else if (geom1.within(geom2)) {
return "A在B內";
} else if (geom1.overlaps(geom2)) {
return "部分重疊";
} else if (geom1.touches(geom2)) {
return "相鄰(共享邊界)";
} else if (geom1.crosses(geom2)) {
return "穿越";
} else if (geom1.intersects(geom2)) {
return "相交";
} else {
return "相離";
}
}
性能優化
空間索引
對於大數據量的空間查詢,使用空間索引可以顯著提升性能:
// 構建R樹索引
STRtree index = new STRtree();
for (WktFeature feature : layer.getFeatures()) {
Geometry geom = GeometryConverter.wkt2Geometry(feature.getWkt());
index.insert(geom.getEnvelopeInternal(), feature);
}
index.build();
// 使用索引查詢
Geometry queryGeom = GeometryConverter.wkt2Geometry(queryWkt);
List<?> candidates = index.query(queryGeom.getEnvelopeInternal());
// 對候選要素進行精確判斷
for (Object obj : candidates) {
WktFeature feature = (WktFeature) obj;
Geometry featureGeom = GeometryConverter.wkt2Geometry(feature.getWkt());
if (featureGeom.intersects(queryGeom)) {
// 真正相交的要素
}
}
外包矩形預篩選
在進行精確空間關係判斷前,先用外包矩形快速過濾:
// 先判斷外包矩形是否相交
if (!geomA.getEnvelopeInternal().intersects(geomB.getEnvelopeInternal())) {
return false; // 外包矩形不相交,幾何必定不相交
}
// 再進行精確判斷
return geomA.intersects(geomB);
小結
本章介紹了空間關係分析的核心內容:
- 基本空間關係:相交、相離、接觸、交叉、包含、重疊
- 距離計算:最短距離計算和距離判斷
- DE-9IM模型:精確的空間關係描述和模式匹配
- 幾何相等:精確相等、規範化相等、拓撲相等
- 性能優化:空間索引和外包矩形預篩選