/***SnapsthepointstotheirmostlikelypositiononroadsusingtheRoadsAPI.*/privateList<SnappedPoint>snapToRoads(GeoApiContextcontext)throwsException{List<SnappedPoint>snappedPoints=newArrayList<>();intoffset=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.}intlowerBound=offset;intupperBound=Math.min(offset+PAGE_SIZE_LIMIT,mCapturedLocations.size());// Get the data we need for this page.LatLng[]page=mCapturedLocations.subList(lowerBound,upperBound).toArray(newLatLng[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();booleanpassedOverlap=false;for(SnappedPointpoint:points){if(offset==0||point.originalIndex>=PAGINATION_OVERLAP-1){passedOverlap=true;}if(passedOverlap){snappedPoints.add(point);}}offset=upperBound;}returnsnappedPoints;}
以下是运行贴合到道路请求后的上述数据。红线是原始数据,蓝线是贴合后的数据。
高效利用配额
对贴合到道路请求的响应包含一个地点 ID 列表,这些 ID 会映射到您提供的点,如果您设置了 interpolate=true,则可能还会包含其他点。
/***Retrievesspeedlimitsforthepreviously-snappedpoints.Thismethodisefficientinterms*ofquotausageasitwillonlyqueryforuniqueplaces.**Note:SpeedlimitdataisonlyavailableforrequestsusinganAPIkeyenabledfora*GoogleMapsAPIsPremiumPlanlicense.*/privateMap<String,SpeedLimit>getSpeedLimits(GeoApiContextcontext,List<SnappedPoint>points)throwsException{Map<String,SpeedLimit>placeSpeeds=newHashMap<>();// Pro tip: Save on quota by filtering to unique place IDs.for(SnappedPointpoint:points){placeSpeeds.put(point.placeId,null);}String[]uniquePlaceIds=placeSpeeds.keySet().toArray(newString[placeSpeeds.keySet().size()]);// Loop through the places, one page (API request) at a time.for(inti=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(SpeedLimitsl:placeLimits){placeSpeeds.put(sl.placeId,sl);}}returnplaceSpeeds;}
[[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["没有我需要的信息","missingTheInformationINeed","thumb-down"],["太复杂/步骤太多","tooComplicatedTooManySteps","thumb-down"],["内容需要更新","outOfDate","thumb-down"],["翻译问题","translationIssue","thumb-down"],["示例/代码问题","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-08-31。"],[[["\u003cp\u003eThis document explains how to use the Google Maps Roads API to snap GPS data to roads, inferring the location based on the full path.\u003c/p\u003e\n"],["\u003cp\u003eIt provides guidance on handling long paths exceeding the API's request limit, emphasizing the need for overlap between requests for accurate location inference.\u003c/p\u003e\n"],["\u003cp\u003eThe document highlights efficient quota usage by querying for speed limits using unique place IDs obtained from the snap to roads response.\u003c/p\u003e\n"],["\u003cp\u003eIt demonstrates the interplay of the Roads API with other Google Maps APIs by geocoding snapped points using their place IDs for address retrieval.\u003c/p\u003e\n"],["\u003cp\u003eThe provided code samples, available in Java, Python, Go, and Node.js, illustrate the concepts and functionalities discussed.\u003c/p\u003e\n"]]],[],null,["# Advanced Concepts\n\n| **Note:** The examples below use the Roads API in the [Java Client for Google Maps Services](https://github.com/googlemaps/google-maps-services-java). You can adapt the concepts to your language of choice. The [Python Client](https://github.com/googlemaps/google-maps-services-python) , [Go Client](https://github.com/googlemaps/google-maps-services-go) and [Node.js Client](https://github.com/googlemaps/google-maps-services-js) are also available on GitHub.\n\nAcquire data\n------------\n\nThere are many ways to obtain collected location data. Here we describe two\ntechniques for acquiring data to use with the [snap to roads](/maps/documentation/roads/snap) feature of\nthe Roads API.\n\n### GPX\n\nGPX is an open XML-based format for sharing routes, tracks and waypoints\ncaptured by GPS devices. This example uses the\n[XmlPull](http://www.xmlpull.org/) parser, a lightweight XML\nparser available for both Java server and mobile environments. \n\n```gdscript\n/**\n * Parses the waypoint (wpt tags) data into native objects from a GPX stream.\n */\nprivate List\u003cLatLng\u003e loadGpxData(XmlPullParser parser, InputStream gpxIn)\n throws XmlPullParserException, IOException {\n // We use a List\u003c\u003e as we need subList for paging later\n List\u003cLatLng\u003e latLngs = new ArrayList\u003c\u003e();\n parser.setInput(gpxIn, null);\n parser.nextTag();\n\n while (parser.next() != XmlPullParser.END_DOCUMENT) {\n if (parser.getEventType() != XmlPullParser.START_TAG) {\n continue;\n }\n\n if (parser.getName().equals(\"wpt\")) {\n // Save the discovered latitude/longitude attributes in each \u003cwpt\u003e.\n latLngs.add(new LatLng(\n Double.valueOf(parser.getAttributeValue(null, \"lat\")),\n Double.valueOf(parser.getAttributeValue(null, \"lon\"))));\n }\n // Otherwise, skip irrelevant data\n }\n\n return latLngs;\n}\n```\n\nHere's some raw GPX data loaded onto a map.\n\n### Android location services\n\nThe best way to capture GPS data from an Android device varies depending on your\nuse case. Take a look at the Android training class on\n[Receiving Location Updates](https://developer.android.com/training/location/receive-location-updates.html),\nas well as the\n[Google Play Location samples on GitHub](https://github.com/googlesamples/android-play-location).\n\nProcess long paths\n------------------\n\nAs the [snap to roads](/maps/documentation/roads/snap) feature infers the location based on the full path,\nrather than individual points, you need to take care when processing long\npaths (that is, paths over the 100-point-per-request limit).\n\nIn order to treat the individual requests as one long path, you should include\nsome overlap, such that the final points from the previous request are included\nas the first points of the subsequent request. The number of points to include\ndepends on the accuracy of your data. You should include more points\nfor low-accuracy requests.\n\nThis example uses the\n[Java Client for Google Maps Services](https://github.com/googlemaps/google-maps-services-java)\nto send paged requests and\nthen rejoins the data, including interpolated points, into the returned list. \n\n```scilab\n/**\n * Snaps the points to their most likely position on roads using the Roads API.\n */\nprivate List\u003cSnappedPoint\u003e snapToRoads(GeoApiContext context) throws Exception {\n List\u003cSnappedPoint\u003e snappedPoints = new ArrayList\u003c\u003e();\n\n int offset = 0;\n while (offset \u003c mCapturedLocations.size()) {\n // Calculate which points to include in this request. We can't exceed the API's\n // maximum and we want to ensure some overlap so the API can infer a good location for\n // the first few points in each request.\n if (offset \u003e 0) {\n offset -= PAGINATION_OVERLAP; // Rewind to include some previous points.\n }\n int lowerBound = offset;\n int upperBound = Math.min(offset + PAGE_SIZE_LIMIT, mCapturedLocations.size());\n\n // Get the data we need for this page.\n LatLng[] page = mCapturedLocations\n .subList(lowerBound, upperBound)\n .toArray(new LatLng[upperBound - lowerBound]);\n\n // Perform the request. Because we have interpolate=true, we will get extra data points\n // between our originally requested path. To ensure we can concatenate these points, we\n // only start adding once we've hit the first new point (that is, skip the overlap).\n SnappedPoint[] points = RoadsApi.snapToRoads(context, true, page).await();\n boolean passedOverlap = false;\n for (SnappedPoint point : points) {\n if (offset == 0 || point.originalIndex \u003e= PAGINATION_OVERLAP - 1) {\n passedOverlap = true;\n }\n if (passedOverlap) {\n snappedPoints.add(point);\n }\n }\n\n offset = upperBound;\n }\n\n return snappedPoints;\n}\n```\n\nHere's the data from above after running the snap to roads requests. The red\nline is the raw data and the blue line is the snapped data.\n\nEfficient use of quota\n----------------------\n\nThe response to a [snap to roads](/maps/documentation/roads/snap) request includes a list of place IDs\nthat map to the points you provided, potentially with additional points if you\nset `interpolate=true`.\n\nIn order to make efficient use of your allowed quota for a speed limits request,\nyou should only query for unique place IDs in your request. This example uses\nthe\n[Java Client for Google Maps Services](https://github.com/googlemaps/google-maps-services-java)\nto query speed limits from a list of place IDs. \n\n```scilab\n/**\n * Retrieves speed limits for the previously-snapped points. This method is efficient in terms\n * of quota usage as it will only query for unique places.\n *\n * Note: Speed limit data is only available for requests using an API key enabled for a\n * Google Maps APIs Premium Plan license.\n */\nprivate Map\u003cString, SpeedLimit\u003e getSpeedLimits(GeoApiContext context, List\u003cSnappedPoint\u003e points)\n throws Exception {\n Map\u003cString, SpeedLimit\u003e placeSpeeds = new HashMap\u003c\u003e();\n\n // Pro tip: Save on quota by filtering to unique place IDs.\n for (SnappedPoint point : points) {\n placeSpeeds.put(point.placeId, null);\n }\n\n String[] uniquePlaceIds =\n placeSpeeds.keySet().toArray(new String[placeSpeeds.keySet().size()]);\n\n // Loop through the places, one page (API request) at a time.\n for (int i = 0; i \u003c uniquePlaceIds.length; i += PAGE_SIZE_LIMIT) {\n String[] page = Arrays.copyOfRange(uniquePlaceIds, i,\n Math.min(i + PAGE_SIZE_LIMIT, uniquePlaceIds.length));\n\n // Execute!\n SpeedLimit[] placeLimits = RoadsApi.speedLimits(context, page).await();\n for (SpeedLimit sl : placeLimits) {\n placeSpeeds.put(sl.placeId, sl);\n }\n }\n\n return placeSpeeds;\n}\n```\n\nHere's the data from above with speed limits marked at each unique place ID.\n\nInterplay with other APIs\n-------------------------\n\nOne of the benefits of having place IDs returned in the [snap to roads](/maps/documentation/roads/snap)\nresponses is that you can use the place ID across many of the\nGoogle Maps Platform APIs. This example uses the [Java Client for Google Maps Services](https://github.com/googlemaps/google-maps-services-java)\nto geocode a place returned from the above snap to road request. \n\n```scilab\n/**\n * Geocodes a snapped point using the place ID.\n */\nprivate GeocodingResult geocodeSnappedPoint(GeoApiContext context, SnappedPoint point) throws Exception {\n GeocodingResult[] results = GeocodingApi.newRequest(context)\n .place(point.placeId)\n .await();\n\n if (results.length \u003e 0) {\n return results[0];\n }\n return null;\n}\n```\n\nHere the speed limit marker has been annotated with the address from the\nGeocoding API.\n\nSample code\n-----------\n\n### Considerations\n\nThe code supporting this document is available as a single Android app for\nillustrative purposes. In practice you shouldn't distribute your server-side\nAPI keys in an Android app as your key cannot be secured against unauthorized\naccess from a third party. Instead, to secure your keys you should deploy the\nAPI-facing code as a server-side proxy and have your Android app send requests\nusing the proxy, to be sure requests are authorized.\n\n### Download\n\nDownload the code from [GitHub](https://github.com/googlemaps/roads-api-samples)."]]