当前位置: 首页 > 知识库问答 >
问题:

使用Gorm和MySQL处理空间数据

柯曦
2023-03-14

我引用了irbanana关于支持PostGIS的空间数据类型的回答。我正在使用MySQL,并试图为自定义数据类型实现Value()EWKBGeomPoint。

我的Gorm模型:

import (
    "github.com/twpayne/go-geom"
    "github.com/twpayne/go-geom/encoding/ewkb"
)

type EWKBGeomPoint geom.Point

type 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/sqlValuer界面:

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代码,最终得到了callback\u create.gocreateCallback函数。在它内部检查,如果primaryField==nil且为真,则它进入调用scope.SQLDB().Exec,然后我无法进一步跟踪。

scope.SQL是字符串插入标记nameloc)值(?,)scope.SQLVars打印[标记名{{12[10 20]0}]。看起来插值发生在这个调用中。

这是对数据库/sql代码的调用吗?

编辑2:

在这里找到了类似的Stackoverflow问题。但是我不明白解决办法。


共有2个答案

狄玮
2023-03-14

更新:这种方法不起作用。

钩子可以让您在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
}
东郭源
2023-03-14

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

根据该文档,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地图库默认使用它。)
 类似资料:
  • 我已经阅读了这里的文章和在使用JAXB时处理数字签名的示例代码,并且遇到了一个问题,我认为是JAXB封送将名称空间引入“signedinfo”元素。 我定义了一个XSD,它将被用不同编程语言实现的多个应用程序使用。我将XSD编译为JAXB注释类,供我的实现(Jersey JAX-RS和JAX-WS)使用。我目前拥有的流量如下: JAXB对象使用适当的数据创建或从远程应用程序传递。 按照Blaise

  • 问题内容: 我有一段 一个如何在CSS中用空格表示id和class 当我使用 它不适用于以上的CSS。 问题答案: 实际上代表了两个不同的类 将不起作用,但您可能最终会获得实际的para编号

  • 我有这样一个问题:我可以写到一个带有地理类型列的Postgis表,并且数据输入正确。当我试图取回它时,我得到了一个java.lang.IllegalArgumentException:不能转换org.PostgreSQL.util.PGObject类型的对象 我使用的是Hibernate Spatial 4.0M1、PostGIS2.0.2、Postgresql 9.1.7和Spring3.2、P

  • 问题内容: 抱歉,很长的帖子! 我有一个包含约30个表的数据库(InnoDB引擎)。这些表中只有两个表,即“ transaction”和“ shift”非常大(第一个表有150万行,而shift有23000行)。现在一切正常,我对当前的数据库大小没有任何问题。 但是,我们将有一个类似的数据库(相同的数据类型,设计等),但数据库更大,例如,“事务”表将具有约 10亿条记录 (每天约有 230 万笔交

  • 问题内容: 我想使用Gorm 来获取具有out_time的最后一个visit_details行。 本身就是VisitDetail OutTime为的类型。 码:- 查询: 问题答案: Go不能特别识别NULL。我认为您可以使用GORM中的原始查询来实现。像这样。 这是一个获取专栏的文章。希望这可以帮助。