// +---------------------------------------------------------------------- // | CRMEB [ CRMEB赋能开发者,助力企业发展 ] // +---------------------------------------------------------------------- // | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved. // +---------------------------------------------------------------------- // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权 // +---------------------------------------------------------------------- // | Author: CRMEB Team // +---------------------------------------------------------------------- /** * 地理围栏工具类 * 提供根据经纬度判断是否在多边形范围内的功能 */ 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;