The article directories
-
- preface
- General implementation
- The database
-
- code
- Custom type + spatial data type
-
- MySQL underlying storage format
- The database
- code
- test
- Calculation of distance
preface
There is a requirement in the recent project to store some latitude and longitude information for the realization of similar return points of interest within 5km (such as surrounding toilets within 5km). In consideration of technical proficiency and operation and maintenance costs, the selection is as follows:
- Persistence layer framework: Mybatis – Plus
- Database: mysql5.7.x
General implementation
The database
The database creates two fields, longitude: LNG, latitude: LAT
- LNG: field type decimal(9,6)
- Lat: field type decimal(9,6)
code
class toilet {
String name;
double lng;
double lat
}
Copy the code
I won’t go into detail here, but it’s a very common implementation
Because the data structure is not of spatial type, spatial indexes cannot be created, and the query performance is prone to bottlenecks.
Custom type + spatial data type
MySQL supports spatial data types.
Spatial data types and functions are available for MyISAM, InnoDB, NDB, and ARCHIVE tables. For indexing SPATIAL columns, MyISAM and InnoDB support both SPATIAL and non-spatial indexes. Other storage engines support non-spatial indexing
MySQL underlying storage format
Query mysql official website, the result is as follows
The actual storage format of Geometry is 25 bytes
- 4 bytes for the integer SRID (0)
- 1 byte (integer byte order) (1 = small byte order)
- 4 bytes for integer type information (MySQL uses values from 1 to 7 to represent
Point
.LineString
.Polygon
.MultiPoint
.MultiLineString
.MultiPolygon
, andGeometryCollection
). - 8 – byte double – precision X coordinates
- 8 – byte double – precision Y coordinates
For example, POINT(1-1) consists of the following 25-byte sequence, each represented by two hexadecimal digits:
mysql> SET @g = ST_GeomFromText('POINT(1 -1)');
mysql> SELECT LENGTH(@g);
+------------+
| LENGTH(@g) |
+------------+
| 25 |
+------------+
mysql> SELECT HEX(@g);
+----------------------------------------------------+
| HEX(@g) |
+----------------------------------------------------+
| 000000000101000000000000000000F03F000000000000F0BF |
+----------------------------------------------------+
Copy the code
composition | The size of the | value |
---|---|---|
SRID | 4 bytes | 00000000 |
Byte order | 1 byte | 01 |
WKB type | 4 bytes | 01000000 |
X coordinate | 8 bytes | 000000000000F03F |
Y | 8 bytes | 000000000000F0BF |
The database
The database creates a field coordinate
- Coordinate: Field type Point
code
Entity class
class toilet {
String name;
geopoint location;
}
// Customize the data type
class geopoint {
double lng;
double lat
}
Copy the code
GeoPointTypeHandler
@Slf4j
@MappedTypes({GeoPoint.class})
public class GeoPointTypeHandler extends BaseTypeHandler<GeoPoint> {
/** * space reference identifier MySQL database default is 0 */
private static int SRID = 0;
A byte order indicator of 1 or 0 indicates small-endian or big-endian storage. Small and large byte orders are also called network data representation (NDR) and external data representation (XDR) */, respectively
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 Go to GeoPoint object **@param bytes
*/
private GeoPoint parse(byte[] bytes) {
ByteBuffer wrap = ByteBuffer.wrap(bytes)
// Small endpoint sort (Java default is big endpoint sort, this should be changed)
.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 Converts bytes to **@param geoPoint
*/
private byte[] to(GeoPoint geoPoint) {
ByteBuffer wrap = ByteBuffer.allocate(25)
// Small endpoint sort (Java default is big endpoint sort, this should be changed)
.order(ByteOrder.LITTLE_ENDIAN);
// SRID: 0
wrap.putInt(SRID);
A byte order indicator of 1 or 0 indicates small-endian or big-endian storage. Small and large byte orders are also known as network Data Representation (NDR) and External Data Representation (XDR), respectively
wrap.put(ENDIAN);
// the WKB type is the code indicating the geometry type wkbType: 1 MySQL uses values from 1 to 7 to represent Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, and GeometryCollection.
wrap.putInt(1);
/ / X coordinate
wrap.putDouble(geoPoint.getLon());
/ / Y
wrap.putDouble(geoPoint.getLat());
returnwrap.array(); }}Copy the code
The entity class is annotated
@TableName(autoResultMap = true) // Attention!! Mapping annotations must be enabled
class toilet {
String name;
@TableField(typeHandler = GeoPointTypeHandler.class)
geopoint location;
}
Copy the code
test
Toilet toilet = new Toilet();
toilet.setName("laker");
toilet.setLocation(new GeoPoint(123.23.1.2)); // Plug the entity directly
toiletService.save(toilet);
/ / queryList<Toilet> toilets = toiletService.list(); . [{"name":"laker",location:{"lng":123.23."lat":1.2}}]
Copy the code
Calculation of distance
The calculation results are consistent with the calculation distance API provided by Autonavi. Autonavi calculates distance webAPI
SELECT ( st_distance_sphere ( point ( 116.481028.39.989643 ), point ( 114.465302.40.004717 ), 6378137.0))AS distance
Copy the code
Note here that the data should not be arbitrarily manipulated or the st_distance_sphere parameter will be returned with an error.
Reference:
- Dev.mysql.com/doc/refman/…