148 lines
4.7 KiB
JavaScript
148 lines
4.7 KiB
JavaScript
|
|
// +----------------------------------------------------------------------
|
|||
|
|
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
|||
|
|
// +----------------------------------------------------------------------
|
|||
|
|
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
|
|||
|
|
// +----------------------------------------------------------------------
|
|||
|
|
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
|||
|
|
// +----------------------------------------------------------------------
|
|||
|
|
// | Author: CRMEB Team <admin@crmeb.com>
|
|||
|
|
// +----------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 地理围栏工具类
|
|||
|
|
* 提供根据经纬度判断是否在多边形范围内的功能
|
|||
|
|
*/
|
|||
|
|
const geoUtil = {
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 判断点是否在多边形内部(射线法)
|
|||
|
|
* @param {Object} point 待判断的点 {lng: 经度, lat: 纬度}
|
|||
|
|
* @param {Array} polygon 多边形顶点数组 [{lng: 经度1, lat: 纬度1}, {lng: 经度2, lat: 纬度2}, ...]
|
|||
|
|
* @returns {Boolean} 是否在多边形内部
|
|||
|
|
*/
|
|||
|
|
isPointInPolygon: function(point, polygon) {
|
|||
|
|
if (!point || !polygon || !Array.isArray(polygon) || polygon.length < 3) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let inside = false;
|
|||
|
|
const x = point.lng;
|
|||
|
|
const y = point.lat;
|
|||
|
|
|
|||
|
|
// 射线法实现
|
|||
|
|
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|||
|
|
const xi = polygon[i].lng;
|
|||
|
|
const yi = polygon[i].lat;
|
|||
|
|
const xj = polygon[j].lng;
|
|||
|
|
const yj = polygon[j].lat;
|
|||
|
|
|
|||
|
|
// 判断点是否在多边形的边上
|
|||
|
|
if (this.isPointOnLine(point, {lng: xi, lat: yi}, {lng: xj, lat: yj})) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 判断点是否与多边形边界相交
|
|||
|
|
const intersect = ((yi > y) !== (yj > y)) &&
|
|||
|
|
(x < (xj - xi) * (y - yi) / (yj - yi) + xi);
|
|||
|
|
|
|||
|
|
if (intersect) {
|
|||
|
|
inside = !inside;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return inside;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 判断点是否在线段上
|
|||
|
|
* @param {Object} point 待判断的点
|
|||
|
|
* @param {Object} lineStart 线段起点
|
|||
|
|
* @param {Object} lineEnd 线段终点
|
|||
|
|
* @returns {Boolean} 是否在线段上
|
|||
|
|
*/
|
|||
|
|
isPointOnLine: function(point, lineStart, lineEnd) {
|
|||
|
|
// 计算点到线段的距离
|
|||
|
|
const distance = this.pointToLineDistance(point, lineStart, lineEnd);
|
|||
|
|
|
|||
|
|
// 如果距离非常小(接近0),且点在线段的包围盒内,则认为点在线段上
|
|||
|
|
return distance < 0.000001 &&
|
|||
|
|
point.lng >= Math.min(lineStart.lng, lineEnd.lng) &&
|
|||
|
|
point.lng <= Math.max(lineStart.lng, lineEnd.lng) &&
|
|||
|
|
point.lat >= Math.min(lineStart.lat, lineEnd.lat) &&
|
|||
|
|
point.lat <= Math.max(lineStart.lat, lineEnd.lat);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算点到线段的距离
|
|||
|
|
* @param {Object} point 点
|
|||
|
|
* @param {Object} lineStart 线段起点
|
|||
|
|
* @param {Object} lineEnd 线段终点
|
|||
|
|
* @returns {Number} 距离
|
|||
|
|
*/
|
|||
|
|
pointToLineDistance: function(point, lineStart, lineEnd) {
|
|||
|
|
const A = point.lng - lineStart.lng;
|
|||
|
|
const B = point.lat - lineStart.lat;
|
|||
|
|
const C = lineEnd.lng - lineStart.lng;
|
|||
|
|
const D = lineEnd.lat - lineStart.lat;
|
|||
|
|
|
|||
|
|
const dot = A * C + B * D;
|
|||
|
|
const lenSq = C * C + D * D;
|
|||
|
|
let param = -1;
|
|||
|
|
|
|||
|
|
if (lenSq !== 0) {
|
|||
|
|
param = dot / lenSq;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let xx, yy;
|
|||
|
|
|
|||
|
|
if (param < 0) {
|
|||
|
|
xx = lineStart.lng;
|
|||
|
|
yy = lineStart.lat;
|
|||
|
|
} else if (param > 1) {
|
|||
|
|
xx = lineEnd.lng;
|
|||
|
|
yy = lineEnd.lat;
|
|||
|
|
} else {
|
|||
|
|
xx = lineStart.lng + param * C;
|
|||
|
|
yy = lineStart.lat + param * D;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const dx = point.lng - xx;
|
|||
|
|
const dy = point.lat - yy;
|
|||
|
|
|
|||
|
|
// 这里返回的是欧几里得距离,在实际地理应用中可能需要转换为实际距离(如米)
|
|||
|
|
return Math.sqrt(dx * dx + dy * dy);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算两个经纬度点之间的距离(Haversine公式)
|
|||
|
|
* @param {Object} point1 第一个点
|
|||
|
|
* @param {Object} point2 第二个点
|
|||
|
|
* @returns {Number} 两点之间的距离(单位:米)
|
|||
|
|
*/
|
|||
|
|
getDistance: function(point1, point2) {
|
|||
|
|
const R = 6371000; // 地球半径(米)
|
|||
|
|
const φ1 = this.toRadians(point1.lat);
|
|||
|
|
const φ2 = this.toRadians(point2.lat);
|
|||
|
|
const Δφ = this.toRadians(point2.lat - point1.lat);
|
|||
|
|
const Δλ = this.toRadians(point2.lng - point1.lng);
|
|||
|
|
|
|||
|
|
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
|
|||
|
|
Math.cos(φ1) * Math.cos(φ2) *
|
|||
|
|
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
|
|||
|
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|||
|
|
|
|||
|
|
const distance = R * c; // 距离(米)
|
|||
|
|
return distance;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 角度转弧度
|
|||
|
|
* @param {Number} degrees 角度
|
|||
|
|
* @returns {Number} 弧度
|
|||
|
|
*/
|
|||
|
|
toRadians: function(degrees) {
|
|||
|
|
return degrees * (Math.PI / 180);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default geoUtil;
|