使用 Places API 沿路线搜索

本文档介绍了如何在规划好的路线上查找酒店、餐厅或加油站。您将学习如何使用 Routes API 获取路线多段线,并将其与 Places API 沿路搜索 (SAR) 请求搭配使用。此外,您还将了解如何通过沿路线设置搜索起点(例如行程 2 小时后)来获得最佳结果。

Routes API

为了搜索沿途的地点,我们将使用 Routes API。Routes API 响应中的路线数据是从起点到目的地的一系列经纬度坐标。路线数据包含沿着道路网的路段和步骤。

路线也会作为编码的多段线返回,您可以将其作为输入参数传递给 SAR 请求。多段线编码是一种有损压缩算法,可让您将一系列坐标存储为单个字符串。从 Routes API 获取多段线不是强制性要求。您可以自行创建数据,但在此示例中,Routes API 是一种快速且可靠的方式来获取所需数据。

在本教程中,我们使用从伦敦 (-37.8167,144.9619) 到曼彻斯特 (-37.8155, 144.9663) 的路线

从伦敦到曼彻斯特的路线

示例中的路线:伦敦到曼彻斯特

第 1 步:从 Routes API 获取路线

如需从 Routes API 获取路线,您需要提供以下信息:

  • 出发地和目的地位置
  • 交通方式(驾车、步行等)
  • 任意航点(可选)
  • 任何偏好设置(避开收费站、避开高速公路等)
  • 流量感知型路由偏好设置可为您提供最精确的估算结果,但计算量较大,因此会增加响应延迟时间。
{"origin":{
    "location": {
        "latLng":{
            "latitude":  -37.8167,
            "longitude": 144.9619
        }
    }
},
"destination":{
    "location": {
        "latLng":{
            "latitude":-37.8155,
            "longitude": 144.9663
        }
    }
},
"routingPreference":"TRAFFIC_AWARE",
"travelMode":"DRIVE"
}

进行调用时,请务必在标头字段掩码中添加“encodedPolyline”字段。

headers = {
    "Content-Type": "application/json",
    "X-Goog-FieldMask": "routes.distanceMeters,routes.duration,routes.legs,routes.polyline.encodedPolyline"
}

完整文档,其中包含有关如何获取路线获取路线多段线的示例。

