MySQL Geometry的使用 —— 路径规划搜索
地图相关服务选择的是四维图新
本文记录的是,地图上路径规划搜索,后端逻辑和SQL(后端),前端相关接口服务可看 MineMap for 2D
一、搜索效果
通过起始点位置,获得最佳路径规划,并搜索出路径周围指定范围内的数据,如下图,为市民广场到宾馆最佳路径,50米内的单位建筑等数据
二、搜索处理逻辑
- 前端先调地图服务中的路径规划接口
- 将路径坐标数据传至后端(数据需要处理)
- 后端根据路径坐标,获得指定距离内的点坐标,构造多边形(可分段构造)
- 搜索出范围内的数据,进行统计(由分段构造,缩小搜索面积)
- 将搜索结果返给前端
三、SQL
通过路径坐标数据,获得路径多边形(如需要查找路径多少范围内的数据时),需要注意单位是度还是米,其中ST_Buffer_Strategy
为线构造面时,起始点及拐弯处,精度策略
-- 通过线坐标构造面坐标,其中参数 0.04单位为度,由传参转换而来
SELECT ST_AsText(
ST_Buffer(
ST_GeomFromText('LINESTRING(103 35,103 36,104 36)'), 0.04,
ST_Buffer_Strategy('end_round',4),ST_Buffer_Strategy('join_round',4)
)
)
由左边路径,构造右边路径多边形的面坐标
-- 其中 ST_POLYGONFROMTEXT()参数为,路径构造的面坐标
select ST_AsGeoJSON(m.geometry) as geometry
from mapdata m
where jd > 103.5 and jd < 104.5 and wd > 35.5 and wd < 36.5
and ST_CONTAINS(ST_POLYGONFROMTEXT('POLYGON(103 35,104 35,104 36,103 36,103 35)'),m.geometry)
四、Java代码中部分工具类
import org.apache.commons.lang3.StringUtils;
import java.util.LinkedHashSet;
import java.util.List;
public class GeometricFormat {
/**
* 线数据格式转换,例如:
* "[[1,1],[2,2]]" ——> "LINESTRING(1 1,2 2)"
*
* @param str
* @return java.lang.String
* @Author: changge
* @date 2021/6/13 20:51
**/
public String linestringFormat2(String str) {
//截取 [[1,1],[2,2]] --> [1,1],[2,2]
String beforeStr = StringUtils.substringBeforeLast(str, "]");
// 去掉下面两行注释,方法参数应为 [[[1,1],[2,3]]]
// String beforeStr1 = StringUtils.substringBeforeLast(beforeStr, "]");
// String afterStr = StringUtils.substringAfter(str, "[");
String afterStr1 = StringUtils.substringAfter(beforeStr, "[");
// "[1,1],[2,2]" ——> "LINESTRING(1 1,2 2)"
String str3 = "LINESTRING" + afterStr1.replace("[", "(").replace("]", ")");
String str4 = str3.replace(",", " ");
String LinestringString = str4.replace(") (", ",");
// 防止路径是由多条线段组成
LinestringString = LinestringString.replace("),(", ",");
// 去除重复坐标
String[] strarr = LinestringString.split(",");
LinkedHashSet<String> temp = new LinkedHashSet<>();
for (int i = 0; i < strarr.length; i++) {
temp.add(strarr[i]);
}
String s = StringUtils.substringBeforeLast(temp.toString(), "]");
LinestringString = StringUtils.substringAfter(s, "[").replace(", ", ",");
return LinestringString;
}
}
import net.sf.json.JSONObject;
/**
* 格式转换
*
* @Author: changge
* @date 2021/8/18 9:45
* @return null
**/
public class getPiUtil {
/**
* 格式转换:米 ——> 度
*
* @param json json数据
* @return String
*/
public Double getPi(JSONObject json) {
// 格式转换
Integer fw = json.getInt("fw");
/**
* 存的经纬度,那么单位则是度,存的是平面坐标系,那么单位就是平面的距离单位
* mysql认为存进去的坐标都是笛卡尔平面坐标系,不会自动进行度米互转
*/
return (fw * 180) / (6371000 * Math.PI);
}
}
/**
* 路径范围搜索
*
* @param json json数据
* @return String
*/
@Override
public cn.hutool.json.JSONObject selectLjssList(JSONObject json) {
// 格式转换
List<List<List<Double>>> ljzb = json.getJSONArray("ljzb");
Double gd = null;
if (json.has("gd")) {
gd = json.getDouble("gd");
}
Double pi = new getPiUtil().getPi(json);
List<Mapdata> mapList = new ArrayList<>();
for (List<List<Double>> lj : ljzb) {
// 判断路径某一段路径坐标大于200个坐标点时,且不为200整数,
// 防止刚好为201,截断剩下的一个坐标点构不成路线
if (lj.size() > 200 && lj.size() % 200 != 1) {
// 按200个坐标点截取,构造多边形,防止某一个多边形面积过大,搜索效率降低
for (List<List<Double>> list : ListUtils.partition(lj, 200)) {
// 获得路径坐标的两个极点(极大、极小)
List<List<Double>> maxPolygon = new getMaxPolygonDate().getMaxPolygon(list);
// 右上角坐标经纬度
Double rightJd = maxPolygon.get(0).get(0);
Double rightWd = maxPolygon.get(0).get(1);
// 左下角坐标经纬度
Double leftJd = maxPolygon.get(1).get(0);
Double leftWd = maxPolygon.get(1).get(1);
// 格式转换
com.alibaba.fastjson.JSONArray jsonArray = com.alibaba.fastjson.JSONArray.parseArray(JSON.toJSONString(list));
// 线数据格式转换,例如:"[[1,1],[2,2]]" ——> "LINESTRING(1 1,2 2)"
String linestring = new GeometricFormat().linestringFormat2(jsonArray.toString());
// 由线坐标获得面坐标
String polygonString = tbSydwMapper.getPolygonString(linestring, pi);
// 根据面坐标进行搜索
List<Mapdata> jtssByPolygon = tbSydwMapper.findMapByPolygon(polygonString, null, leftJd, leftWd, rightJd, rightWd, json.getString("dwdm"));
// 累计每一段路径搜索的数据
for (Mapdata m : jtssByPolygon) {
mapList.add(m);
}
}
}else{
// 判断路径某一段路径坐标小于200个坐标点时,执行逻辑和上面差不多
...
}
return mapCount(mapList);
}
}