Spring Boot + MyBatis plus + MySQL 实现位置直线距离实时计算

一、背景

在一些服务类系统开发时,难免遇到一些服务者和客户之间的一些距离计算,更方便的让服务者了解客户的地址,更好的方式就是调用地图sdk或者api实现,但有些需要通过接口查看多个地点的直线距离时,接口请求会比较多,比如我当前接入的地图接口,每秒请求不能超过三次,因此不符合我们一次查多个地点的位置;所以需要借助其他方式实现;

二、解决方案

  1. 在表结构中添加字段

在添加的字段中存客户的经纬度坐标,前提是客户的位置是固定的,字段类型为point类型

添加字段脚本为

sql 复制代码
ALTER TABLE merchant_shop ADD COLUMN geo_point POINT  null comment '地理坐标';
ALTER TABLE merchant_shop ADD SPATIAL INDEX idx_geo_point(geo_point);

2.在表结构对应实体添加位置字段

java 复制代码
  /**
     * MybatisX 会自动识别 POINT 类型,并使用对应的 TypeHandler
     * 它通常会引入 org.locationtech.jts.geom.Point
     */
    @TableField(value = "geo_point",typeHandler = GeoPointTypeHandler.class // 引用你自己的TypeHandler
    )
    private Point geoPoint;

3.引入集合空间计算和分析依赖

JTS(Java Topology Suite)库是一个强大的几何处理工具,提供了丰富的几何对象创建、操作和分析功能。通过JTS,你可以进行各种几何计算和空间分析,并将几何对象转换为标准的WKT格式以便于存储和传输。在实际应用中,JTS广泛用于地理信息系统(GIS)、地图服务和空间数据处理等领域。

java 复制代码
  <dependency>
            <groupId>org.locationtech.jts</groupId>
            <artifactId>jts-core</artifactId>
            <version>1.18.2</version> <!-- 建议使用较新版本 -->
        </dependency>

4.自定实现TypeHandler,处理经纬度坐标

java 复制代码
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKBReader;
import org.locationtech.jts.io.WKTReader;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class GeoPointTypeHandler  extends BaseTypeHandler<Point> {

    // SRID 4326 对应 WGS84 坐标系 (GPS 使用的坐标系)
    public static final GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326);
    private static final WKBReader wkbReader = new WKBReader(geometryFactory);
    private static final WKTReader wktReader = new WKTReader(geometryFactory);

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Point parameter, JdbcType jdbcType) throws SQLException {
        // 将 Java 的 Point 对象写入数据库
        ps.setObject(i, parameter);
    }

    @Override
    public Point getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return getPointFromResultSet(rs.getObject(columnName));
    }

    @Override
    public Point getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return getPointFromResultSet(rs.getObject(columnIndex));
    }

    @Override
    public Point getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return getPointFromResultSet(cs.getObject(columnIndex));
    }

    /**
     * 从数据库返回的对象中解析出 Point 对象
     * 数据库驱动可能返回 byte[] (WKB) 或 String (WKT)
     */
    private Point getPointFromResultSet(Object obj) throws SQLException {
        if (obj == null) {
            return null;
        }
        if (obj instanceof Point) {
            return (Point) obj;
        }
        try {
            if (obj instanceof byte[]) {
                // 处理 WKB (Well-Known Binary) 格式
                return (Point) wkbReader.read((byte[]) obj);
            } else if (obj instanceof String) {
                // 处理 WKT (Well-Known Text) 格式, e.g., "POINT(103.87 36.05)"
                return (Point) wktReader.read((String) obj);
            }
        } catch (ParseException e) {
            throw new SQLException("Error parsing geometry from database: " + e.getMessage(), e);
        }
        throw new SQLException("Unsupported geometry type returned from database: " + obj.getClass().getName());
    }
}

5.通过SQL脚本查询实现,查询过程使用MySQL现有函数ST_Distance_Sphere

ST_Distance_Sphere 是MySQL中的一个空间函数,它用于计算球面上两点之间的最小距离。这个函数非常有用,特别是在处理地理位置数据时,比如计算两个地点之间的直线距离

sql 复制代码
 SELECT  
    ms.*, 
    ST_Distance_Sphere(geo_point,
        POINT(#{latValue}, #{lngValue})
        ) AS linearDistance
 FROM merchant_shop ms

注:latValue 为当前位置的精度, lngValue为当前位置的维度;

通过以上方案可以实现距离计算,就可以不用频繁调用三方地图接口;

相关推荐
ChinaRainbowSea15 分钟前
Spring Boot3 + JDK21 的迁移 超详细步骤
java·spring boot·后端·spring
Jul1en_36 分钟前
【自动化测试】介绍Web自动化测试及Selenium安装
spring boot·功能测试·selenium
老华带你飞43 分钟前
旅游|基于Java旅游信息推荐系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端·旅游
老华带你飞1 小时前
医院挂号|基于Java医院挂号管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot
豐儀麟阁贵1 小时前
9.6使用正则表达式
java·开发语言·数据库·mysql
悟空码字1 小时前
SpringBoot实现消息推送:让服务器学会“主动搭讪”
java·spring boot·后端
+VX:Fegn08952 小时前
人力资源管理|基于springboot + vue人力资源管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
即随本心0.o2 小时前
SSE服务搭建
java·spring boot
灰什么鱼2 小时前
OkHttp + Retrofit2 调用第三方接口完整教程(以nomad为例)
java·spring boot·okhttp·retrofit
一 乐2 小时前
海鲜商城购物|基于SprinBoot+vue的海鲜商城系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot