02-几何数据基础

几何数据基础

概述

几何数据是GIS的核心。所有的空间分析都建立在几何对象之上。本章介绍几何数据的基本概念、创建方法和常用操作。

几何对象模型

坐标点(Coordinate)

坐标点是几何数据的最小单元,由X、Y坐标组成(三维数据还包含Z坐标):

// 创建坐标点
Coordinate coord = new Coordinate(116.397, 39.908);

// 带高程的坐标点
Coordinate coord3D = new Coordinate(116.397, 39.908, 100);

几何工厂(GeometryFactory)

几何工厂用于创建各种几何对象:

// 使用默认精度模型
GeometryFactory factory = new GeometryFactory();

// 使用指定精度模型(浮点精度)
GeometryFactory factory = new GeometryFactory(
    new PrecisionModel(PrecisionModel.FLOATING)
);

基本几何类型

点(Point)

点是最简单的几何类型,表示单个位置:

// 从坐标创建点
Point point = factory.createPoint(new Coordinate(116.397, 39.908));

// 获取坐标值
double x = point.getX();
double y = point.getY();

应用场景:POI(兴趣点)、设备位置、采样点等。

线(LineString)

线由一系列有序的点组成:

// 创建线
Coordinate[] coords = new Coordinate[] {
    new Coordinate(0, 0),
    new Coordinate(10, 10),
    new Coordinate(20, 0)
};
LineString line = factory.createLineString(coords);

// 线的基本属性
double length = line.getLength();        // 长度
boolean closed = line.isClosed();        // 是否闭合
Point start = line.getStartPoint();      // 起点
Point end = line.getEndPoint();          // 终点
int numPoints = line.getNumPoints();     // 节点数

应用场景:道路、河流、管线等。

环(LinearRing)

环是闭合的线,首尾点坐标相同:

// 创建环(首尾点必须相同)
Coordinate[] ringCoords = new Coordinate[] {
    new Coordinate(0, 0),
    new Coordinate(10, 0),
    new Coordinate(10, 10),
    new Coordinate(0, 10),
    new Coordinate(0, 0)  // 与起点相同
};
LinearRing ring = factory.createLinearRing(ringCoords);

// 判断是否为环
boolean isRing = ring.isRing();

面(Polygon)

面由外环和可选的内环(岛洞)组成:

// 外环
LinearRing shell = factory.createLinearRing(outerCoords);

// 内环(岛洞)
LinearRing hole = factory.createLinearRing(innerCoords);

// 创建带洞的多边形
Polygon polygon = factory.createPolygon(shell, new LinearRing[]{hole});

// 面的基本属性
double area = polygon.getArea();                    // 面积
Geometry exterior = polygon.getExteriorRing();      // 外环
int numHoles = polygon.getNumInteriorRing();        // 内环数量
Geometry interior = polygon.getInteriorRingN(0);    // 第一个内环

应用场景:地块、行政区划、建筑物轮廓等。

多点(MultiPoint)

多个点的集合:

Point[] points = new Point[] {
    factory.createPoint(new Coordinate(0, 0)),
    factory.createPoint(new Coordinate(10, 10))
};
MultiPoint multiPoint = factory.createMultiPoint(points);

多线(MultiLineString)

多条线的集合:

LineString[] lines = new LineString[] {line1, line2, line3};
MultiLineString multiLine = factory.createMultiLineString(lines);

多面(MultiPolygon)

多个面的集合:

Polygon[] polygons = new Polygon[] {polygon1, polygon2};
MultiPolygon multiPolygon = factory.createMultiPolygon(polygons);

WKT格式

WKT(Well-Known Text)是几何对象的标准文本表示格式:

// WKT示例
String pointWkt = "POINT (116.397 39.908)";
String lineWkt = "LINESTRING (0 0, 10 10, 20 0)";
String polygonWkt = "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))";
String multiPolygonWkt = "MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))";

WKT与几何对象互转

// WKT转Geometry
public static Geometry wkt2Geometry(String wkt) {
    WKTReader2 reader = new WKTReader2();
    return reader.read(wkt);
}

// Geometry转WKT
public static String geometry2Wkt(Geometry geometry) {
    WKTWriter2 writer = new WKTWriter2();
    return writer.write(geometry);
}

实践要点

  • WKT是调试和日志记录的首选格式
  • 数据交换时使用WKT可避免二进制格式的兼容性问题
  • 大数据量场景下,WKB(二进制格式)效率更高

几何类型判断

判断几何类型是数据处理的常见需求:

/**
 * 获取几何类型
 */
public static GeometryType geometryType(Geometry geom) {
    return GeometryType.valueOfByTypeName(geom.getGeometryType());
}

// 使用方式
Geometry geom = wkt2Geometry(wkt);
GeometryType type = geometryType(geom);
switch (type) {
    case POINT:
        // 点处理逻辑
        break;
    case POLYGON:
    case MULTIPOLYGON:
        // 面处理逻辑
        break;
    // ...
}

基本属性获取

判断几何是否为空

/**
 * 判断几何是否为空
 */
public static boolean isEmpty(Geometry geom) {
    return geom.isEmpty();
}

获取几何维度

/**
 * 获取几何维度
 * 点为0,线为1,面为2
 */
public static int dimension(Geometry geom) {
    return geom.getDimension();
}

获取节点数量

/**
 * 获取节点个数
 */
public static int numPoints(Geometry geom) {
    return geom.getNumPoints();
}

获取长度和面积

/**
 * 获取几何长度
 * 注意:结果单位与坐标系单位一致
 */
public static double length(Geometry geom) {
    return geom.getLength();
}

