判断路线是否含指定坐标及Google Places API路线一致性问题
解决方案:检查路线是否包含特定坐标 & API路线一致性说明
我来帮你搞定这两个问题,先从核心的「判断坐标是否在路线上」说起,再聊聊API路线的一致性问题。
一、实现坐标是否在路线上的检测
你已经通过Directions库拿到了路线的所有坐标点,接下来只需要计算目标点(从Firebase获取的交通灯坐标)到路线线段的最短距离,只要这个距离小于你设定的阈值(比如50米,可根据需求调整),就可以认为这个点在路线上。
第一步:添加工具类计算距离
先写一个工具类,封装球面距离计算和点到线段的最短距离逻辑:
public class MapUtils { // 地球半径(单位:米) private static final double EARTH_RADIUS = 6371000; // 计算两点间的球面直线距离 public static double calculateDistance(LatLng point1, LatLng point2) { double lat1 = Math.toRadians(point1.latitude); double lon1 = Math.toRadians(point1.longitude); double lat2 = Math.toRadians(point2.latitude); double lon2 = Math.toRadians(point2.longitude); double dLat = lat2 - lat1; double dLon = lon2 - lon1; double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) * Math.sin(dLon / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return EARTH_RADIUS * c; } // 计算点到路线线段的最短距离 public static double distanceToSegment(LatLng targetPoint, LatLng segmentStart, LatLng segmentEnd) { double segmentLength = calculateDistance(segmentStart, segmentEnd); if (segmentLength == 0) { return calculateDistance(targetPoint, segmentStart); } // 计算投影点在segment上的比例 double t = ((targetPoint.latitude - segmentStart.latitude) * (segmentEnd.latitude - segmentStart.latitude) + (targetPoint.longitude - segmentStart.longitude) * (segmentEnd.longitude - segmentStart.longitude)) / (segmentLength * segmentLength); // 把比例限制在0-1之间(避免投影点超出线段范围) t = Math.max(0, Math.min(1, t)); // 计算投影点坐标 LatLng projectionPoint = new LatLng( segmentStart.latitude + t * (segmentEnd.latitude - segmentStart.latitude), segmentStart.longitude + t * (segmentEnd.longitude - segmentStart.longitude) ); return calculateDistance(targetPoint, projectionPoint); } // 判断点是否在路线附近(threshold为距离阈值,单位米) public static boolean isPointOnRoute(LatLng targetPoint, List<LatLng> routePoints, double threshold) { for (int i = 0; i < routePoints.size() - 1; i++) { LatLng segmentStart = routePoints.get(i); LatLng segmentEnd = routePoints.get(i + 1); double distance = distanceToSegment(targetPoint, segmentStart, segmentEnd); if (distance <= threshold) { return true; } } return false; } }
第二步:修改你的PathActivity代码
- 先在Activity里定义一个列表,存储从Firebase获取的交通灯坐标:
private List<LatLng> trafficLightPoints = new ArrayList<>();
- 改写
fetchAllTrafficLights方法,把获取到的坐标存入列表:
public void fetchAllTrafficLights() { pd.show(); firestore.collection("Controller").get().addOnCompleteListener(this, new OnCompleteListener<QuerySnapshot>() { @Override public void onComplete(Task<QuerySnapshot> task) { if(task.isSuccessful()) { trafficLightPoints.clear(); // 清空旧数据 for(QueryDocumentSnapshot documentSnapshot : task.getResult()) { double lat = Double.parseDouble(documentSnapshot.get("controllerLatitude").toString()); double lng = Double.parseDouble(documentSnapshot.get("controllerLongitude").toString()); trafficLightPoints.add(new LatLng(lat, lng)); Log.i("Hello", lat + " " + lng); } pd.dismiss(); // 如果地图和路线已加载,直接触发检查 if (mMap != null && !trafficLightPoints.isEmpty() && !polylines.isEmpty()) { checkTrafficLightsOnRoute(); } } } }) .addOnFailureListener(this, new OnFailureListener() { @Override public void onFailure(Exception e) { Toast.makeText(PathActivity.this, "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show(); pd.dismiss(); } }); }
- 添加检查方法,遍历交通灯坐标判断是否在路线上:
private void checkTrafficLightsOnRoute() { // 取最短路线(对应你代码里的shortestRouteIndex) Route shortestRoute = routeList.get(shortestRouteIndex); List<LatLng> routePoints = shortestRoute.getPoints(); double distanceThreshold = 50; // 50米阈值,可根据实际需求调整 for (LatLng lightPoint : trafficLightPoints) { boolean isOnRoute = MapUtils.isPointOnRoute(lightPoint, routePoints, distanceThreshold); if (isOnRoute) { // 做点在路线上的处理,比如添加标记 MarkerOptions markerOptions = new MarkerOptions() .position(lightPoint) .icon(BitmapDescriptorFactory.fromResource(R.drawable.traffic_light_marker)); // 替换成你的交通灯图标 mMap.addMarker(markerOptions); Toast.makeText(this, "发现路线上的交通灯:" + lightPoint.latitude + "," + lightPoint.longitude, Toast.LENGTH_SHORT).show(); } } }
- 在
onRoutingSuccess方法末尾,添加触发检查的逻辑:
@Override public void onRoutingSuccess(ArrayList<Route> routeList, int shortestRouteIndex) { pd.dismiss(); mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(start, 16)); if(polylines.size()>0) { for (Polyline poly : polylines) { poly.remove(); } } polylines = new ArrayList<>(); //add route(s) to the map. for (int i = 0; i <routeList.size(); i++) { //In case of more than 5 alternative routes int colorIndex = i % COLORS.length; PolylineOptions polyOptions = new PolylineOptions(); polyOptions.color(getResources().getColor(COLORS[colorIndex])); polyOptions.width(10 + i * 3); polyOptions.addAll(routeList.get(i).getPoints()); Polyline polyline = mMap.addPolyline(polyOptions); polylines.add(polyline); Toast.makeText(getApplicationContext(),"Route "+ (i+1) +": distance - "+ routeList.get(i).getDistanceValue()+": duration - "+ routeList.get(i).getDurationValue(),Toast.LENGTH_SHORT).show(); // Start marker MarkerOptions options = new MarkerOptions(); options.position(start); options.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_start_blue)); mMap.addMarker(options); // End marker options = new MarkerOptions(); options.position(end); options.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_end_green)); mMap.addMarker(options); } // 如果交通灯数据已加载,检查是否在路线上 if (!trafficLightPoints.isEmpty()) { checkTrafficLightsOnRoute(); } }
二、Google Directions API路线坐标的一致性说明
直接给结论:API返回的路线坐标并不是始终完全一致的,主要原因有这些:
- 实时交通影响:如果你的请求包含实时交通参数(比如设置了
departure_time为当前时间),API会根据实时拥堵调整路线,导致坐标变化。 - 路线偏好差异:即使起点终点相同,选择不同的出行模式(驾车/步行/骑行)、避开收费/高速路等偏好,返回的路线会完全不同。
- 地图数据更新:Google会定期更新道路网络数据(比如新建道路、封闭路段),或者优化路由算法,都会导致路线变化。
如果需要相对稳定的路线,可以这么做:
- 固定
departure_time为一个过去的固定时间戳,避开实时交通的影响。 - 固定所有路线偏好参数(出行模式、避障选项等),确保每次请求的条件一致。
- 缓存第一次获取的路线坐标,后续直接使用缓存(但要注意定期更新缓存,避免道路变化导致路线过时)。
内容的提问来源于stack exchange,提问作者Raj
相关产品推荐
相关产品推荐

