Postgresql 使用Gorm插入和选择PostGIS几何图形

Postgresql 使用Gorm插入和选择PostGIS几何图形,postgresql,go,postgis,go-gorm,wkb,Postgresql,Go,Postgis,Go Gorm,Wkb,我一直在试图找到一种使用Golang,特别是库来插入和检索几何类型的方法。我还尝试使用定义不同几何图形类型的库,并提供不同格式之间的编码/解码 Orb已经为每种类型实现了Scan()和Value()方法。这允许go的Insert()和Scan()函数处理基元以外的类型。然而,Orb希望使用以众所周知的二进制(WKB)格式表示的几何体 orb文档显示,要实现这一点,只需将字段包装在PostGIS函数ST_AsBinary()和ST_GeomFromWKB()中,分别用于查询和插入。例如,表定义为:

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

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
        );
    `)
您只需执行以下操作:

rows, err := db.Query("SELECT id, name, ST_AsBinary(geom) FROM orbtest LIMIT 1")
对于插入(其中p为圆点):

这是我的问题:通过使用GORM,我没有能力用这些函数构建这些查询。GORM将自动向给定结构的数据库中插入值,并将数据扫描到结构的整个层次结构中。那些
Scan()
Value()
方法是在幕后调用的,不受我的控制

试图直接将二进制数据插入到几何列中是行不通的,而直接查询几何列将以十六进制给出结果

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

是否有可能制定某种触发器或规则,对输入/输出的数据自动调用所需的函数

我还应该注意到,我正在使用的库完全独立于数据和模式,因此我没有硬编码任何类型查询的特权。我当然可以编写一个函数来扫描整个数据模型,并从头开始生成查询,但我更希望有更好的选择

有人知道用SQL实现这一点的方法吗?仅仅通过查询列本身就能够自动调用列上的函数吗

如有任何建议,将不胜感激

是否有可能制定某种触发器或规则,对输入/输出的数据自动调用所需的函数

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

类型示例结构{
ID int
名称字符串
几何。。。
}
func(e*示例)AfterFind()(错误){
e、 Geom=…//在这里做你喜欢做的事
返回
}

你可以使用一些。我发现它们非常简洁实用。

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

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

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

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


在阅读之前,我必须去除这些字节,因为orb内置的扫描需要WKB格式。

我使用@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"`
}
然后对表格设置/迁移部分进行了一些自定义:

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


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

类型Geometry4326*geos.Geometry
//值将给定的Geometry4326结构转换为WKT,以便将其存储在
//数据库。实现用于数据库操作的Valuer接口。
func(g Geometry4326)Value()(driver.Value,错误){
str,err:=g.ToWKT()
如果错误!=零{
返回零,错误
}
返回“SRID=4326;”+str,无
}
//扫描将几何图形的十六进制表示形式转换为给定的几何图形4326
//结构。实现用于数据库操作的扫描仪接口。
func(g*Geometry4326)扫描(值接口{})错误{
字节,确定:=值。([]字节)
如果!好的{
返回错误。新建(“无法将数据库值转换为几何图形”)
}
str:=字符串(字节)
geom,err:=geos.FromHex(str)
如果错误!=零{
返回错误。换行(错误,“无法从十六进制获取几何体”)
}
几何图形:=几何图形4326(几何图形)
*g=几何体
归零
}
此解决方案可能并不适合所有人,因为并非所有人都需要使用GEOS C库,这可能会让人难以在windows上工作。但我确信,同样的事情可以通过使用不同的库来完成

另外,我在struct上实现了
UnmarshalJSON()
MarshalJSON()
,这样它就可以自动封送/取消封送GeoJSON,然后无缝地从数据库保存/获取。我使用将GeoJSON转换为结构体/从结构体转换为结构体,然后将所述结构体转换为我正在使用的结构体。有点复杂,是的,但它是有效的

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