blog icon indicating copy to clipboard operation
blog copied to clipboard

两个位置经纬度之间直线距离计算

Open xianzou opened this issue 2 years ago • 0 comments

两个位置经纬度之间直线距离计算

需要计算两个经纬度之间的直线距离,实现类是定位打卡效果;

代码如下:

import MapUtils from '@/utils/mapUtils';

const calculateTwoPlaceDistance = () => {
    // 调用距离计算接口,注意两个坐标类型要一致,都是腾讯地图位置或者高德地图,否则需要自己转换
    MapUtils.calculateDistance({
        from: '24.622473,118.037944', // 起点坐标
        to: '24.622154,118.039084', // 终点坐标
        success: res => { // 成功后的回调
            let hw = res.result.elements[0].distance; // 拿到距离(米)

            if (hw && hw !== -1) {
                if (hw < 1000) { 
                    hw += 'm';
                } else {
                    // 转换成公里
                    hw = `${(hw / 2 / 500).toFixed(2)}km`;
                }
            } else {
                hw = '距离太近或请刷新重试';
            }
            alert(`当前位置与办公地点距离:${hw}`);
        },
    });
};

mapUtils代码如下:

var ERROR_CONF = {
    KEY_ERR: 311,
    KEY_ERR_MSG: 'key格式错误',
    PARAM_ERR: 310,
    PARAM_ERR_MSG: '请求参数信息有误',
    SYSTEM_ERR: 600,
    SYSTEM_ERR_MSG: '系统错误',
    WX_ERR_CODE: 1000,
    WX_OK_CODE: 200
};

var EARTH_RADIUS = 6378136.49;

var Utils = {

    /**
     * 构造错误数据结构
     * @param {Number} errCode 错误码
     * @param {Number} errMsg 错误描述
     */
    buildErrorConfig(errCode, errMsg) {
        return {
            status: errCode,
            message: errMsg
        };
    },

    /**
     * 回调函数默认处理
     */
    polyfillParam(param) {
        param.success = param.success || function () { };
        param.fail = param.fail || function () { };
        param.complete = param.complete || function () { };
    },

    /**
     * 验证param对应的key值是否为空
     *
     * @param {Object} param 接口参数
     * @param {String} key 对应参数的key
     */
    checkParamKeyEmpty(param, key) {
        if (!param[key]) {
            var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, `${ERROR_CONF.PARAM_ERR_MSG + key}参数格式有误`);

            param.fail(errconf);
            param.complete(errconf);
            return true;
        }
        return false;
    },

    /**
     * 计算角度
     */
    rad(d) {
        return d * Math.PI / 180.0;
    },

    /**
     * 得到终点query字符串
     * @param {Array|String} 检索数据
     */
    location2query(data) {
        if (typeof data === 'string') {
            return data;
        }
        let query = '';

        for (let i = 0; i < data.length; i++) {
            var d = data[i];

            if (query) {
                query += ';';
            }
            if (d.location) {
                query = `${query + d.location.lat},${d.location.lng}`;
            }
            if (d.latitude && d.longitude) {
                query = `${query + d.latitude},${d.longitude}`;
            }
        }
        return query;
    },

    /**
     * 处理终点location数组
     * @return 返回终点数组
     */
    getEndLocation(location){
        var to = location.split(';');
        var endLocation = [];

        for (let i = 0; i < to.length; i++) {
            endLocation.push({
                lat: parseFloat(to[i].split(',')[0]),
                lng: parseFloat(to[i].split(',')[1])
            });
        }
        return endLocation;
    },

    /**
     * 计算两点间直线距离
     * @param a 表示纬度差
     * @param b 表示经度差
     * @return 返回的是距离,单位m
     */
    getDistance(latFrom, lngFrom, latTo, lngTo) {
        var radLatFrom = this.rad(latFrom);
        var radLatTo = this.rad(latTo);
        var a = radLatFrom - radLatTo;
        var b = this.rad(lngFrom) - this.rad(lngTo);
        let distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2)));

        distance *= EARTH_RADIUS;
        distance = Math.round(distance * 10000) / 10000;
        return parseFloat(distance.toFixed(0));
    },

    /**
     * 获取location参数
     */
    getLocationParam(location) {
        if (typeof location === 'string') {
            var locationArr = location.split(',');

            if (locationArr.length === 2) {
                location = {
                    latitude: location.split(',')[0],
                    longitude: location.split(',')[1]
                };
            } else {
                location = {};
            }
        }
        return location;
    },

    /**
     * 验证location值
     *
     * @param {Object} param 接口参数
     */
    checkLocation(param) {
        var location = this.getLocationParam(param.location);

        if (!location || !location.latitude || !location.longitude) {
            var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, `${ERROR_CONF.PARAM_ERR_MSG} location参数格式有误`);

            param.fail(errconf);
            param.complete(errconf);
            return false;
        }
        return true;
    },

    /**
     * 处理用户参数是否传入坐标进行不同的处理
     */
    locationProcess(param, locationsuccess, locationfail, locationcomplete) {
        var that = this;

        locationfail = locationfail || function (res) {
            res.statusCode = ERROR_CONF.WX_ERR_CODE;
            param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
        };
        locationcomplete = locationcomplete || function (res) {
            if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
                param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
            }
        };
        if (!param.location) {
            that.getWXLocation(locationsuccess, locationfail, locationcomplete);
        } else if (that.checkLocation(param)) {
            var location = Utils.getLocationParam(param.location);

            locationsuccess(location);
        }
    }
};

var MapUtils = {

    /**
     *
     * 新增直线距离计算。
     *
     * @param {Object} options 接口参数对象
     *
     */
    calculateDistance(options) {

        options = options || {};
        Utils.polyfillParam(options);

        if (Utils.checkParamKeyEmpty(options, 'to')) {
            return;
        }

        var requestParam = {
            mode: options.mode || 'walking',
            to: Utils.location2query(options.to),
            output: 'json',
        };

        if (options.from) {
            options.location = options.from;
        }

        // 计算直线距离
        var locationsuccess = function (result) {
            var locationTo = Utils.getEndLocation(requestParam.to);// 处理终点坐标
            var data = {
                message: 'query ok',
                result: {
                    elements: []
                },
                status: 0
            };

            for (var i = 0; i < locationTo.length; i++) {
                data.result.elements.push({// 将坐标存入
                    distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng),
                    duration: 0,
                    from: {
                        lat: result.latitude,
                        lng: result.longitude
                    },
                    to: {
                        lat: locationTo[i].lat,
                        lng: locationTo[i].lng
                    }
                });
            }
            var calculateResult = data.result.elements;
            var distanceResult = [];

            for (var i = 0; i < calculateResult.length; i++) {
                distanceResult.push(calculateResult[i].distance);
            }
            return options.success(data, {
                calculateResult,
                distanceResult
            });
        };

        Utils.locationProcess(options, locationsuccess);
    }

};

export default MapUtils;

xianzou avatar Jun 23 '22 07:06 xianzou