使用Gorm插入和选择PostGIS Geometry

rob*_*y22 8 postgresql postgis go wkb go-gorm

我一直试图找到一种方法来使用Golang插入和检索几何类型,特别是库gorm.我还尝试使用为几何定义不同类型的库orb,并提供不同格式之间的编码/解码.

天体具有Scan()Value()方法已经为每种类型的实现.这允许go Insert()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
        );
    `)
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

rows, err := db.Query("SELECT id, name, ST_AsBinary(geom) FROM orbtest LIMIT 1")
Run Code Online (Sandbox Code Playgroud)

对于insert(其中p是orb.Point):

db.Exec("INSERT INTO orbtest (id, name, geom) VALUES ($1, $2, ST_GeomFromWKB($3))", 1, "Test", wkb.Value(p))
Run Code Online (Sandbox Code Playgroud)

这是我的问题:通过使用GORM,我没有能够使用这些功能构建这些查询的奢侈.GORM将在给定结构的情况下自动将值插入数据库,并将数据扫描到结构的整个层次结构中.那些Scan()Value()方法在幕后被称为,没有我的控制.

尝试直接将二进制数据插入几何列中将不起作用,直接查询几何列将以十六进制形式给出结果.

我已经尝试了多种数据库方法来解决这个问题.我试图创建自动调用几何列上所需函数的视图.这适用于查询,但不能插入.

是否有可能制定某种触发器或规则来自动调用进/出数据所需的函数?

我还应该注意到,我正在处理的库完全独立于数据和模式,所以我没有任何类型的查询硬编码的奢侈.我当然可以编写一个扫描整个数据模型的函数,并从头开始生成查询,但我更喜欢有更好的选择.

有谁知道在SQL中使这个工作的方法?能够通过查询列本身自动调用列上的函数吗?

任何建议将不胜感激.

irb*_*ana 6

我将@robbieperry22 的答案与不同的编码库一起使用,发现我根本不需要修改字节。

包括要点供参考。

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"`
}
Run Code Online (Sandbox Code Playgroud)

然后对表设置/迁移部分进行了一些自定义:

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()

Run Code Online (Sandbox Code Playgroud)


rob*_*y22 2

我最终使用的另一个解决方案是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
}
Run Code Online (Sandbox Code Playgroud)

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

我还在该结构上实现了UnmarshalJSON()MarshalJSON(),以便它可以自动编组/解组 GeoJSON,然后无缝地从数据库中保存/获取。我使用geojson-go将 GeoJSON 与结构相互转换,然后使用geojson-geos-go将所述结构转换为我正在使用的go-geos结构。是的,有点复杂,但它确实有效。