geometry:MySQL的空间数据类型(Spatial Data Type)与JTS(OSGeo)类型之间的序列化和反序列化

文章介绍了如何将MySQL中的空间数据类型转换为Java对象,主要利用JTS库进行WKB数据的解析和序列化。通过WKBReader和WKBWriter进行二进制与Geometry对象间的转换,同时提到了MySQL的WKB存储格式和OGC的开放地理数据互操作规范。
摘要由CSDN通过智能技术生成

现在很多应用都需要根据距离对信息进行排序,MYSQL5.7.28以后新增了空间数据类型(Spatial Data Type 即geometry)和相应的距离计算。

关于如何进行距离计算和排序不是本文要说的重点。本文要说明的是如何将MySQL存储的空间数据类型(Spatial Data Type)转为对应的Java对象以方便访问。

地理空间数据格式是有规范的,OpenGIS即(Open Geodata Interoperation Specification,OGIS-开放的地理数据互操作规范)由美国OGC(OpenGIS协会,Open Geospatial Consortium)提出。OGC是一个非盈利性组织,目的是促进采用新的技术和商业方式来提高地理信息处理的互操作性(Interoperability),它致力于消除地理信息应用(如地理信息系统,遥感,土地信息系统,自动制图/设施管理(AM/FM)系统)之间以及地理应用与其它信息技术应用之间的藩篱,建立一个无“边界”的、分布的、基于构件的地理数据互操作环境。
也就是说OpenGIS对地理空间数据做了规范定义,MySQL定义的空间数据类型(比如POINT–点,POLYGON–多边形)在存储时也都遵循OpenGIS的数据规范。参见MySQL官方文档《11.4 Spatial Data Types》
同时OpenGIS还提供了几何基础类库实现这些定义,对应到Java语言的就是JTS库《JTS Topology Suite》

根据MySQL官方文档《11.4 Spatial Data Types》说明,MySQL是以二进制形式存储存储空间数据类型(即WKB),比如对于一个POINT(1 -1),就是保存为长度25个字节的数组。格式如下:

ComponentSizeValue
SRID4 byte0
Byte order1 byte01
WKB type4 bytes01000000
X coordinate8 bytes000000000000F03F
Y coordinate8 bytes000000000000F0BF

上面的格式很简单,如果自己对这些数据进行解析也不是很难的事儿,但这样要自己定义相关的类,写好多代码。
事实上JTS库已经帮我们实现了实现了二进制数据到Java数据对象(Geometry)的相互转换。我们没有必要重复造轮子。
我们所要做的就是代码中使用JTS库的Geometry对象保存空间数据,并通过JTS库来实现将Geometry序列化为WKB数据保存到数据库,以及将从数据库中读取的WKB格式二进制数据反序列化为Geometry对象。

关于WKB,WKT格式的说明参见本文最后《参考资料》一节提供的链接

JTS库依赖引入

		<dependency>
			<groupId>com.vividsolutions</groupId>
			<artifactId>jts</artifactId>
			<version>1.13</version>
		</dependency>

WKB解析

以下代码实现MySQL的WKB数据解析为com.vividsolutions.jts.geom.Geometry类的过程,

    /**
     * 将MySQL存储的WKB格式的二进制数据解析为{@link Geometry}对象
     * @param binary
     * @throws ParseException
     */
    public Geometry fromWKB(byte[] binary) throws ParseException {
        if(null == binary) {
          return null;
        }
        if(binary.length < 25) {
            throw new ParseException("INVALID binary data length,more than 25 bytes required");
        }
        int srid = ByteBuffer.wrap(binary,0,4).asIntBuffer().get();
        WKBReader wkbReader = new WKBReader();
        Geometry geo = wkbReader.read(Arrays.copyOfRange(binary, 4, binary.length));
        geo.setSRID(srid);
        return geo;
    }
    public final <T extends Geometry>T fromWKB(byte[] binary, Class<T> targetType) throws ParseException {
        return targetType.cast(fromWKB(binary));
    }

WKBReader在解析WIKB数据时并不会处理最开始的SRID部分,所以上面的代码中会先从数组头部读取4个字节作为SRID,将数组索引4开始的剩余数据交给WKBReader解析。

Geomerty序列化为WKB

    /**
     * 将{@link Geometry}类型转为适合MySQL数据库存储的二进制格式
     * @param <T>
     * @param input
     */
    public <T extends Geometry>byte[] toWKB(T input)  {
        if(null == input) {
            return null;  
          }
        WKBWriter wkbWriter = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
        byte[] binary = wkbWriter.write(input);
        byte[] out = new byte[4 + binary.length];
        /** 写入SRID */
        ByteBuffer.wrap(out).asIntBuffer().put(input.getSRID());
        System.arraycopy(binary, 0, out, 4, binary.length);
        return out;
    }

根据MySQL的官方说明,WKB存储时字节序(Byte order)为小端(little-endian),而WKBWriter的默认构造方法创建对象时默认字节序为大端,所以这里不可以使用默认构造方法创建对象,必须指定为小端。

ResultSet

从数据查询结果集对象(ResultSet)中读取Geometry对象.

    /**
     * 读取数据记录指定字段的值转为空间数据对象
     * @param rs
     * @param columnIndex
     * @throws SQLException
     */
    public final Object readGeometryData(ResultSet rs, int columnIndex) throws SQLException {
        try {
            return fromWKB(checkNotNull(rs,"rs is null").getObject(columnIndex));
        } catch (ParseException e) {
            throw new SQLException(e);
        }
    }

调用示例

    @Test
    public void test1() {
        try {
            Point point = new GeometryFactory().createPoint(new Coordinate(10,20));
            System.out.printf("point %s\n",point);
            byte[] binary = toWKB(point);
            Geometry p2 = fromWKB(binary);
            System.out.printf("p2 %s\n",p2);
            
        } catch (Exception e) {
            log(e.getMessage(),e);
        }
    }

完整代码参见我的码云仓库:https://gitee.com/l0km/sql2java/blob/dev/sql2java-base/src/main/java/gu/sql2java/geometry/MysqlGeometryDataCodec.java

https://gitee.com/l0km/sql2java/blob/dev/sql2java-base/src/test/java/gu/sql2java/SpatialDataCodecTest.java

参考资料

《mysql 5.7.28 空间地理位置计算》

《11.4 Spatial Data Types》

《JTS Topology Suite》

《Well-Known Text (WKT) Format》

《Well-Known Binary (WKB) Format》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值