/**
 * 获取几何面积
 * 注意:结果单位与坐标系单位一致
 */
public static double area(Geometry geom) {
    return geom.getArea();
}

重要提醒:长度和面积的计算结果与坐标系相关:

  • 地理坐标系(经纬度):单位为度,计算结果没有实际意义
  • 投影坐标系:单位为米,计算结果为实际的米/平方米

中心点计算

几何中心(质心)

/**
 * 获取几何中心点(质心)
 * 可能位于几何外部
 */
public static Geometry centroid(Geometry geom) {
    return geom.getCentroid();
}

内部中心点

/**
 * 获取几何内部中心点
 * 保证位于几何内部
 */
public static Geometry interiorPoint(Geometry geom) {
    return geom.getInteriorPoint();
}

选择建议

  • 标注点位时使用interiorPoint,确保标注在要素内部
  • 统计分析时使用centroid,获取几何质心

边界与外包矩形

获取边界

/**
 * 获取几何边界
 * 面返回边界线,线返回端点
 */
public static Geometry boundary(Geometry geom) {
    return geom.getBoundary();
}

获取外包矩形

/**
 * 获取几何外包矩形
 * 返回包围几何的最小矩形
 */
public static Geometry envelope(Geometry geom) {
    return geom.getEnvelope();
}

应用场景

  • 快速空间筛选(先用外包矩形过滤,再精确判断)
  • 确定地图显示范围
  • 空间索引构建

几何有效性检查

拓扑有效性

几何对象必须符合特定的拓扑规则才被认为是有效的:

/**
 * 判断几何拓扑是否合法
 */
public static IsValidModel isValid(Geometry geom) {
    IsValidOp isValidOp = new IsValidOp(geom);
    if (!isValidOp.isValid()) {
        TopologyValidationErrorType type = TopologyValidationErrorType
            .getByErrorType(isValidOp.getValidationError().getErrorType());
        String msg = type != null ? type.getDesc() : "未知拓扑错误";
        return new IsValidModel(false,
            isValidOp.getValidationError().getCoordinate(), type, msg);
    }
    return new IsValidModel(true, null, null, null);
}

常见的拓扑错误类型:

  • 自相交:多边形边界与自身相交
  • 重复点:相邻节点坐标相同
  • 环方向错误:外环应逆时针,内环应顺时针
  • 岛洞错误:内环不在外环内部

简单几何判断

/**
 * 判断几何是否简单
 * 线不自相交为简单,面无自相交为简单
 */
public static IsSimpleModel isSimple(Geometry geom) {
    IsSimpleOp isSimpleOp = new IsSimpleOp(geom);
    isSimpleOp.setFindAllLocations(true);
    if (!isSimpleOp.isSimple()) {
        return new IsSimpleModel(false, isSimpleOp.getNonSimpleLocations());
    }
    return new IsSimpleModel(true, null);
}

几何修复

对于无效的几何,可以尝试自动修复:

/**
 * 修复无效几何
 * 主要针对多边形的自相交问题
 */
public static Geometry validate(Geometry geom) {
    if (geom instanceof Polygon) {
        if (geom.isValid()) {
            geom.normalize();
            return geom;
        }
        Polygonizer polygonizer = new Polygonizer();
        addPolygon((Polygon) geom, polygonizer);
        return toPolygonGeometry(polygonizer.getPolygons());
    } else if (geom instanceof MultiPolygon) {
        if (geom.isValid()) {
            geom.normalize();
            return geom;
        }
        Polygonizer polygonizer = new Polygonizer();
        for (int n = geom.getNumGeometries(); n-- > 0; ) {
            addPolygon((Polygon) geom.getGeometryN(n), polygonizer);
        }
        return toPolygonGeometry(polygonizer.getPolygons());
    }
    return geom;
}

修复原理:通过多边形化(Polygonize)算法,将几何边界重新组织为有效的多边形。

实践案例

案例1:计算地块面积

// 读取WKT格式的地块数据
String wkt = "POLYGON((...))" ;
Geometry geom = wkt2Geometry(wkt);

// 检查坐标系
// 如果是经纬度坐标,需要先转换到投影坐标系
int wkid = 4490; // CGCS2000地理坐标系
int projWkid = CrsUtil.getProjectedWkid(geom); // 获取对应投影坐标系

// 坐标转换
Geometry projGeom = CrsUtil.transform(geom, wkid, projWkid);

// 计算面积(结果为平方米)
double areaSqm = area(projGeom);
double areaMu = areaSqm / 666.67;  // 转换为亩

案例2:批量数据有效性检查

List<String> invalidWkts = new ArrayList<>();

for (WktFeature feature : layer.getFeatures()) {
    Geometry geom = wkt2Geometry(feature.getWkt());
    IsValidModel result = isValid(geom);
    
    if (!result.isValid()) {
        System.out.println("要素ID: " + feature.getWfId() + 
            " 错误类型: " + result.getMessage() +
            " 错误位置: " + result.getCoordinate());
        invalidWkts.add(feature.getWkt());
    }
}

小结

本章介绍了几何数据的基础知识:

  1. 几何模型:点、线、面是基本几何类型,通过组合形成复杂几何
  2. WKT格式:标准的文本表示格式,便于数据交换和调试
  3. 基本属性:长度、面积、中心点等是常用的几何属性
  4. 有效性检查:数据处理前应检查几何有效性,必要时进行修复

下一章将介绍坐标系统,理解坐标系是正确处理GIS数据的关键。

posted @ 2025-11-26 20:20  我才是银古  阅读(0)  评论(0)    收藏  举报