在请求中提供此信息后,Routes API 将返回一条路线对象。路线对象将包含以下信息:

  • 路线的总距离
  • 路线的总时长
  • 相应路线的路段和步骤
  • 路线、路段和步骤的编码多段线。
{
  "routes": [
    {
      "legs": [
        {
          "distanceMeters": 321799,
          "duration": "15401s",
          "staticDuration": "14518s",
          "polyline": {
            "encodedPolyline": "y_kyH`_XOr@q@xKGnBBZ|AlGPj@Y^k@^MEqAfAQLK?eI … <rest of content removed for readability>"
          },
          "startLocation": {
            "latLng": {
              "latitude": 51.507334500000006,
              "longitude": -0.1280107
            }
          },
          "endLocation": {
            "latLng": {
              "latitude": 53.4808513,
              "longitude": -2.2425864
            }
          },
          "steps": [
            {
              "distanceMeters": 320,
              "staticDuration": "82s",
              "polyline": {
                "encodedPolyline": "y_kyH`_XOr@q@xKGnBBZ|AlG"
              },
              "startLocation": {
                "latLng": {
                  "latitude": 51.507334500000006,
                  "longitude": -0.1280107
                }
              },
              "endLocation": {
                "latLng": {
                  "latitude": 51.507207,
                  "longitude": -0.1323681
                }
              },
              "navigationInstruction": {
                "maneuver": "DEPART",
                "instructions": "Head northwest on Trafalgar Sq/A4 toward Spring Gardens\nContinue to follow A4\nLeaving toll zone\nEntering toll zone\nLeaving toll zone in 210m at Haymarket"
              },
              "localizedValues": {
                "distance": {
                  "text": "0.3 km"
                },
                "staticDuration": {
                  "text": "1 min"
                }
              },
# rest of the response removed for readability

第 2 步:沿路搜索请求

Places API 文本搜索提供了“沿路搜索”请求,可让您搜索路线沿途的地点。如需发出沿路搜索请求,您至少需要提供以下信息:

  • 用于指定要在响应中返回哪些字段的字段掩码
  • 在 Google Cloud 控制台中启用的 API 的有效 API 密钥
  • 用于指明您要查找的地点的搜索文本字符串,例如“提供辛辣素食的餐厅”
  • 路线的编码多段线,从上一次 Routes API 调用中检索
  • Places Text Search API 端点的网址
import requests

url = 'https://places.googleapis.com/v1/places:searchText'
api_key = 'YOUR_API_KEY'  # Replace with your actual API key
route_polyline = 'YOUR_ROUTE_POLYLINE'  # Replace with your encoded route polyline

headers = {
    'Content-Type': 'application/json',
    'X-Goog-Api-Key': api_key,
    'X-Goog-FieldMask': 'places.displayName,places.formattedAddress,places.priceLevel'
}

data = {
    "textQuery":
 "Spicy Vegetarian Food",
    "searchAlongRouteParameters": {
        "polyline": {
            "encodedPolyline": route_polyline
        }
    }
}

response = requests.post(url, headers=headers, json=data)

请求数据示例

“沿路搜索”请求会返回位于路线沿途的地点列表。以下是示例数据的一部分。您可以通过设置结果参数的数量上限来限制响应长度,当然,添加更多字段会增加收到的数据量。如需详细了解 Places API 响应,请参阅文档

{
  "places": [
    {
      "formattedAddress": "33 Haymarket, London SW1Y 4HA, UK",
      "displayName": {
        "text": "xxx",
        "languageCode": "en"
      }
    },
    {
      "formattedAddress": "224 Piccadilly, London W1J 9HP, UK",
      "priceLevel": "PRICE_LEVEL_MODERATE",
      "displayName": {
        "text": "yyy",
        "languageCode": "en"
      }
    },
    {
      "formattedAddress": "63 Neal St, London WC2H 9PJ, UK",
      "displayName": {
        "text": "zzz",
        "languageCode": "en"
      }
    },

响应数据示例

路线摘要和绕道时间

仅查找地点就很不错了,但如果能添加前往这些地点所需的时间信息,将会很有帮助。Places API 文本搜索中的 SAR 还可以返回一个包含行程时长和距离的路线摘要字段。路线摘要数据字段是响应根的子字段,因此您不得在字段掩码中添加“places.”前缀。

'X-Goog-FieldMask': 'places.displayName,places.formattedAddress,places.priceLevel,routingSummaries'

如需获取摘要,您还必须为搜索提供起始位置参数,该参数用于计算。

"routingParameters": {
      "origin": {
        "latitude":  -37.8167,
        "longitude": 144.9619
      }
    }

您收到的响应中会有一个新的部分,其中包含路线摘要,其中包含时长和距离(以米为单位)的路段

"routingSummaries": [
    {
      "legs": [
        {
          "duration": "662s",
          "distanceMeters": 3093
        }
      ]
    },

接下来,我们来看看如何定义沿路线的搜索起始位置。

第 3 步:获取路线沿途 2 小时内的位置信息

考虑一个常见的用例:驾驶员希望查找的餐厅不在路线起点,而是在路线下方。在我们的示例中,从伦敦到曼彻斯特的行程大约需要 4 小时。司机想在行驶 2 小时后找到沿途的餐厅。此请求会返回时长,即 120 分钟 * 60 秒 = 7200 秒。

在 Routes API 响应中,我们可以找到路线的每条支线和每条支线的每个步骤的持续时间。请务必在请求的字段掩码中添加“legs”字段。循环遍历腿部和步骤,直到累计时长达到 2 小时或 7200 秒的限制。然后,我们找到了要将其设置为 SAR 请求起点的航段和步骤

为了加快工作速度,您可以尝试使用 Python 多段线库。您可以使用它从“polyline.endodedPolyline”数据字段获取坐标。

在环境终端中运行以下命令。

> pip install polyline
import requests
import polyline

# We've covered getting a Routes API response earlier,
data = response.json()

  # Extract the first route and its encoded polyline
  route = data["routes"][0]
  polyline_points = polyline.decode(route["polyline"]["encodedPolyline"])

  # Calculate total duration of the route in seconds
  total_duration_seconds = route["duration"]

  # Calculate the desired time offset in seconds, 2h = 120 minutes * 60
  desired_time_offset_seconds = time_offset_minutes * 60

  # Iterate through the legs and steps to find the point at the desired time offset
  elapsed_time_seconds = 0
  for leg in route["legs"]:
      for step in leg["steps"]:
          step_duration_seconds = step["staticDuration"]

          # Check if the desired time offset falls within this step, remove last "s" from string and convert to int
          second_value = int(step_duration_seconds[:-1])
          if elapsed_time_seconds + second_value >= desired_time_offset_seconds:
              # Interpolate to find the exact point within the step
              fraction_of_step = (desired_time_offset_seconds - elapsed_time_seconds) / second_value
              step_polyline_points = polyline.decode(step["polyline"]["encodedPolyline"])
              index = int(len(step_polyline_points) * fraction_of_step)
              return step_polyline_points[index]

          elapsed_time_seconds += second_value

  # If the point is not found (e.g., time offset exceeds route duration)
  return None

现在,我们已经找到了行程开始 2 小时后的位置,可以在请求中使用该位置。只需在“origin”参数中添加纬度和经度,该参数又是“routingParameters”参数的一部分。建议使用我们之前介绍的“routingSummaries”数据字段。如果需要,您还可以添加其他参数,例如出行方式和避开收费站的指示。


"routingParameters": {
    "origin": {
      "latitude": xx.xxxx,
      "longitude": yy.yyyy
    },
    "travelMode":"DRIVE",
    "routeModifiers": {
      "avoidTolls": true
    }
  }

包含搜索结果的路线

示例结果(添加了汽车图标以显示搜索来源)。

如图所示,API 返回的充电站偏向于路线的终点,结果大约从行程中途开始。搜索功能仍会依托相同的 Google Maps Platform 数据,这些数据会考虑地点相关性和距离等因素。

总结

在本教程中,我们学习了如何组合使用路线和地点这两个 Google Maps Platform API 来规划行程,并在行程开始 2 小时后查找餐厅。需要执行的步骤包括获取包含路线中每一步的纬度和经度坐标的编码多段线,并设置“沿路搜索”请求来源,以便获得最佳结果。

此功能为 Places API 中现有的文本搜索和附近搜索功能增添了一款强大的新工具。接下来合理的做法是添加位置信息服务,以便您可以使用驾驶员位置作为起点来查找最佳搜索来源。此外,此功能还可与车载语音助理完美搭配使用,您可以通过语音指令告知其您偏好的餐饮选项。

后续措施

建议的进一步阅读材料:

参稿人

此文档由 Google 维护。以下贡献者最初撰写了该文章。

主要作者:Mikko Toivanen | Google 地图平台解决方案工程师