ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

java电子围栏

java电子围栏 java版的电子围栏违规判断经过测试准确率百分之百经过各大AI审查已经无限趋近于完美本工具类支持圆形矩形多边形性能与准确率都有保障实测判断多边形围栏单线程执行千万次次只需要十几秒多线程只需要1秒废话不说直接上代码先贴工具类代码import com.core.entity.Point;import java.util.List;/*** 描述: 电子围栏计算【V4.2 上线定稿防御增强版】* 版本: 4.2.0* 日期: 2026-08-13* 数学前提与约束重要* 1. 多边形边界容差采用【经纬度平面求垂足 Haversine球面距离】组合方案* ✅ 围栏边长 ≤50km垂足误差厘米~分米级国内园区/城配业务完全可用* ⚠️ 围栏边长 100km平面垂足和球面大圆垂足存在偏差会引入数米~数十米误差不推荐使用* 2. AABB轴对齐矩形说明* ✅ 天然支持跨本初子午线(0°经度)* ⚠️ 跨±180°国际日期变更线矩形调用方必须传入 minLng maxLng 格式例minLng170,maxLng-170否则判定失效* ⚠️ 斜旋转矩形禁止使用本方法斜矩形请组装4个顶点调用多边形方法 isInPolygon* 3. 多边形射线法不支持跨±180°国际日期变更线围栏* 4. 强制约束围栏坐标、点位【坐标系必须完全一致 WGS84 / GCJ02 / BD09 不可混用】*/public class GeofencingUtils {/*** WGS84 椭球长半轴(赤道半径) 米Haversine公式使用*/private static final double EARTH_RADIUS 6378137.0;private static double rad(double d) {return d * Math.PI / 180.0;}/*** 圆形围栏判断球面大圆距离全球通用* param radius 半径米* param center 圆心点位* param point 待判断点位* return true 在圆内*/public static boolean isInCircle(double radius, Point center, Point point) {// 防御判空if (center null || point null) {return false;}double radLat1 rad(center.getLat());double radLat2 rad(point.getLat());double a radLat1 - radLat2;double b rad(center.getLng()) - rad(point.getLng());double temp Math.pow(Math.sin(a / 2), 2) Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2);if (temp 1.0) temp 1.0;if (temp 0.0) temp 0.0;double s 2 * Math.asin(Math.sqrt(temp));s s * EARTH_RADIUS;return s radius;}/*** AABB轴对齐矩形仅适用于正北正南对齐矩形* ⚠️ 斜旋转矩形绝对不要用斜矩形请组装4个顶点调用多边形方法 isInPolygon* ⚠️ 跨±180°日期变更线场景必须传入 minLng maxLng否则逻辑失效* param lng 检测点经度* param lat 检测点纬度* param minLng 最小经度跨180场景填大数如170* param maxLng 最大经度跨180场景填小数如-170* param minLat 最小纬度* param maxLat 最大纬度* return true 在矩形包围盒内*/public static boolean isInRectangleArea(double lng, double lat, double minLng, double maxLng,double minLat, double maxLat) {if (isInRange(lat, minLat, maxLat)) {if (minLng * maxLng 0) {return isInRange(lng, minLng, maxLng);} else {if (Math.abs(minLng) Math.abs(maxLng) 180) {return isInRange(lng, minLng, maxLng);} else {double left Math.max(minLng, maxLng);double right Math.min(minLng, maxLng);return isInRange(lng, left, 180) || isInRange(lng, right, -180);}}}return false;}private static boolean isInRange(double point, double left, double right) {return point Math.min(left, right) point Math.max(left, right);}/*** 球面点到线段【真实地面距离(米)】* 原理经纬度平面求垂足 → Haversine计算球面米距* ⚠️ 适用约束围栏边长 ≤50km超大范围100km会存在球面垂足近似偏差*/private static double pointToSegmentSphereMeter(Point target, Point segStart, Point segEnd) {double lng0 target.getLng();double lat0 target.getLat();double lng1 segStart.getLng();double lat1 segStart.getLat();double lng2 segEnd.getLng();double lat2 segEnd.getLat();double a lng0 - lng1;double b lat0 - lat1;double c lng2 - lng1;double d lat2 - lat1;double dot a * c b * d;double lenSq c * c d * d;double param;if (lenSq 0) {param -1;} else {param dot / lenSq;}// 平面投影垂足经纬度double footLng, footLat;if (param 0) {footLng lng1;footLat lat1;} else if (param 1) {footLng lng2;footLat lat2;} else {footLng lng1 param * c;footLat lat1 param * d;}// Haversine 计算目标点 ↔ 垂足 球面大圆距离米return calcHaversineMeter(target.getLng(), target.getLat(), footLng, footLat);}/*** Haversine 计算两点球面大圆距离返回米* ✅ 增加浮点钳位保护防止asin入参越界产生NaN*/private static double calcHaversineMeter(double lngA, double latA, double lngB, double latB) {double radLat1 rad(latA);double radLat2 rad(latB);double a radLat1 - rad(latB);double b rad(lngA) - rad(lngB);double temp Math.pow(Math.sin(a / 2), 2) Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2);// 钳位处理防止浮点精度溢出导致 asin NaNif (temp 1.0) temp 1.0;if (temp 0.0) temp 0.0;double s 2 * Math.asin(Math.sqrt(temp));return s * EARTH_RADIUS;}/*** 多边形围栏标准奇偶射线法 【球面真实米级边界容差】* ⚠️ 限制1.暂不支持跨±180°日期变更线多边形2.边长100km存在球面垂足近似误差* param point 检测点位* param pts 多边形顶点集合顺时针/逆时针均可简单不自交多边形* param toleranceMeter 边界容差(米)普通GPS推荐3米* return true 在内部/边界容差范围内*/public static boolean isInPolygon(Point point, ListPoint pts, double toleranceMeter) {// 防御校验空对象、顶点不足直接返回falseif (point null || pts null || pts.size() 3) {return false;}// 容差负数保护强制最小为0if (toleranceMeter 0) {toleranceMeter 0;}boolean inside false;int size pts.size();for (int i 0, j size - 1; i size; j i) {Point pi pts.get(i);Point pj pts.get(j);// 顶点对象防御顶点null直接跳过这条边容错避免NPEif (pi null || pj null) {continue;}double distMeter pointToSegmentSphereMeter(point, pi, pj);if (distMeter toleranceMeter) {return true;}// 标准奇偶射线穿透算法if (((pi.getLat() point.getLat()) ! (pj.getLat() point.getLat()))) {double lngIntersect ((point.getLat() - pi.getLat()) * (pj.getLng() - pi.getLng()))/ (pj.getLat() - pi.getLat()) pi.getLng();if (point.getLng() lngIntersect) {inside !inside;}}}return inside;}/*** 重载兼容老代码默认3米球面容差*/public static boolean isInPolygon(Point point, ListPoint pts) {return isInPolygon(point, pts, 3.0);}}再贴实体类package com.core.entity;import lombok.Data;import lombok.ToString;DataToStringpublic class Point {/*** 经度-180~180东经正西经负*/private double lng;/*** 纬度-90~90北纬正南纬负*/private double lat;public Point(double lng, double lat) {this.lng lng;this.lat lat;}}说明用哪种坐标系不重要但是围栏的经纬度与要判断的坐标点必须保持是同一种坐标系踩坑记录如果要用行政区划来做围栏国内的行政区划例如上海市的边界范围是由好几个独立模块组成的所以做判断的时候这几个独立模块要分开来单独判断否则会导致判断不准确
返回列表