You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

判断路线是否含指定坐标及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代码

  1. 先在Activity里定义一个列表,存储从Firebase获取的交通灯坐标:
private List<LatLng> trafficLightPoints = new ArrayList<>();
  1. 改写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();
        }
    });
}
  1. 添加检查方法,遍历交通灯坐标判断是否在路线上:
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();
        }
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:08:35