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

如何在Google Directions API(alternatives=true)中筛选多类型路线并修复代码

解决Google Directions API多路线筛选与显示问题

看起来你已经成功调用了带alternatives=true的Directions API,拿到了3条路线,但现有代码因为循环中重复覆盖PolylineOptions,最终只显示了最后一条路线。接下来我们一步步实现最快路线、最短路线、避收费路线的筛选功能,同时优化代码结构让它更易维护。

第一步:创建RouteModel封装路线信息

首先,我们创建一个数据类来封装每条路线的关键信息,避免用HashMap带来的混乱:

public class RouteModel {
    public String distanceText;
    public String durationText;
    public int distanceValue; // 距离数值(米),用于排序最短路线
    public int durationValue; // 时长数值(秒),用于排序最快路线
    public boolean hasTolls; // 是否包含收费路段
    public List<LatLng> pathPoints; // 路线的所有坐标点

    public RouteModel() {
        pathPoints = new ArrayList<>();
    }
}

第二步:修改DirectionsJSONParser解析完整路线信息

原来的Parser只提取了部分信息,现在我们要补充解析距离/时长的数值、是否收费(通过检查路线步骤中的toll_road字段):

public class DirectionsJSONParser {

    public List<RouteModel> parse(JSONObject jObject) {
        List<RouteModel> routeList = new ArrayList<>();
        JSONArray jRoutes = null;
        JSONArray jLegs = null;
        JSONArray jSteps = null;

        try {
            jRoutes = jObject.getJSONArray("routes");

            // 遍历每条路线
            for (int i = 0; i < jRoutes.length(); i++) {
                RouteModel route = new RouteModel();
                JSONObject jRoute = jRoutes.getJSONObject(i);
                jLegs = jRoute.getJSONArray("legs");
                boolean hasTolls = false;

                // 遍历路线的每个Leg
                for (int j = 0; j < jLegs.length(); j++) {
                    JSONObject jLeg = jLegs.getJSONObject(j);

                    // 解析距离信息
                    JSONObject jDistance = jLeg.getJSONObject("distance");
                    route.distanceText = jDistance.getString("text");
                    route.distanceValue = jDistance.getInt("value");

                    // 解析时长信息
                    JSONObject jDuration = jLeg.getJSONObject("duration");
                    route.durationText = jDuration.getString("text");
                    route.durationValue = jDuration.getInt("value");

                    // 检查是否包含收费路段
                    jSteps = jLeg.getJSONArray("steps");
                    for (int k = 0; k < jSteps.length(); k++) {
                        JSONObject jStep = jSteps.getJSONObject(k);
                        if (jStep.has("toll_road") && jStep.getBoolean("toll_road")) {
                            hasTolls = true;
                            break;
                        }
                    }
                    route.hasTolls = hasTolls;

                    // 解析路线坐标点
                    for (int k = 0; k < jSteps.length(); k++) {
                        JSONObject jStep = jSteps.getJSONObject(k);
                        JSONObject polylineObj = jStep.getJSONObject("polyline");
                        String polyline = polylineObj.getString("points");
                        List<LatLng> points = decodePoly(polyline);
                        route.pathPoints.addAll(points);
                    }
                }
                routeList.add(route);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return routeList;
    }

    // 保留原来的decodePoly方法
    private List<LatLng> decodePoly(String encoded) {
        List<LatLng> poly = new ArrayList<>();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng point = new LatLng((double) lat / 1E5, (double) lng / 1E5);
            poly.add(point);
        }
        return poly;
    }
}

第三步:修改AsyncTask处理解析结果

原来的AsyncTask返回的是List<List<HashMap>>,现在改成返回List<RouteModel>,并实现筛选逻辑:

// 修改AsyncTask的泛型参数
private class FetchDirectionsTask extends AsyncTask<String, Void, List<RouteModel>> {
    private List<RouteModel> routeList; // 保存路线列表用于UI切换

