一切就绪!

着手开发前,请先阅读我们的开发者文档

激活 Google Maps Roads API

为帮助您起步,我们将引导您在 Google Developers Console 中先完成几项任务:

  1. 创建或选择项目
  2. 激活 Google Maps Roads API
  3. 创建相应密钥
继续

高级概念

注:下面的示例使用 Java Client for Google Maps Services 中的 Google Maps Roads API。 您可以根据自己选择的语言调整其概念。 GitHub 上也提供了 Python ClientGo ClientNode.js Client

获得数据

可以通过多种方式获得收集的位置数据。 我们将在这里介绍两种方法,用于获得供 Google Maps Roads API道路吸附功能使用的数据。

GPX

GPX 是一种基于 Open XML 的格式,用于共享由 GPS 设备采集的路线、轨迹和路径点。 此示例使用的是 XmlPull 解析器,它是一种可同时用于 Java 服务器和移动环境的轻量级 XML 解析器。

/**
 * Parses the waypoint (wpt tags) data into native objects from a GPX stream.
 */
private List<LatLng> loadGpxData(XmlPullParser parser, InputStream gpxIn)
        throws XmlPullParserException, IOException {
    // We use a List<> as we need subList for paging later
    List<LatLng> latLngs = new ArrayList<>();
    parser.setInput(gpxIn, null);
    parser.nextTag();

    while (parser.next() != XmlPullParser.END_DOCUMENT) {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            continue;
        }

        if (parser.getName().equals("wpt")) {
            // Save the discovered latitude/longitude attributes in each <wpt>.
            latLngs.add(new LatLng(
                    Double.valueOf(parser.getAttributeValue(null, "lat")),
                    Double.valueOf(parser.getAttributeValue(null, "lon"))));
        }
        // Otherwise, skip irrelevant data
    }

    return latLngs;
}

以下是一些加载到地图上的原始 GPX 数据。

地图上的原始 GPX 数据

Android 位置服务

从 Android 设备采集 GPS 数据的最佳方式因您的用例而异。 请参阅有关接收位置更新的 Android 培训课程,以及 GitHub 上的 Google Play 位置示例

处理长路径

由于道路吸附功能是根据完整路径而不是单个点推测位置,因此您需要谨慎处理长路径(即,超过 100 点/请求限制的路径)。

为将单个请求视为一条长路径,您应该加入一定程度的重叠,使得上一请求的最终点以起始点形式包括在后续请求内。

包括的点数量将取决于数据的精确度。 请求的精确度越低,包括的点数量应越多。

此示例利用 Java Client for Google Maps Services 发送“分页”请求,然后将数据(包括内插点)重新连入返回的列表。

/**
 * Snaps the points to their most likely position on roads using the Roads API.
 */
private List<SnappedPoint> snapToRoads(GeoApiContext context) throws Exception {
    List<SnappedPoint> snappedPoints = new ArrayList<>();

    int offset = 0;
    while (offset < mCapturedLocations.size()) {
        // Calculate which points to include in this request. We can't exceed the API's
        // maximum and we want to ensure some overlap so the API can infer a good location for
        // the first few points in each request.
        if (offset > 0) {
            offset -= PAGINATION_OVERLAP;   // Rewind to include some previous points.
        }
        int lowerBound = offset;
        int upperBound = Math.min(offset + PAGE_SIZE_LIMIT, mCapturedLocations.size());

        // Get the data we need for this page.
        LatLng[] page = mCapturedLocations
                .subList(lowerBound, upperBound)
                .toArray(new LatLng[upperBound - lowerBound]);

        // Perform the request. Because we have interpolate=true, we will get extra data points
        // between our originally requested path. To ensure we can concatenate these points, we
        // only start adding once we've hit the first new point (that is, skip the overlap).
        SnappedPoint[] points = RoadsApi.snapToRoads(context, true, page).await();
        boolean passedOverlap = false;
        for (SnappedPoint point : points) {
            if (offset == 0 || point.originalIndex >= PAGINATION_OVERLAP - 1) {
                passedOverlap = true;
            }
            if (passedOverlap) {
                snappedPoints.add(point);
            }
        }

        offset = upperBound;
    }

    return snappedPoints;
}

以下是运行道路吸附请求后由以上示例生成的数据。 红线是原始数据,蓝线是吸附数据。

已吸附至道路的数据示例

配额的高效利用

道路吸附请求的响应包括地点 ID 列表,其中的 ID 对应于您提供的点,如果您设置 interpolate=true,则还可能对应于其他点。

为高效利用速度限制请求允许的配额,您只应在请求中查询唯一的地点 ID。 此示例利用 Java Client for Google Maps Services 从某个地点 ID 列表查询速度限制。

/**
 * Retrieves speed limits for the previously-snapped points. This method is efficient in terms
 * of quota usage as it will only query for unique places.
 *
 * Note: Speed limit data is only available for requests using an API key enabled for a
 * Google Maps APIs Premium Plan license.
 */
private Map<String, SpeedLimit> getSpeedLimits(GeoApiContext context, List<SnappedPoint> points)
        throws Exception {
    Map<String, SpeedLimit> placeSpeeds = new HashMap<>();

    // Pro tip: Save on quota by filtering to unique place IDs.
    for (SnappedPoint point : points) {
        placeSpeeds.put(point.placeId, null);
    }

    String[] uniquePlaceIds =
            placeSpeeds.keySet().toArray(new String[placeSpeeds.keySet().size()]);

    // Loop through the places, one page (API request) at a time.
    for (int i = 0; i < uniquePlaceIds.length; i += PAGE_SIZE_LIMIT) {
        String[] page = Arrays.copyOfRange(uniquePlaceIds, i,
                Math.min(i + PAGE_SIZE_LIMIT, uniquePlaceIds.length));

        // Execute!
        SpeedLimit[] placeLimits = RoadsApi.speedLimits(context, page).await();
        for (SpeedLimit sl : placeLimits) {
            placeSpeeds.put(sl.placeId, sl);
        }
    }

    return placeSpeeds;
}

以下是由以上示例生成的数据,在每个唯一地点 ID 处都标记了速度限制。

地图上的速度限制标志

与其他 API 互动

道路吸附请求响应中返回地点 ID 的其中一个优点是:您可以跨多种 Google Maps API 使用地点 ID。

此示例利用 Java Client for Google Maps Services 对从以上道路吸附请求返回的地点进行地理编码。

/**
 * Geocodes a snapped point using the place ID.
 */
private GeocodingResult geocodeSnappedPoint(GeoApiContext context, SnappedPoint point) throws Exception {
    GeocodingResult[] results = GeocodingApi.newRequest(context)
            .place(point.placeId)
            .await();

    if (results.length > 0) {
        return results[0];
    }
    return null;
}

在该示例中,使用来自 Google Maps Geocoding API 的地址对速度限制标记进行了标注。

标记上显示的地理编码地址

示例代码

注意事项

为便于说明,这篇文章中以单个 Android 应用形式提供辅助代码。 在实践中,您不应在 Android 应用中分发您的服务器端 API 密钥,因为这样做将无法保障密钥安全,防范第三方未经授权的访问。

为保障密钥的安全,您应该改为将面向 API 的代码部署为服务器端代理,让您的 Android 应用通过该代理发送请求,从而确保请求获得授权。

下载

GitHub 下载代码。

发送以下问题的反馈:

此网页
Google Maps Roads API
Google Maps Roads API
需要帮助?请访问我们的支持页面