资讯详情

资讯详情

计算以点为圆心半径R的站点信息列表

下面给出一个完整的 Java 实现根据当前点的经纬度对 API 返回的所有站点计算距离筛选出 10 公里内的站点并按距离由近到远排序。一、定义站点模型javaData public class Station { private Long id; private String name; private Double longitude; // 经度 private Double latitude; // 纬度 }返回结果可以复用站点模型额外加一个距离字段javaData public class StationDistanceVO { private Long id; private String name; private Double longitude; private Double latitude; private Double distanceKm; // 距离公里 }二、Haversine 距离工具类javapublic class GeoUtils { private static final double EARTH_RADIUS_KM 6371.0; /** * 计算两个经纬度之间的距离单位公里 */ public static double distanceKm(double lat1, double lng1, double lat2, double lng2) { double dLat Math.toRadians(lat2 - lat1); double dLng Math.toRadians(lng2 - lng1); double a Math.sin(dLat / 2) * Math.sin(dLat / 2) Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLng / 2) * Math.sin(dLng / 2); double c 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return EARTH_RADIUS_KM * c; } }三、核心计算方法javapublic ListStationDistanceVO findNearbyStations(double currentLat, double currentLng, ListStation allStations, double radiusKm) { if (allStations null || allStations.isEmpty()) { return Collections.emptyList(); } return allStations.stream() // 过滤掉经纬度为空的脏数据 .filter(s - s.getLatitude() ! null s.getLongitude() ! null) // 计算距离 .map(s - { double distance GeoUtils.distanceKm( currentLat, currentLng, s.getLatitude(), s.getLongitude() ); StationDistanceVO vo new StationDistanceVO(); vo.setId(s.getId()); vo.setName(s.getName()); vo.setLongitude(s.getLongitude()); vo.setLatitude(s.getLatitude()); vo.setDistanceKm(distance); return vo; }) // 过滤出半径内的站点 .filter(vo - vo.getDistanceKm() radiusKm) // 按距离升序排序 .sorted(Comparator.comparingDouble(StationDistanceVO::getDistanceKm)) .collect(Collectors.toList()); }四、调用示例假设你从 API 拿到了站点列表java// 当前点经纬度 double currentLat 39.909; double currentLng 116.397; // API 返回的所有站点 ListStation allStations stationApi.getAllStations(); // 计算 10 公里内的站点按距离排序 ListStationDistanceVO nearbyList findNearbyStations( currentLat, currentLng, allStations, 10.0 ); // 输出 nearbyList.forEach(vo - System.out.println(vo.getName() - vo.getDistanceKm() km) );五、如果需要分页分页只需在排序后截取子列表javaint page 1; int size 20; int from (page - 1) * size; int to Math.min(from size, nearbyList.size()); ListStationDistanceVO pageList nearbyList.subList(from, to); int total nearbyList.size();或者直接返回全部由前端分页。数据量不大时推荐前端分页避免多次计算。六、性能优化建议边界框预过滤如果站点数量很大上万先用矩形范围缩小候选集再精确计算 Haversine。javadouble latDelta radiusKm / 111.0; double lngDelta radiusKm / (111.0 * Math.cos(Math.toRadians(currentLat))); ListStation candidates allStations.stream() .filter(s - Math.abs(s.getLatitude() - currentLat) latDelta Math.abs(s.getLongitude() - currentLng) lngDelta) .collect(Collectors.toList());然后再对 candidates 计算精确距离。并行流站点特别多时可以用parallelStream()加速距离计算但要注意线程安全和结果顺序。排序后再collect顺序仍然可控。缓存如果当前点经常重复比如热门商圈可以把结果缓存到 Redis设置短 TTL。数据库层计算如果站点在 MySQL 里直接用 SQL 的 Haversine 公式 边界框 空间索引减少数据传输量。七、注意事项经纬度顺序Haversine 公式参数是(lat1, lng1, lat2, lng2)别传反。空值处理站点经纬度可能为 null必须过滤否则计算会 NPE。距离单位上面返回的是公里如需米乘以 1000。浮点误差Math.sqrt(a)中 a 可能因浮点误差略大于 1导致sqrt为 NaN。可以用Math.min(1, a)保护javadouble c 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - Math.min(1, a)));排序稳定性距离相同的站点可以再按 id 排序保证结果可复现。八、完整工具类javapublic class NearbyStationUtils { private static final double EARTH_RADIUS_KM 6371.0; public static ListStationDistanceVO findNearby(double lat, double lng, ListStation stations, double radiusKm) { if (stations null || stations.isEmpty()) { return Collections.emptyList(); } return stations.stream() .filter(s - s.getLatitude() ! null s.getLongitude() ! null) .map(s - { double d distanceKm(lat, lng, s.getLatitude(), s.getLongitude()); StationDistanceVO vo new StationDistanceVO(); vo.setId(s.getId()); vo.setName(s.getName()); vo.setLongitude(s.getLongitude()); vo.setLatitude(s.getLatitude()); vo.setDistanceKm(d); return vo; }) .filter(vo - vo.getDistanceKm() radiusKm) .sorted(Comparator.comparingDouble(StationDistanceVO::getDistanceKm)) .collect(Collectors.toList()); } public static double distanceKm(double lat1, double lng1, double lat2, double lng2) { double dLat Math.toRadians(lat2 - lat1); double dLng Math.toRadians(lng2 - lng1); double a Math.sin(dLat / 2) * Math.sin(dLat / 2) Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLng / 2) * Math.sin(dLng / 2); a Math.min(1, Math.max(-1, a)); double c 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return EARTH_RADIUS_KM * c; } }九、总结核心步骤拿到当前点经纬度和站点列表遍历站点用 Haversine 公式计算距离过滤掉超过 10 公里的按距离升序排序返回结果可分页。如果站点很多先用边界框缩小范围再精确计算。注意空值、经纬度顺序和浮点误差保护。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →