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

使用 Gorm 和 MySQL 处理空间数据

使用 Gorm 和 MySQL 处理空间数据

Go
炎炎设计 2022-05-18 16:40:39
我参考了 irbanana关于支持 PostGIS 的空间数据类型的回答。我正在使用 MySQL 并尝试实现Value()自定义数据类型EWKBGeomPoint。我的戈姆模型:import (    "github.com/twpayne/go-geom"    "github.com/twpayne/go-geom/encoding/ewkb")type EWKBGeomPoint geom.Pointtype Tag struct {    Name string `json:"name"`json:"siteID"` // forign key    Loc EWKBGeomPoint `json:"loc"`}据我所知,MySQL 支持这样的插入:INSERT INTO `tag` (`name`,`loc`) VALUES ('tag name',ST_GeomFromText('POINT(10.000000 20.000000)'))或者INSERT INTO `tag` (`name`,`loc`) VALUES ('tag name', ST_GeomFromWKB(X'0101000000000000000000F03F000000000000F03F'))如果我自己做一个Value()来满足database/sql's 的Valuer界面:func (g EWKBGeomPoint) Value() (driver.Value, error) {    log.Println("EWKBGeomPoint value called")    b := geom.Point(g)    bp := &b    floatArr := bp.Coords()    return fmt.Sprintf("ST_GeomFromText('POINT(%f %f)')", floatArr[0], floatArr[1]), nil}包括在内的整个值ST_GeomFromText()在 Gorm 的单引号中引用,因此它不起作用:INSERT INTO `tag` (`name`,`loc`) VALUES ('tag name','ST_GeomFromText('POINT(10.000000 20.000000)')');我如何使它工作?编辑1:我追踪到 Gorm 代码,最终它得到了 tocallback_create.go的createCallback功能。在里面检查if primaryField == nil它是真的,它进入调用scope.SQLDB().Exec然后我没有进一步追踪。scope.SQL 是字符串INSERT INTO标记(名称,loc) VALUES (?,?)并scope.SQLVars打印[tag name {{1 2 [10 20] 0}}]. 看起来插值发生在这个调用中。这是调用database/sql代码吗?
查看完整描述

2 回答

?
叮当猫咪

TA贡献1776条经验 获得超12个赞

这是另一种方法;使用二进制编码。


根据此文档,MySQL 使用 4 个字节存储几何值以指示 SRID(空间参考 ID),然后是值的 WKB(众所周知的二进制)表示。


因此,一个类型可以使用 WKB 编码并在 Value() 和 Scan() 函数中添加和删除四字节前缀。在其他答案中找到的 go-geom 库有一个 WKB 编码包,github.com/twpayne/go-geom/encoding/wkb。


例如:


type MyPoint struct {

    Point wkb.Point

}


func (m *MyPoint) Value() (driver.Value, error) {

    value, err := m.Point.Value()

    if err != nil {

        return nil, err

    }


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

    if !ok {

        return nil, fmt.Errorf("did not convert value: expected []byte, but was %T", value)

    }


    mysqlEncoding := make([]byte, 4)

    binary.LittleEndian.PutUint32(mysqlEncoding, 4326)

    mysqlEncoding = append(mysqlEncoding, buf...)


    return mysqlEncoding, err

}


func (m *MyPoint) Scan(src interface{}) error {

    if src == nil {

        return nil

    }


    mysqlEncoding, ok := src.([]byte)

    if !ok {

        return fmt.Errorf("did not scan: expected []byte but was %T", src)

    }


    var srid uint32 = binary.LittleEndian.Uint32(mysqlEncoding[0:4])


    err := m.Point.Scan(mysqlEncoding[4:])


    m.Point.SetSRID(int(srid))


    return err

}

使用 MyPoint 类型定义标签:


type Tag struct {

    Name string   `gorm:"type:varchar(50);primary_key"`

    Loc  *MyPoint `gorm:"column:loc"`

}


func (t Tag) String() string {

    return fmt.Sprintf("%s @ Point(%f, %f)", t.Name, t.Loc.Point.Coords().X(), t.Loc.Point.Coords().Y())

}

使用以下类型创建标签:


tag := &Tag{

    Name: "London",

    Loc: &MyPoint{

        wkb.Point{

            geom.NewPoint(geom.XY).MustSetCoords([]float64{0.1275, 51.50722}).SetSRID(4326),

        },

    },

}


err = db.Create(&tag).Error

if err != nil {

    log.Fatalf("create: %v", err)

}

MySQL结果:


mysql> describe tag;

+-------+-------------+------+-----+---------+-------+

| Field | Type        | Null | Key | Default | Extra |

+-------+-------------+------+-----+---------+-------+

| name  | varchar(50) | NO   | PRI | NULL    |       |

| loc   | geometry    | YES  |     | NULL    |       |

+-------+-------------+------+-----+---------+-------+



mysql> select name, st_astext(loc) from tag;

+--------+------------------------+

| name   | st_astext(loc)         |

+--------+------------------------+

| London | POINT(0.1275 51.50722) |

+--------+------------------------+

(ArcGIS 说4326 是用于在全世界存储参考数据的最常见的空间参考。它是 PostGIS 空间数据库和 GeoJSON 标准的默认值。它也被默认用于大多数 Web 制图库。)


查看完整回答
反对 回复 2022-05-18
?
慕桂英3389331

TA贡献2036条经验 获得超8个赞

Hooks可以让你在 Gorm 的 sql 生成之前将列设置为gorm.Expr 。


例如,在插入之前是这样的:


func (t *Tag) BeforeCreate(scope *gorm.Scope) error {


  x, y := .... // tag.Loc coordinates


  text := fmt.Sprintf("POINT(%f %f)", x, y)


  expr := gorm.Expr("ST_GeomFromText(?)", text)


  scope.SetColumn("loc", expr)


  return nil

}


查看完整回答
反对 回复 2022-05-18
  • 2 回答
  • 0 关注
  • 123 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号