简介
最近在做一个业务功能,需求涉及到了通过终端的定位获取距离终端最近的实体店铺的需求。因为之前没有涉及到地理位置坐标计算距离的知识,只知道redis可以计算两个坐标点(经纬度)之间的距离。但是不满足目前的需求,因为除了计算距离外还要进行筛选、排序、分页。所以就不考虑在redis实现。更不可能全表查询内存排序。所以上网搜索资料,找到了pgsql的插件postgis,是数据库层面支持经纬度距离计算的插件。
postgis官网:postgis.net/
postgis安装
postgis的安装其实是有两种方法的。
第一种是在安装pgsql的时候通过stack builder进行安装。但是这种可能会出现现在不下来,活着很慢会卡顿的情况。
所以我是使用第二种方法安装,直接官网下载postgis插件安装包进行安装,整个过程也很简单。我使用的是pgsql 14(postgis需要和pgsql版本对应,具体可以看官网介绍)。安装过程大概就是下面的几步:
然后选下面的步骤全部是即可
创建数据表
-- 先创建数据库,然后执行创建插件postgis
CREATE EXTENSION postgis;
create table t_store
(
id serial not null
constraint t_store_pk
primary key,
name varchar(64) not null,
longitude varchar,
latitude varchar not null,
geom geometry(Point, 4326) not null,
create_time timestamp default now() not null,
update_time timestamp default now() not null
);
comment on column t_store.name is '名称';
comment on column t_store.longitude is '经度';
comment on column t_store.latitude is '纬度';
comment on column t_store.geom is '地理位置信息';
alter table t_store
owner to postgres;
复制代码
搭建Spring Boot + MybatisPlus开发框架
搭建代码主要有下面几个注意点。
添加对应依赖
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.5.1'
// postgis 插件依赖包
compile group: 'net.postgis', name: 'postgis-jdbc', version: '2.5.1'
compile group: 'org.postgresql', name: 'postgresql'
}
复制代码
添加配置
spring.application.name=gradle_test
server.port=8080
# pgsql
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=Jdbc:postgresql://localhost:5432/postgis_test
spring.datasource.username=postgres
spring.datasource.password=123456
# mybatis config :自定义的类型转换类
mybatis-plus.type-handlers-package=com.unfbx.gradle_test.config.typehandler
复制代码
2、自定义TypeHandler来处理pgsql的地理位置字段
package com.unfbx.gradle_test.config.typehandler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.postgis.Geometry;
import org.postgis.PGgeometry;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.sqlException;
/**
* 描述:
*
* @author
* @date 2022-04-03
*/
public class AbstractGeometryTypeHandler<T extends Geometry> extends BaseTypeHandler<T> {
/**
* 表示向PreparedStatement里面设置值
*
* @param ps
* @param i
* @param parameter
* @param jdbcType
* @throws SQLException
*/
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
PGgeometry geometry = new PGgeometry();
geometry.setGeometry(parameter);
ps.setObject(i, geometry);
}
/**
* 根据列名获取值
*
* @param rs
* @param columnName
* @return
* @throws SQLException
*/
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
PGgeometry pGgeometry = (PGgeometry) rs.getObject(columnName);
if (pGgeometry == null) {
return null;
}
return (T) pGgeometry.getGeometry();
}
/**
* 根据列索引位置获取值
*
* @param rs
* @param columnIndex
* @return
* @throws SQLException
*/
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
PGgeometry pGgeometry = (PGgeometry) rs.getObject(columnIndex);
if (pGgeometry == null) {
return null;
}
return (T) pGgeometry.getGeometry();
}
/**
* 获取值 通过列索引
*
* @param cs
* @param columnIndex
* @return
* @throws SQLException
*/
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
PGgeometry pGgeometry = (PGgeometry) cs.getObject(columnIndex);
if (pGgeometry == null) {
return null;
}
return (T) pGgeometry.getGeometry();
}
}
复制代码
自定义坐标点处理类型转换继承AbstractGeometryTypeHandler
@MappedTypes(Point.class)
public class PointTypeHandler extends AbstractGeometryTypeHandler<Point> {
}
复制代码
坐标转换工具
public class CoordinatesUtil {
public static Point buildPoint(String longitude, String latitude) throws Exception {
if (StringUtils.isEmpty(longitude) || StringUtils.isEmpty(latitude)) {
throw new Exception("坐标数据异常");
}
try {
Point point = new Point(Double.parseDouble(longitude),Double.parseDouble(latitude));
return point;
} catch (Exception e) {
throw e;
}
}
}
复制代码
接口功能测试
这里只做了俩接口。坐标经纬度拾取直接使用百度地图或者高德地图即可。
百度地图坐标拾取系统
新增一个经纬度信息
@PostMapping("/store")
public String add(@RequestBody Store store) throws Exception {
store.setGeom(CoordinatesUtil.buildPoint(store.getLongitude(), store.getLatitude()));
storeService.save(store);
return "添加成功,id:" + store.getId();
}
复制代码
根据定位信息查询距离内的商户
请求参数携带经纬度信息。下面查询的是距离南汇嘴10000m以内的地方。
@PostMapping("/stores")
public List<StoreResp> queryList(@RequestBody StoreReq req){
return storeService.queryList(req);
}
复制代码
{
"longitude": "121.979515",
"latitude": "30.888643",
"distance":10000
}
复制代码
返回信息
[
{
"id": 1,
"name": "上海天文馆",
"distance": 5585.5020385
},
{
"id": 2,
"name": "南汇嘴",
"distance": 0.0
}
]
复制代码
数据库查询的sql其实是:
select
id, name,geom,
ST_Distance(ST_GeographyFromText(ST_AsText('POINT(121.926753 30.912628)')),
ST_GeographyFromText(ST_AsText(geom))) as distance
from
t_store;
复制代码
源码地址
github.com/Grt1228/pos…
参考资料:
[1] 百度地图坐标拾取系统: api.map.baidu.com/lbsapi/getp…
[2] 参考csdn文章: blog.csdn.net/zxt521yt/ar…
原文链接:https://juejin.cn/post/7083293713480286222
本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 cloud@ksuyun.com 举报,一经查实,本站将立刻删除。
如若转载,请注明出处:https://www.daxuejiayuan.com/17345.html
如若转载,请注明出处:https://www.daxuejiayuan.com/17345.html