MySQL空间函数计算线半径为300米之内面的经纬度集合

项目场景

MySQL空间函数运用:ST_Asgeojson、ST_Buffer、ST_GeomFromText

项目场景:某一天,突然来了一个需求,需要计算线集合半径为300米之内面的经纬度集合,前端用地图api嫌麻烦,所以要后端算好,前端直接用。我直呼:好(zhen)家(gou)伙(lan)!


函数介绍

MySQL版本:8.0.25

ST_Asgeojson

返回 JSON 格式的表示几何的字符串。

ST_GeomFromText

用于将几何数据从可读的文本类型转换成内部存储的二进制类型。

ST_Buffer

获取几何对象和距离,然后返回与源对象的距离小于或等于以输入距离为单位测量半径的所有点。

st_buffer(geometry, 半径)

geometry为点时【例:st_buffer(ST_GeomFromText("POINT(1 2)"), 300)】,点+半径生成圆;
geometry为线时【例:st_buffer(ST_GeomFromText("linestring(1 2,3 4)"), 300)】,线+半径生成圆角矩形;
geometry为面时【例:st_buffer(ST_GeomFromText("POLYGON(1 2,3 4,5 6)"), 300)】,面+半径生成更大的面。

在地图功能中,缓冲区是非常常见的功能,一来可以查看点线面一定范围类的覆盖区域,二来在一些分析场景中,已知一个位子坐标信息及缓冲半径,生成缓冲区作为查询条件进行地理搜索

代码实现

刚开始做这个需求的时候,找到了对应的函数,写起来飞快
三下五除二,就写好了
具体实现如下:

<select id="getPolygon" resultType="com.*.*.**Response">
        select
        ST_Asgeojson(ST_Buffer(ST_GeomFromText(
            <foreach collection="dto.line" item="point" open="'linestring(" close=")'" separator=", ">
                    ${point.x} ${point.y}
            </foreach>
        ), ${dto.radius})) as geometry from dual
    </select>

OK,打完收工


自测时,发现了不对劲,返回的经纬度集合中,居然出现了负数!!!不可思议呀!
随即问前端
我:你入参传线的经纬度是什么坐标系?
前端:84坐标系
我:嗯?!(灵光一闪)
以前做过GIS的我,知道地图有84坐标系、火星坐标系、百度坐标系等等
当时我就在想MySQL使用的什么坐标系?!

经过了解,原来ST_Buffer函数的入参和返参都使用的是墨卡托坐标系

ST_Buffer
注意:ST_BUFFER()的参数地理信息及返回值均使用墨卡托坐标系,如非墨卡托坐标系的geojson,需使用工具类进行转换处理

于是乎:业务逻辑修改成,先将传入的84坐标系的点集合 转换成 墨卡托坐标系的点集合,交由数据库进行计算,再将计算的结果转换成84坐标系的点集合,传给前端。


	@Resource
    private DeviceWarnDAO deviceWarnDAO;

	public MyResponse getPolygon(MyDTO dto){
        if (dto.getLine().size() == 0) {
            throw new BusinessException("经纬度集合不为空");
        }
        if (Objects.isNull(dto.getRadius())) {
            throw new BusinessException("半径不为空");
        }
        List<MyQO> list = dto.getLine();
        list.forEach(v -> {
            JSONArray jsonArray = MercatorUtils.lngLat2Mercator(v.getX(), v.getY());
            v.setX(jsonArray.getDouble(0));
            v.setY(jsonArray.getDouble(1));
        });
        MyResponse myResponse = deviceWarnDAO.getPolygon(dto);
        JSONObject jsonObject = JSON.parseObject(myResponse.getGeometry());
        JSONObject newJsonObject = MercatorUtils.mercatorPolygon2Lnglat(jsonObject);
        myResponse.setGeometry(newJsonObject.toJSONString());
        return myResponse;
	}

附上工具类


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import static java.lang.Math.PI;

/**
 * 墨卡托工具类
 * @author admin
 */
public class MercatorUtils {

    private final static String COORDINATES = "coordinates";
    /**
     * 点位geojson转墨卡托
     *
     * @param point
     * @return
     */
    public static JSONObject point2Mercator(JSONObject point) {
        JSONArray xy = point.getJSONArray(COORDINATES);
        JSONArray mercator = lngLat2Mercator(xy.getDouble(0), xy.getDouble(1));
        point.put(COORDINATES, mercator);
        return point;
    }

    /**
     * 经纬度转墨卡托
     */
    public static JSONArray lngLat2Mercator(double lng, double lat) {
        double x = lng * 20037508.342789 / 180;
        double y = Math.log(Math.tan((90 + lat) * PI / 360)) / (PI / 180);
        y = y * 20037508.34789 / 180;
        JSONArray xy = new JSONArray();
        xy.add(x);
        xy.add(y);
        return xy;
    }

    public static void main(String[] args) {
        JSONArray a = lngLat2Mercator(117.348938,31.805369);
    }

    /**
     * 墨卡托坐标系数据转普通坐标系
     */
    public static JSONObject mercatorPolygon2Lnglat(JSONObject polygon) {
        JSONArray coordinates = polygon.getJSONArray(COORDINATES);
        JSONArray xy = coordinates.getJSONArray(0);
        JSONArray ms = new JSONArray();
        for (int i = 0; i < xy.size(); i++) {
            JSONArray p = xy.getJSONArray(i);
            JSONArray m = mercator2lngLat(p.getDouble(0), p.getDouble(1));
            ms.add(m);
        }
        JSONArray newCoordinates = new JSONArray();
        newCoordinates.add(ms);
        polygon.put(COORDINATES, newCoordinates);
        return polygon;
    }

    public static JSONArray mercator2lngLat(double mercatorX, double mercatorY) {
        double x = mercatorX / 20037508.342789 * 180;
        double y = mercatorY / 20037508.34789 * 180;
        y = 180 / PI * (2 * Math.atan(Math.exp(y * PI / 180)) - PI / 2);
        JSONArray newCoordinates = new JSONArray();
        newCoordinates.add(x);
        newCoordinates.add(y);
        return newCoordinates;
    }
}

成功!!


最后

该需求比较急,着急做需求就没有尝试使用代码计算,另外Java代码也是可以实现的;
为了深入学习MySQL的空间函数,为此记录一下

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像风一样 

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值