前言
在 GIS 开发中,常常会用到求几何对象的外包矩形,求中心点,求质心等等,今天我让AI写了一些求质心的方法,可以看看,当作参考,最终实际使用个人的建议是:如果只是求质心,并且要使其在内部,那么用geometry.getInteriorPoint()就可以了
接下来一起看看AI分析了哪些东西(二,三,四,六,七里的方法是AI给的):
用到的Maven依赖
java
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.19.0</version>
</dependency>
一、JTS 原生质心(基准方案)
java
public static Point jtsCentroid(Geometry geometry) {
if (geometry == null || geometry.isEmpty()) {
return null;
}
return geometry.getCentroid();
}
特点:
- ✅ 简单、稳定、性能好
- ✅ 对任意 Geometry 类型都可用
- ❌ 对 MultiPolygon / 碎面不友好
- ❌ 凹多边形可能落在外部
适合场景: 单 Polygon、数据预览、快速计算
其实个人认为这个方法基本能覆盖大部分情况了,如果图省事的话其实是能直接用的,或者用我前言里说的geometry.getInteriorPoint()
二、MultiPolygon:按面积加权质心
当 MultiPolygon 中存在多个面时,更合理的做法是按面积加权平均:
Cx = ∑(Ai × xi) / ∑Ai Cy = ∑(Ai × yi) / ∑Ai
java
public static Point weightedCentroidByArea(MultiPolygon mp) {
double totalArea = 0.0, cx = 0.0, cy = 0.0;
for (int i = 0; i < mp.getNumGeometries(); i++) {
Polygon p = (Polygon) mp.getGeometryN(i);
double area = p.getArea();
Point c = p.getCentroid();
cx += c.getX() * area;
cy += c.getY() * area;
totalArea += area;
}
return geometryFactory.createPoint(
new Coordinate(cx / totalArea, cy / totalArea));
}
- ✅ 优点:比原生质心更符合"整体重心"直觉
- ❌ 缺点:仍会被极小碎面轻微影响
三、忽略极小面的加权质心
在很多业务数据中,MultiPolygon 常伴随大量噪声碎面(如总面积 1% 以内的微面),对质心计算引入了不必要的干扰。
java
public static Point weightedCentroidIgnoreSmall(
MultiPolygon mp, double minAreaRatio) {
double totalArea = mp.getArea();
double usedArea = 0.0, cx = 0.0, cy = 0.0;
for (int i = 0; i < mp.getNumGeometries(); i++) {
Polygon p = (Polygon) mp.getGeometryN(i);
double area = p.getArea();
if (area / totalArea < minAreaRatio) continue;
Point c = p.getCentroid();
cx += c.getX() * area;
cy += c.getY() * area;
usedArea += area;
}
return geometryFactory.createPoint(
new Coordinate(cx / usedArea, cy / usedArea));
}
经验值: minAreaRatio = 0.01(忽略小于总面积 1% 的面)
工程实践中最常用、最稳健的方案
四、仅取最大面的质心(极端简化)
java
public static Point largestPolygonCentroid(MultiPolygon mp) {
Polygon largest = null;
double maxArea = 0.0;
for (int i = 0; i < mp.getNumGeometries(); i++) {
Polygon p = (Polygon) mp.getGeometryN(i);
double area = p.getArea();
if (area > maxArea) {
maxArea = area;
largest = p;
}
}
return largest == null ? null : largest.getCentroid();
}
- ✅ 优点:极度稳定,不受碎面干扰
- ❌ 缺点:完全忽略次要面
适合场景: 行政区划、地块主区域标注
五、凹多边形:质心 vs 内部点
凹多边形的质心可能落在图形外部,这在做点标注时是个严重问题。JTS 提供了另一个方法:
java
public static Point interiorPoint(Geometry geometry) {
return geometry.getInteriorPoint();
}
对比:
| 方法 | 是否一定在内 | 稳定性 |
|---|---|---|
getCentroid() |
❌ | 高 |
getInteriorPoint() |
✅ | 中 |
💡 标注点 / 名称显示优先用
interiorPoint
六、MultiLineString:按长度加权质心
线要素没有"面积",应使用长度加权:
java
public static Point weightedCentroidByLength(MultiLineString mls) {
double totalLen = 0.0, cx = 0.0, cy = 0.0;
for (int i = 0; i < mls.getNumGeometries(); i++) {
LineString ls = (LineString) mls.getGeometryN(i);
double len = ls.getLength();
Point c = ls.getCentroid();
cx += c.getX() * len;
cy += c.getY() * len;
totalLen += len;
}
return geometryFactory.createPoint(
new Coordinate(cx / totalLen, cy / totalLen));
}
适合场景: 道路网、管线、河流中心线
七、自定义权重质心(通用解法)
当不同几何对象本身就有业务权重(如人口、产值):
java
public static Point customWeightedCentroid(
Geometry[] geometries, double[] weights) {
double wx = 0.0, wy = 0.0, totalWeight = 0.0;
for (int i = 0; i < geometries.length; i++) {
Point c = geometries[i].getCentroid();
double w = weights[i];
wx += c.getX() * w;
wy += c.getY() * w;
totalWeight += w;
}
return geometryFactory.createPoint(
new Coordinate(wx / totalWeight, wy / totalWeight));
}
这是最通用的质心抽象模型 ✅
八、外包矩形
java
// 外包矩形
public static Geometry boundingBox(Geometry geometry) {
Envelope env = geometry.getEnvelopeInternal();
return geometryFactory.toGeometry(env);
}
这个没什么好说,比较常规了
总结
| 场景 | 推荐方法 |
|---|---|
| 单 Polygon | getCentroid() |
| MultiPolygon(通用) | weightedCentroidIgnoreSmall() ✅ |
| 强抗噪需求 | largestPolygonCentroid() |
| 凹多边形标注 | getInteriorPoint() |
| 线要素 | weightedCentroidByLength() |
| 业务加权 | customWeightedCentroid() |
| 使结果在内部一般使用 | getInteriorPoint() |
有试了下AI给的几个方法,个人觉得都一般,不如jts的getCentroid和getInteriorPoint,实际生产使用,建议还是用这两个,除非说计算和实际业务有关联,这种情况就要自己写方法了。