为了账号安全,请及时绑定邮箱和手机立即绑定

使用 Gorm 插入和选择 PostGIS 几何

使用 Gorm 插入和选择 PostGIS 几何

Go
繁星淼淼 2023-06-05 17:49:15
我一直在尝试寻找一种使用 Golang 插入和检索几何类型的方法,特别是库gorm。我还尝试使用为几何定义不同类型并提供不同格式之间的编码/解码的库orb 。Orb已经为每种类型Scan()实现Value()了方法。这允许 goInsert()和Scan()函数与基元以外的类型一起工作。然而,Orb 希望使用以众所周知的二进制 (WKB) 格式表示的几何图形。orb 文档表明,要完成此操作,您应该简单地将字段包装在 PostGIS 函数中ST_AsBinary(),并ST_GeomFromWKB()分别用于查询和插入。例如,表定义为:_, err = db.Exec(`        CREATE TABLE IF NOT EXISTS orbtest (            id SERIAL PRIMARY KEY,            name TEXT NOT NULL,            geom geometry(POLYGON, 4326) NOT NULL        );    `)你可以这样做:rows, err := db.Query("SELECT id, name, ST_AsBinary(geom) FROM orbtest LIMIT 1")对于插入(其中 p 是一个 orb.Point):db.Exec("INSERT INTO orbtest (id, name, geom) VALUES ($1, $2, ST_GeomFromWKB($3))", 1, "Test", wkb.Value(p))这是我的问题:通过使用 GORM,我无法使用这些函数构建那些查询。GORM 会自动将值插入给定结构的数据库,并将数据扫描到结构的整个层次结构中。这些Scan()和Value()方法是在幕后调用的,不受我的控制。尝试将二进制数据直接插入几何列是行不通的,直接查询几何列将给出十六进制的结果。我尝试了多种数据库方法来解决这个问题。我试图创建自动调用几何列上所需函数的视图。这适用于查询,但不适用于插入。是否可以制作某种触发器或规则来自动调用传入/传出数据所需的函数?我还应该注意,我正在使用的库完全独立于数据和模式工作,因此我没有硬编码任何查询的奢侈。我当然可以编写一个函数来扫描整个数据模型,并从头开始生成查询,但如果有更好的选择,我会更喜欢。有谁知道在 SQL 中进行这项工作的方法吗?能够通过查询列本身自动调用列上的函数吗?任何建议将不胜感激。
查看完整描述

4 回答

?
元芳怎么了

TA贡献1798条经验 获得超7个赞

包括要点以供参考。


import  "github.com/twpayne/go-geom/encoding/geojson"



type EWKBGeomPoint geom.Point


func (g *EWKBGeomPoint) Scan(input interface{}) error {

    gt, err := ewkb.Unmarshal(input.([]byte))

    if err != nil {

        return err

    }

    g = gt.(*EWKBGeomPoint)


    return nil

}


func (g EWKBGeomPoint) Value() (driver.Value, error) {

    b := geom.Point(g)

    bp := &b

    ewkbPt := ewkb.Point{Point: bp.SetSRID(4326)}

    return ewkbPt.Value()

}



type Track struct {

    gorm.Model


    GeometryPoint EWKBGeomPoint `gorm:"column:geom"`

}

然后在表设置/迁移部分使用了一些定制:


err = db.Exec(`CREATE TABLE IF NOT EXISTS tracks (

    id SERIAL PRIMARY KEY,

    geom geometry(POINT, 4326) NOT NULL

);`).Error

if err != nil {

    return err

}


err = gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{

{

    ID: "init",

    Migrate: func(tx *gorm.DB) error {

        return tx.CreateTable(

            Tables...,

        ).Error

    },

},

{

    ID: "tracks_except_geom",

    Migrate: func(tx *gorm.DB) error {

        return db.AutoMigrate(Track{}).Error

    },

},

}).Migrate()


查看完整回答
反对 回复 2023-06-05
?
慕尼黑8549860

TA贡献1818条经验 获得超11个赞

我最终使用的解决方案如下:

首先,我创建了包含所有 orb 类型的新类型,例如:

type Polygon4326 orb.Polygon
type Point4326 orb.Point

然后我在每种类型上实现了Scan(),方法。Value()然而,我不得不编辑字节并转换为十六进制或从十六进制转换。当您直接查询 PostGIS 中的空间列时,它将返回 EWKB 的十六进制表示形式,本质上是 WKB,但包括 4 个字节来表示投影 ID(在我的例子中是 4326)。

在插入之前,我必须添加代表 4326 投影的字节。

在阅读之前,我不得不剥离那些字节,因为 orb 内置扫描预期的 WKB 格式。


查看完整回答
反对 回复 2023-06-05
?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

是否可以制作某种触发器或规则来自动调用传入/传出数据所需的函数?


曾经尝试过 gorm 钩子,例如:


type Example struct {

    ID   int

    Name string

    Geom ...

}


func (e *Example) AfterFind() (err error) {

    e.Geom = ... // Do whatever you like here

    return

}

您可以使用一些挂钩。我发现它们非常整洁且有用。



查看完整回答
反对 回复 2023-06-05
?
神不在的星期二

TA贡献1963条经验 获得超6个赞

我最终使用的另一个解决方案是使用go-geos,因为我发现我需要使用 GEOS C 库。这样,我就可以将结构转换为WKT用于插入(因为 postgis 接受它作为常规文本)并WKB在扫描时从中转换。

type Geometry4326 *geos.Geometry


// Value converts the given Geometry4326 struct into WKT such that it can be stored in a 

// database. Implements Valuer interface for use with database operations.

func (g Geometry4326) Value() (driver.Value, error) {


    str, err := g.ToWKT()

    if err != nil {

        return nil, err

    }


    return "SRID=4326;" + str, nil

}


// Scan converts the hexadecimal representation of geometry into the given Geometry4326 

// struct. Implements Scanner interface for use with database operations.

func (g *Geometry4326) Scan(value interface{}) error {


    bytes, ok := value.([]byte)

    if !ok {

        return errors.New("cannot convert database value to geometry")

    }


    str := string(bytes)


    geom, err := geos.FromHex(str)

    if err != nil {

        return errors.Wrap(err, "cannot get geometry from hex")

    }


    geometry := Geometry4326(geom)

    *g = geometry


    return nil

}

此解决方案可能并不适合所有人,因为并非所有人都需要使用 GEOS C 库,这在 Windows 上工作可能会很痛苦。我确信同样的事情可以使用不同的库来完成。

我另外在结构上实现了UnmarshalJSON()and ,以便它可以自动编组/解组 GeoJSON,然后无缝地保存/从数据库中获取。我使用geojson-go将 GeoJSON 转换为结构或从结构转换,然后使用geojson-geos-go将所述结构转换为我正在使用的go-geosMarshalJSON()结构来完成此操作。有点复杂,是的,但它有效。


查看完整回答
反对 回复 2023-06-05
  • 4 回答
  • 0 关注
  • 302 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信