    @Override
    protected List<RouteModel> doInBackground(String... strings) {
        JSONObject jsonObject;
        try {
            jsonObject = new JSONObject(strings[0]);
            DirectionsJSONParser directionsJSONParser = new DirectionsJSONParser();
            routeList = directionsJSONParser.parse(jsonObject);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return routeList;
    }

    @Override
    protected void onPostExecute(List<RouteModel> routes) {
        if (routes == null || routes.isEmpty()) {
            Toast.makeText(MapActivity.this, "Directions not found!", Toast.LENGTH_SHORT).show();
            return;
        }
        routeList = routes;
        // 默认显示最快路线
        displaySelectedRoute(getFastestRoute(routeList));
        // 初始化UI按钮(可以放到Activity onCreate里)
        initRouteSwitchButtons();
    }

    // 获取最快路线:时长数值最小的
    private RouteModel getFastestRoute(List<RouteModel> routeList) {
        RouteModel fastest = routeList.get(0);
        for (RouteModel route : routeList) {
            if (route.durationValue < fastest.durationValue) {
                fastest = route;
            }
        }
        return fastest;
    }

    // 获取最短路线:距离数值最小的
    private RouteModel getShortestRoute(List<RouteModel> routeList) {
        RouteModel shortest = routeList.get(0);
        for (RouteModel route : routeList) {
            if (route.distanceValue < shortest.distanceValue) {
                shortest = route;
            }
        }
        return shortest;
    }

    // 获取避收费路线:优先选最快的无收费路线,没有的话返回null
    private RouteModel getTollFreeRoute(List<RouteModel> routeList) {
        List<RouteModel> tollFreeRoutes = new ArrayList<>();
        for (RouteModel route : routeList) {
            if (!route.hasTolls) {
                tollFreeRoutes.add(route);
            }
        }
        if (tollFreeRoutes.isEmpty()) {
            Toast.makeText(MapActivity.this, "No toll-free routes available!", Toast.LENGTH_SHORT).show();
            return null;
        }
        return getFastestRoute(tollFreeRoutes);
    }

    // 绘制选中的路线
    private void displaySelectedRoute(RouteModel route) {
        if (route == null) return;

        // 清除之前的路线,避免重叠
        mMap.clear();

        PolylineOptions polylineOptions = new PolylineOptions();
        polylineOptions.addAll(route.pathPoints);
        polylineOptions.width(12);
        polylineOptions.color(Color.RED);
        polylineOptions.geodesic(true);

        mMap.addPolyline(polylineOptions);

        // 调整地图视野到路线范围
        LatLngBounds.Builder boundsBuilder = new LatLngBounds.Builder();
        for (LatLng point : route.pathPoints) {
            boundsBuilder.include(point);
        }
        LatLngBounds routeBounds = boundsBuilder.build();
        mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(routeBounds, 12));

        // 更新距离和时长显示
        mDisTextView.setText(route.distanceText);
        mDuraTextView.setText(route.durationText);
    }

    // 初始化路线切换按钮
    private void initRouteSwitchButtons() {
        Button btnFastest = findViewById(R.id.btn_fastest);
        Button btnShortest = findViewById(R.id.btn_shortest);
        Button btnTollFree = findViewById(R.id.btn_toll_free);

        btnFastest.setOnClickListener(v -> displaySelectedRoute(getFastestRoute(routeList)));
        btnShortest.setOnClickListener(v -> displaySelectedRoute(getShortestRoute(routeList)));
        btnTollFree.setOnClickListener(v -> displaySelectedRoute(getTollFreeRoute(routeList)));
    }
}

关键问题修复说明

  1. 原有代码只显示一条路线的原因:在onPostExecute的循环中,每次都会重新创建polylineOptions和points,最后只添加了最后一次循环的Polyline,所以只显示最后一条路线。现在我们先筛选出目标路线,再单独绘制它。
  2. 筛选逻辑的核心:通过解析API返回的distance.value(米)和duration.value(秒)来做数值比较,比文本更准确;通过检查步骤中的toll_road字段判断是否有收费路段。

内容的提问来源于stack exchange,提问作者Samuel Teo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:45:28