記事のディレクトリ
序文
最近、5 km以内の関心のあるポイント(たとえば、周囲5 km以内のトイレ)に同様のリターンを達成するために、緯度と経度の情報を保存する必要があるプロジェクトに対する需要があります。技術的な習熟度と運用および保守のコストを考慮に入れます。 、選択は次のとおりです。
-
永続層フレームワーク:mybatis-plus
-
データベース:Mysql5.7.x
一般的な認識
データベース
データベースは、経度:、lng
緯度:の2つのフィールドを作成します。lat
-
lng:フィールドタイプdecimal(9、6)
-
lat:フィールドタイプdecimal(9、6)
コード
class toilet {
String name;
double lng;
double lat
}
ここでは詳しく説明しません。これは非常に一般的な方法です。
これは空間データ構造ではないため、空間インデックスを作成できず、クエリのパフォーマンスにボトルネックが発生する可能性があります。
カスタムタイプ+空間データタイプ
MySQLは空間データ型をサポートしています。
空間データ型と関数を使用することができMyISAM
、InnoDB
、NDB
、およびARCHIVE
テーブル。空間列のインデックスを作成するために使用され、MyISAM
およびInnoDB
サポートの両方SPATIAL
と非SPATIAL
インデックス。他のストレージエンジンは非SPATIAL
インデックスをサポートします
MySQLの基盤となるストレージ形式
mysqlの公式ウェブサイトをクエリすると、結果は次のようになります
Geometryの実際のストレージ形式は次のとおりです。長さ25バイト
- 整数SRIDの場合は4バイト(0)
- 1バイト(整数バイト順)(1 =リトルエンディアン)
- 整数型情報の4つのバイトは、(MySQLは示すために1〜7の値を使用して
Point
、LineString
、Polygon
、MultiPoint
、MultiLineString
、MultiPolygon
、およびGeometryCollection
。) - 8バイトの倍精度X座標
- 8バイトの倍精度Y座標
たとえばPOINT(1 -1)
、次の25バイトのシーケンスで構成され、各シーケンスは2つの16進数で表されます。
mysql> SET @g = ST_GeomFromText('POINT(1 -1)');
mysql> SELECT LENGTH(@g);
+------------+
| LENGTH(@g) |
+------------+
| 25 |
+------------+
mysql> SELECT HEX(@g);
+----------------------------------------------------+
| HEX(@g) |
+----------------------------------------------------+
| 000000000101000000000000000000F03F000000000000F0BF |
+----------------------------------------------------+
組成 | サイズ | 値 |
---|---|---|
SRID | 4バイト | 00000000 |
バイトオーダー | 1バイト | 01 |
WKBタイプ | 4バイト | 01000000 |
X座標 | 8バイト | 000000000000F03F |
Y座標 | 8バイト | 000000000000F0BF |
データベース
データベースにフィールドを作成しますcoordinate
- 座標:フィールドタイプポイント
コード
エンティティクラス
class toilet {
String name;
geopoint location;
}
// 自定义数据类型
class geopoint {
double lng;
double lat
}
GeoPointTypeHandler
@Slf4j
@MappedTypes({
GeoPoint.class})
public class GeoPointTypeHandler extends BaseTypeHandler<GeoPoint> {
/**
* 空间参照标识系 MySQL数据库默认为0
*/
private static int SRID = 0;
/**
* 字节顺序指示符为1或0表示小端或大端存储。小字节序和大字节序分别也称为网络数据表示(NDR)和外部数据表示(XDR)
*/
private static byte ENDIAN = (byte) 1;
@Override
public void setNonNullParameter(PreparedStatement ps, int i, GeoPoint parameter, JdbcType jdbcType) throws SQLException {
ps.setBytes(i, to(parameter));
}
@Override
public GeoPoint getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parse(rs.getBytes(columnName));
}
@Override
public GeoPoint getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parse(rs.getBytes(columnIndex));
}
@Override
public GeoPoint getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parse(cs.getBytes(columnIndex));
}
/**
* bytes转GeoPoint对象
*
* @param bytes
*/
private GeoPoint parse(byte[] bytes) {
ByteBuffer wrap = ByteBuffer.wrap(bytes)
// 小端点排序(Java默认是大端点排序,这里要改下)
.order(ByteOrder.LITTLE_ENDIAN);
int SRID = wrap.getInt();
byte endian = wrap.get();
int wkbType = wrap.getInt();
double x = wrap.getDouble();
double y = wrap.getDouble();
GeoPoint geoPoint = new GeoPoint(x, y);
log.info("geo-point:{}", JSONUtil.toJsonStr(geoPoint));
return geoPoint;
}
/**
* GeoPoint转bytes对象
*
* @param geoPoint
*/
private byte[] to(GeoPoint geoPoint) {
ByteBuffer wrap = ByteBuffer.allocate(25)
// 小端点排序(Java默认是大端点排序,这里要改下)
.order(ByteOrder.LITTLE_ENDIAN);
// SRID: 0
wrap.putInt(SRID);
// 字节顺序指示符为1或0表示小端或大端存储。小字节序和大字节序分别也称为网络数据表示(NDR)和外部数据表示(XDR)
wrap.put(ENDIAN);
// WKB类型是指示几何类型的代码 wkbType: 1 MySQL使用从1个值至7,以表示 Point,LineString, Polygon,MultiPoint, MultiLineString, MultiPolygon,和 GeometryCollection。
wrap.putInt(1);
// X坐标
wrap.putDouble(geoPoint.getLon());
// Y坐标
wrap.putDouble(geoPoint.getLat());
return wrap.array();
}
}
エンティティクラスと関連ソリューション
@TableName(autoResultMap = true) // 注意!! 必须开启映射注解
class toilet {
String name;
@TableField(typeHandler = GeoPointTypeHandler.class)
geopoint location;
}
テスト
Toilet toilet = new Toilet();
toilet.setName("laker");
toilet.setLocation(new GeoPoint(123.23, 1.2)); // 直接塞实体
toiletService.save(toilet);
// 查询
List<Toilet> toilets = toiletService.list();
...
[{
"name":"laker",location:{
"lng":123.23,"lat":1.2}}]
距離を計算する
計算結果は、AutoNaviが提供する計算距離APIと一致しています。
Gaodeは距離webapiを計算します
SELECT ( st_distance_sphere ( point ( 116.481028,39.989643 ), point ( 114.465302,40.004717 ), 6378137.0 ) ) AS distance
参照:
- https://dev.mysql.com/doc/refman/5.7/en/gis-data-formats.html