Lagent / lagent /actions /bing_map.py
Superkingjcj's picture
Upload 111 files
e679d69 verified
raw
history blame
10.7 kB
# flake8: noqa: E501
import json
import os
from typing import Optional, Type
import aiohttp
import requests
from lagent.actions.base_action import AsyncActionMixin, BaseAction, tool_api
from lagent.actions.parser import BaseParser, JsonParser
class BINGMap(BaseAction):
"""BING Map plugin for looking up map information."""
def __init__(
self,
key: Optional[str] = None,
description: Optional[dict] = None,
parser: Type[BaseParser] = JsonParser,
) -> None:
super().__init__(description, parser)
key = os.environ.get('BING_MAP_KEY', key)
if key is None:
raise ValueError(
'Please set BING Map API key either in the environment '
'as BING_MAP_KEY or pass it as `key` parameter.')
self.key = key
self.base_url = 'http://dev.virtualearth.net/REST/V1/'
@tool_api(explode_return=True)
def get_distance(self, start: str, end: str) -> dict:
"""Get the distance between two locations in km.
Args:
start (:class:`str`): The start location
end (:class:`str`): The end location
Returns:
:class:`dict`: distance information
* distance (str): the distance in km.
"""
# Request URL
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key
# GET request
r = requests.get(url)
# TODO check request status?
data = json.loads(r.text)
# Extract route information
route = data['resourceSets'][0]['resources'][0]
# Extract distance in miles
distance = route['travelDistance']
return dict(distance=distance)
@tool_api(explode_return=True)
def get_route(self, start: str, end: str) -> dict:
"""Get the route between two locations in km.
Args:
start (:class:`str`): The start location
end (:class:`str`): The end location
Returns:
:class:`dict`: route information
* route (list): the route, a list of actions.
"""
# Request URL
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key
# GET request
r = requests.get(url)
data = json.loads(r.text)
# Extract route information
route = data['resourceSets'][0]['resources'][0]
itinerary = route['routeLegs'][0]['itineraryItems']
# Extract route text information
route_text = []
for item in itinerary:
if 'instruction' in item:
route_text.append(item['instruction']['text'])
return dict(route=route_text)
@tool_api(explode_return=True)
def get_coordinates(self, location: str) -> dict:
"""Get the coordinates of a location.
Args:
location (:class:`str`): the location need to get coordinates.
Returns:
:class:`dict`: coordinates information
* latitude (float): the latitude of the location.
* longitude (float): the longitude of the location.
"""
url = self.base_url + 'Locations'
params = {'query': location, 'key': self.key}
response = requests.get(url, params=params)
json_data = response.json()
coordinates = json_data['resourceSets'][0]['resources'][0]['point'][
'coordinates']
return dict(latitude=coordinates[0], longitude=coordinates[1])
@tool_api(explode_return=True)
def search_nearby(self,
search_term: str,
places: str = 'unknown',
latitude: float = 0.0,
longitude: float = 0.0,
radius: int = 5000) -> dict:
"""Search for places nearby a location, within a given radius, and return the results into a list. You can use either the places name or the latitude and longitude.
Args:
search_term (:class:`str`): the place name.
places (:class:`str`): the name of the location. Defaults to ``'unknown'``.
latitude (:class:`float`): the latitude of the location. Defaults to ``0.0``.
longitude (:class:`float`): the longitude of the location. Defaults to ``0.0``.
radius (:class:`int`): radius in meters. Defaults to ``5000``.
Returns:
:class:`dict`: places information
* places (list): the list of places, each place is a dict with name and address, at most 5 places.
"""
url = self.base_url + 'LocalSearch'
if places != 'unknown':
pos = self.get_coordinates(**{'location': places})
latitude, longitude = pos[1]['latitude'], pos[1]['longitude']
# Build the request query string
params = {
'query': search_term,
'userLocation': f'{latitude},{longitude}',
'radius': radius,
'key': self.key
}
# Make the request
response = requests.get(url, params=params)
# Parse the response
response_data = json.loads(response.content)
# Get the results
results = response_data['resourceSets'][0]['resources']
addresses = []
for result in results:
name = result['name']
address = result['Address']['formattedAddress']
addresses.append(dict(name=name, address=address))
if len(addresses) == 5:
break
return dict(place=addresses)
class AsyncBINGMap(AsyncActionMixin, BINGMap):
"""BING Map plugin for looking up map information."""
@tool_api(explode_return=True)
async def get_distance(self, start: str, end: str) -> dict:
"""Get the distance between two locations in km.
Args:
start (:class:`str`): The start location
end (:class:`str`): The end location
Returns:
:class:`dict`: distance information
* distance (str): the distance in km.
"""
# Request URL
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key
# GET request
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
# TODO check request status?
data = await resp.json()
# Extract route information
route = data['resourceSets'][0]['resources'][0]
# Extract distance in miles
distance = route['travelDistance']
return dict(distance=distance)
@tool_api(explode_return=True)
async def get_route(self, start: str, end: str) -> dict:
"""Get the route between two locations in km.
Args:
start (:class:`str`): The start location
end (:class:`str`): The end location
Returns:
:class:`dict`: route information
* route (list): the route, a list of actions.
"""
# Request URL
url = self.base_url + 'Routes/Driving?o=json&wp.0=' + start + '&wp.1=' + end + '&key=' + self.key
# GET request
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
data = await resp.json()
# Extract route information
route = data['resourceSets'][0]['resources'][0]
itinerary = route['routeLegs'][0]['itineraryItems']
# Extract route text information
route_text = []
for item in itinerary:
if 'instruction' in item:
route_text.append(item['instruction']['text'])
return dict(route=route_text)
@tool_api(explode_return=True)
async def get_coordinates(self, location: str) -> dict:
"""Get the coordinates of a location.
Args:
location (:class:`str`): the location need to get coordinates.
Returns:
:class:`dict`: coordinates information
* latitude (float): the latitude of the location.
* longitude (float): the longitude of the location.
"""
url = self.base_url + 'Locations'
params = {'query': location, 'key': self.key}
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as resp:
data = await resp.json()
coordinates = data['resourceSets'][0]['resources'][0]['point'][
'coordinates']
return dict(latitude=coordinates[0], longitude=coordinates[1])
@tool_api(explode_return=True)
async def search_nearby(self,
search_term: str,
places: str = 'unknown',
latitude: float = 0.0,
longitude: float = 0.0,
radius: int = 5000) -> dict:
"""Search for places nearby a location, within a given radius, and return the results into a list. You can use either the places name or the latitude and longitude.
Args:
search_term (:class:`str`): the place name.
places (:class:`str`): the name of the location. Defaults to ``'unknown'``.
latitude (:class:`float`): the latitude of the location. Defaults to ``0.0``.
longitude (:class:`float`): the longitude of the location. Defaults to ``0.0``.
radius (:class:`int`): radius in meters. Defaults to ``5000``.
Returns:
:class:`dict`: places information
* places (list): the list of places, each place is a dict with name and address, at most 5 places.
"""
url = self.base_url + 'LocalSearch'
if places != 'unknown':
pos = self.get_coordinates(**{'location': places})
latitude, longitude = pos[1]['latitude'], pos[1]['longitude']
# Build the request query string
params = {
'query': search_term,
'userLocation': f'{latitude},{longitude}',
'radius': radius,
'key': self.key
}
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as resp:
data = await resp.json()
results = data['resourceSets'][0]['resources']
addresses = []
for result in results:
name = result['name']
address = result['Address']['formattedAddress']
addresses.append(dict(name=name, address=address))
if len(addresses) == 5:
break
return dict(place=addresses)