WKB(well-known binary) 是WKT的二进制表示形式,解决了WKT表达方式冗余的问题,便于传输和在数据库中存储相同的信息WKB是采用二进制存储表示点线面等。
WKB比WKT的优势在于WKB是二进制,主要是解决传输和存储的效率问题,所以MySQL,Postgre数据存储Geometry类型的数据,存储的值是WKB格式的。
WKB(Well-Known Binary)是OGC定义的一种用于表示几何对象的二进制格式。
对于一个2D点(没有Z和M值)的WKB表示(小端序):
字节顺序:1字节(0x01表示小端序)
类型值:4字节(Point的类型值为1,2D点)
坐标:两个双精度浮点数(各8字节),x和y。
以一个点(Point)为例,其WKB十六进制表示为:
0101000000000000000000F03F000000000000F03F
- 字节序:01(表示little-endian)
- 几何类型:01000000(表示Point)
- 坐标值:X坐标为0x000000000000F03F,Y坐标为0x000000000000F03F12
一、java代码实现点xy转为wkb十六进制字符串
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class PointToWKB {
// 将双精度浮点数转换为小端字节序的8字节表示
private static String doubleToLittleEndianHex(double value) {
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putDouble(value);
return bytesToHex(buffer.array());
}
// 将整型转换为小端字节序的4字节表示
private static String intToLittleEndianHex(int value) {
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(value);
return bytesToHex(buffer.array());
}
// 字节数组转十六进制字符串
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
hexString.append(String.format("%02x", b));
}
System.out.println(hexString.toString());
return hexString.toString();
}
// 生成点的WKB十六进制表示
public static String convertPointToWKBHex(double x, double y) {
// 字节顺序标记 (小端字节序: 0x01)
String byteOrderHex = "01";
// 几何类型 (点: 1)
String geomTypeHex = intToLittleEndianHex(1);
// X坐标的十六进制表示
String xHex = doubleToLittleEndianHex(x);
// Y坐标的十六进制表示
String yHex = doubleToLittleEndianHex(y);
// 组合所有部分
return byteOrderHex + geomTypeHex + xHex + yHex;
}
public static void main(String[] args) {
double longitude = 121.387451356 ; // 经度
double latitude = 31.12874904; // 纬度
// 转换为WKB十六进制字符串
String wkbHex = convertPointToWKBHex(longitude, latitude);
// 输出结果
System.out.println("经度: " + longitude + ", 纬度: " + latitude);
System.out.println("WKB十六进制字符串: " + wkbHex);
}
}
二、oracle函数实现点xy转wkb十六进制字符串
CREATE OR REPLACE FUNCTION double_to_little_endian_hex(
p_value IN NUMBER
) RETURN VARCHAR2
IS
l_raw RAW(8);
BEGIN
-- 使用 UTL_RAW 转换函数(Oracle 11g+)
l_raw := UTL_RAW.CAST_FROM_BINARY_DOUBLE(p_value);
-- 返回小端序十六进制表示(反转字节顺序)
RETURN LOWER(RAWTOHEX(UTL_RAW.REVERSE(l_raw)));
EXCEPTION
-- 处理 UTL_RAW 不可用的情况(Oracle 10g 或其他限制)
WHEN OTHERS THEN
-- 回退方案:手动实现 IEEE 754 双精度转换
DECLARE
l_hex VARCHAR2(16);
l_val BINARY_DOUBLE := p_value;
l_ieee RAW(8);
BEGIN
-- 使用 DBMS_LOB 实现转换(10g 兼容方案)
DBMS_LOB.CREATETEMPORARY(l_ieee, TRUE);
DBMS_LOB.WRITEAPPEND(l_ieee, 8, UTL_RAW.CAST_FROM_BINARY_DOUBLE(l_val));
l_hex := RAWTOHEX(UTL_RAW.REVERSE(l_ieee));
RETURN LOWER(l_hex);
EXCEPTION
WHEN OTHERS THEN
-- 最终回退:返回 0 的表示
RETURN '0000000000000000';
END;
END double_to_little_endian_hex;
CREATE OR REPLACE FUNCTION point_to_wkb_hex(
p_x IN NUMBER, -- 经度/X坐标
p_y IN NUMBER -- 纬度/Y坐标
) RETURN VARCHAR2
IS
-- WKB 固定前缀 (01 + 01000000)
c_prefix CONSTANT VARCHAR2(10) := '0101000000';
l_x_hex VARCHAR2(16);
l_y_hex VARCHAR2(16);
BEGIN
-- 转换经度坐标
l_x_hex := double_to_little_endian_hex(p_x);
-- 转换纬度坐标
l_y_hex := double_to_little_endian_hex(p_y);
-- 组合所有部分形成完整 WKB
RETURN c_prefix || l_x_hex || l_y_hex;
EXCEPTION
WHEN OTHERS THEN
-- 返回一个有效的 WKB 错误点
RETURN c_prefix || '000000000000F87F' || '000000000000F87F'; -- NaN点
END point_to_wkb_hex;
SELECT point_to_wkb_hex(121.387451356,31.12874904) AS wkb_hex FROM DUAL;
0101000000e5b3c500cc585e40ff3074b2f5203f40
通过postgis转换验证
SELECT ENCODE(ST_AsBinary(ST_GeomFromText('POINT(121.387451356 31.12874904)')),'hex') AS wkb;
0101000000e5b3c500cc585e40ff3074b2f5203f40
select ST_AsEWKT('0101000000e5b3c500cc585e40ff3074b2f5203f40'::geometry)
POINT(121.387451356 31.12874904)