geometry:MySQL的空间数据类型(Spatial Data Type)与JTS(OSGeo)类型之间的序列化和反序列化
创始人
2025-05-29 09:02:34
0

现在很多应用都需要根据距离对信息进行排序,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库依赖引入

		com.vividsolutionsjts1.13

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 fromWKB(byte[] binary, Class targetType) throws ParseException {return targetType.cast(fromWKB(binary));}

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

Geomerty序列化为WKB

    /*** 将{@link Geometry}类型转为适合MySQL数据库存储的二进制格式* @param * @param input*/public 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);}}

调用示例

    @Testpublic 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》

相关内容

热门资讯

基于LIN通信的诊断概述 文章目录 前言一、帧的类型1.1 诊断帧(Diagnostic Frame) 二、LIN的传输层2....
MySQL数据库的定时备份实践 1.背景介绍也许你会发现本站建站时间久远,但是本站的各类访问量总是很低,...
潮汕三日游,去潮汕旅游大概多少... 潮汕三日游,去潮汕旅游大概多少钱 作为一个对美食和文化充满热情的旅行者,我一直对潮汕这片土地心怀向往...
端午假期安全提示,转给师生家长 01 关注交通安全 1.出行途中注意交通安全,遵守交通规则,乘坐正规运营车辆,不坐“三无”车辆,不坐...
西藏几月是旺季? 我最近和老公商量着要去西藏玩7天,说实话,我对旅行的准备总是不太上心,所以这次特别希望能有一份详尽的...
甘南9日游攻略详细安排,一家三... 甘南,这片被《国家地理》盛赞为“人生必去的50个地方之一”的净土,宛如一颗镶嵌在青藏高原东北边缘的璀...
尝鲜福建四果汤,清凉一夏不是梦 夏日炎炎,来一碗福建四果汤,瞬间驱散暑气,开启清凉模式。作为闽南地区的传统消暑甜品,四果汤历史悠久,...
宜昌三峡三天两晚景点攻略,宜昌... 最近,我有幸和朋友们一起踏上了前往宜昌三峡的旅程。宜昌三峡,那山清水秀、景色宜人的地方,一直是我向往...
泰国启动“萨瓦滴·你好”旅游推... 曼谷5月30日电(李映民 赵婧楠)为庆祝中泰建交50周年,泰国国家旅游局于5月29日在曼谷启动“萨瓦...
智慧景区一体化建设方案 随着2023年文旅部《关于推动智慧旅游发展的指导意见》出台,全国景区掀起数字化转型浪潮。如何在激烈竞...
端午邂逅“六一” 东丽区精心打... 天津北方网讯:即将到来的端午佳节与“六一”国际儿童节不期而遇,当传统文化与童趣时光奇妙邂逅,为文旅注...
中签率100%,茅台机场购酒规... 蓝鲸新闻5月30日讯(记者 朱欣悦)5月29日,遵义茅台机场微信公众号披露,在2025年5月30日、...
新疆昌吉消防深入旅游景区开展端... 图为消防人员在景区民族风情园商户开展消防培训。刘臣 摄 5月29日,新疆昌吉州消防救援支队阜康市消...
17岁中学生成中国首位北坡登顶... 近日,多家媒体报道称北京第八十中学学生、17岁的李浩榕成为中国首位从北坡登顶珠峰的青少年的消息登上热...
甘南扎尕那:藏在石头山里的&q... 甘南扎尕那:藏在石头山里的“神仙村落”,雨后云雾缭绕,仿佛穿越异世界! 在甘南迭部县的群山深处,有一...
“非遗+民宿” 让民宿成为目的... 央广网北京5月30日消息(记者彭扬)“非遗是活着的在地文化,非遗与民宿的融合,通过非遗的注入真正提升...
护航端午假期,济南公交上线10... 海报新闻记者 秦文 济南报道 端午佳节将至,为满足市民游客返乡探亲、购物游玩出行需求,济南公交以“高...
港投·空港花园酒店盛大启幕:郑... 2025年5月30日,郑州航空港区迎来商旅服务业的重要里程碑——由航空港投资集团旗下公用事业集团匠心...