惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyberwarzone
Cyberwarzone
V
Vulnerabilities – Threatpost
T
Tenable Blog
Forbes - Security
Forbes - Security
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
G
GRAHAM CLULEY
Know Your Adversary
Know Your Adversary
S
Securelist
C
Cybersecurity and Infrastructure Security Agency CISA
Project Zero
Project Zero
C
CXSECURITY Database RSS Feed - CXSecurity.com
V
Visual Studio Blog
WordPress大学
WordPress大学
Latest news
Latest news
K
Kaspersky official blog
T
Tailwind CSS Blog
T
Threat Research - Cisco Blogs
B
Blog RSS Feed
C
Cisco Blogs
博客园 - 聂微东
Martin Fowler
Martin Fowler
T
The Blog of Author Tim Ferriss
小众软件
小众软件
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
L
LINUX DO - 热门话题
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
P
Proofpoint News Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
S
SegmentFault 最新的问题
Security Latest
Security Latest
Y
Y Combinator Blog
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 最新话题
月光博客
月光博客
The GitHub Blog
The GitHub Blog
博客园 - 三生石上(FineUI控件)
S
Security Affairs
P
Proofpoint News Feed
D
DataBreaches.Net
有赞技术团队
有赞技术团队
云风的 BLOG
云风的 BLOG

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
几何数据基础
我才是银古 · 2026-06-22 · via 博客园 - 我才是银古

几何数据基础

概述

几何数据是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数据的关键。