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);
    }
}
posted @ 2021-10-08 09:22  长歌→  阅读(319)  评论(0编辑  收藏  举报