登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go go-geom 怎样计算几何对象的坐标包围盒

来源:17golang原创

时间:2026-10-31 01:56:13 380浏览 收藏

使用 go-geom 计算几何对象的坐标包围盒,核心是调用 g.Bounds(),确认 IsEmpty() 为假后,再读取各维度的 Min 与 Max。二维 XY 对象的常用顺序是 [minX, minY, maxX, maxY]。这个范围与坐标轴平行,适合表达视图范围或作粗筛选。

例如,一条配送线路已经用平面坐标表示,需要给地图视图提供覆盖范围。这里先解决四个极值的取得,不加入空间索引、投影转换或最小旋转矩形算法。示例固定依赖 v1.6.1,该标签的 go.mod 声明 Go 1.22;选定版本是为了让接口依据明确,不表示它是最新版本。

项目官方仓库:https://github.com/twpayne/go-geom

先用四个极值描述二维范围

LineString 表示折线,XY 规定两个坐标分量的含义;Bounds 保存各维最小值与最大值。维度0对应X,维度1对应Y。因此,X极值来自 Min(0)、Max(0),Y极值来自 Min(1)、Max(1)。IsEmpty 则是范围对象的空状态判断。接口定义可见包文档的 Bounds 部分。

LineString与XY布局及Bounds极值字段关系
图1:几何对象与范围字段的静态结构说明图,连线表示所属或方法关系,不表示执行顺序。
取值含义
Min(0)全部坐标中的最小X
Min(1)全部坐标中的最小Y
Max(0)全部坐标中的最大X
Max(1)全部坐标中的最大Y

把有效折线转换成四个数

在新建的示例目录中初始化模块并固定依赖;已有项目跳过初始化。以下是供读者运行的示例,不是本文实际运行记录。

# 新示例目录初始化;已有 go.mod 的项目不再执行这一行
go mod init example.com/geometrybox
# 使用与本文依据一致的依赖标签
go get github.com/twpayne/go-geom@v1.6.1
# 把下方完整程序保存为 main.go 后执行
go run .

函数刻意只接收 *geom.LineString,把输入限制在二维有限坐标折线。SetCoords 的错误用于发现坐标分量数不合布局的问题;数值是否有限由示例另外处理,不把这些责任都交给包围盒方法。

package main

import (
    "fmt"
    "log"
    "math"

    "github.com/twpayne/go-geom"
)

// lineBoxXY 只接收二维折线,拒绝非有限坐标和空范围。
func lineBoxXY(line *geom.LineString) ([4]float64, error) {
    var box [4]float64
    if line == nil {
        return box, fmt.Errorf("折线不能为空指针")
    }
    if line.Layout() != geom.XY {
        return box, fmt.Errorf("需要 XY 布局,实际为 %v", line.Layout())
    }
    for i, value := range line.FlatCoords() {
        if math.IsNaN(value) || math.IsInf(value, 0) {
            return box, fmt.Errorf("坐标分量 %d 不是有限数", i)
        }
    }
    bounds := line.Bounds()
    // 空对象没有可以交给视图使用的有限极值。
    if bounds.IsEmpty() {
        return box, fmt.Errorf("折线没有坐标")
    }
    box = [4]float64{
        bounds.Min(0), bounds.Min(1),
        bounds.Max(0), bounds.Max(1),
    }
    return box, nil
}

func main() {
    // 示例使用同一平面坐标系中的三个 XY 点。
    line, err := geom.NewLineString(geom.XY).SetCoords([]geom.Coord{
        {12, 8}, {-4, 17}, {7, -3},
    })
    if err != nil {
        log.Fatal(err)
    }
    box, err := lineBoxXY(line)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("minX=%g minY=%g maxX=%g maxY=%g\n",
        box[0], box[1], box[2], box[3])
}

按给出的三个点手工推算,X范围为-4至12,Y范围为-3至17。由此可预期程序打印下列文字;这组数值是示例推导,未在本轮执行程序。

minX=-4 minY=-3 maxX=12 maxY=17

空范围和零宽、零高不是一回事

新建但尚未扩展的范围没有有效坐标。v1.6.1 的实现用正无穷初始化最小值、负无穷初始化最大值,并通过极值关系判断空状态。所以先判断空,再向外提供数字;不要把没有坐标的范围序列化成正常视图参数。依据:固定标签的 bounds.go。

一个有效点的最小值和最大值可以相等;一条竖直折线的X跨度也可以为零。这类范围不等于没有数据。如果视图需要最小显示宽度,应由调用方按业务单位增加留白,不要把零跨度当空对象丢弃。对于 NaN 或无穷值,示例入口直接返回错误,避免让异常值进入后续矩形计算。

多条折线如何合并范围

几条折线共享一个范围时,可以创建 geom.NewBounds(geom.XY),再用 Extend 扩展。此处的折线集合必须满足XY布局、同一坐标系、有限坐标三项输入约束;Extend 关联的是已有范围及待包含的几何,合并Bounds保留所有有效坐标的极值。

折线集合输入约束与Extend和合并Bounds的关系
图2:多条二维折线与共享包围盒的静态关系说明图,不是运行结果。
// 以下片段放在 main 内,接在已经创建 line 的代码之后。
other, err := geom.NewLineString(geom.XY).SetCoords([]geom.Coord{
    {20, 5}, {22, 11},
})
if err != nil {
    log.Fatal(err)
}
combined := geom.NewBounds(geom.XY)
for _, item := range []*geom.LineString{line, other} {
    // 复用上面的输入约束;空折线在本例中直接跳过。
    if item == nil {
        log.Fatal("待合并折线不能为空指针")
    }
    if item.Empty() {
        continue
    }
    if _, err := lineBoxXY(item); err != nil {
        log.Fatal(err)
    }
    combined.Extend(item)
}
if combined.IsEmpty() {
    log.Fatal("没有可用的几何坐标")
}
fmt.Printf("合并范围:[%g,%g,%g,%g]\n",
    combined.Min(0), combined.Min(1),
    combined.Max(0), combined.Max(1))

把追加折线的两个点纳入计算后,预期合并范围为 [-4,-3,22,17]。这个扩展会修改 combined;如果需要保留旧范围供另一处使用,应先复制范围对象,不要只复制指针变量。

应用到其他几何时注意什么

包提供统一的几何接口,Point、Polygon等类型也可取得各自的Bounds。这里的辅助函数只针对折线,替换类型时要重新安排输入约束,不能把函数签名直接当通用处理器。普通几何的坐标存储和范围计算关系可参考项目的几何基础实现。

布局与坐标系分开看。XY只说明有X、Y分量;同为XY不代表单位和投影相同。设置SRID也不能替代实际坐标转换。经纬度数据直接取极值,是输入数值的矩形范围;跨越经度接缝时,它未必是视觉上最紧的地理覆盖区域。

多维输入先明确要哪一层范围。XYZ多了高度,XYM第三个分量是M而非高度;如果只要地图二维范围,就明确读取前两维并单独制定输入策略。不要默认维度2始终代表Z。布局及维度含义见项目的 Layout 定义。

相关问题

包围盒重叠就意味着原几何相交吗?

不能据此作精确相交结论。矩形范围适合粗筛,原始几何的形状仍需另行判断。

Bounds是不是面积最小的旋转矩形?

不是。这里按坐标轴逐维取得极值,没有搜索矩形旋转角度。

几何坐标改了,之前保存的范围会自动变吗?

不要依赖这种联动。需要新范围时重新调用几何对象的Bounds;缓存范围的失效由调用方管理。对视图参数而言,先确认输入,再判断空状态,最后读取四个极值,是最容易维护的一种写法。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>