Source code for graphh.CGH

import requests
import unicodedata
import json
import warnings
from graphh import CGHError



[docs]class GraphHopper: """ GraphHopper API class. Parameters ---------- api_key: str API key to be used for queries premium: bool Whether the account corresponding to this key is a premium account or not Examples -------- >>> from graphh import GraphHopper >>> gh_client = GraphHopper(api_key=YOUR_API_KEY) >>> gh_client.address_to_latlong("Rennes, République") (48.1098593, -1.6787526) >>> latlong_Paris = gh_client.address_to_latlong("Paris, France") >>> latlong_Madrid = gh_client.address_to_latlong("Madrid, Spain") >>> gh_client.distance([latlong_Paris, latlong_Madrid], unit="km") 1269.9657 >>> gh_client.duration([latlong_Paris, latlong_Madrid], unit="h") 11.641364444444443 >>> import pprint >>> pprint.pprint(gh_client.route([latlong_Paris, latlong_Madrid])) {'hints': {'visited_nodes.average': '947.0', 'visited_nodes.sum': '947'}, 'info': {'copyrights': ['GraphHopper', 'OpenStreetMap contributors'], 'took': 43}, 'paths': [{'ascend': 11624.469142794609, 'bbox': [-3.778313, 40.412748, 2.346683, 48.878851], 'descend': 11026.474138140678, 'details': {}, 'distance': 1269965.7, 'instructions': [{'distance': 246.715, 'heading': 165.02, 'interval': [0, 2], 'sign': 0, 'street_name': 'Rue Blanche', 'text': 'Continue onto Rue Blanche', 'time': 67674}, ... """ url = "https://graphhopper.com/api/1/" def __init__(self, api_key, premium=False): self.api_key = api_key self.premium = premium def _url_requests(self, api, data, request="get"): """ This function does an url request (GET or POST) with given parameters Parameters ---------- api: str name of the api used data: dict dict of parameters to insert in the url request: str, optional Type of request : GET or POST By default, the request will be GET Returns ------- dict The dictionary return by url request (GET or POST) """ complete_url = GraphHopper.url + api + "?" if request.upper() == "POST": complete_url += "key={}".format(self.api_key) reponse = requests.post(url=complete_url, json=data, headers={'content-type': 'application/json'}) else: data["key"] = self.api_key reponse = requests.get(url=complete_url, params=data) try: reponse.raise_for_status() except requests.exceptions.HTTPError as e: CGHError.CGHError(e) return reponse.json() def _latlong_handle_request(self,l_latlong,request="get"): """" This function changes the format of the coordinates according to the request Parameters ---------- l_latlong: list Tuple list (latitude, longitude) of the considerated points request: str, optional Type of request : GET or POST By default, the request will be GET Returns ------- list list of the coordinates according to the request """ l_latlong_handle = [] if request.upper() == "POST": for latlong in l_latlong: l_latlong_handle.append([latlong[1], latlong[0]]) else: for point in l_latlong: l_latlong_handle.append(','.join([str(latlong) for latlong in point])) return l_latlong_handle
[docs] def geocode(self, address, limit=1, locale="en"): """This function does geocoding. It transforms a given address into matching geographic coordinates. Parameters ---------- address : str The address of the location that needs to be transformed. limit : int, optional The number of matching location you would like to get. By default, the function will only return one location. locale : str, optional The language of the answer. By default, the answer will be in english. Returns ------- dict A dictionary containing the matching locations' information, including their geographic coordinates, and the number of ms it took. """ a = str(unicodedata.normalize('NFKD',str(address)).encode('ascii', 'ignore')) data = dict() data["q"] = "{}".format(a.replace(" ", "+")) data["limit"] = str(limit) data["locale"] = locale return self._url_requests("geocode", data)
[docs] def reverse_geocode(self, latlong, locale="en"): """This function does reverse geocoding. It transforms given geographic coordinates into matching addresses. Parameters ---------- latlong : tuple The geographic coordinates that need to be transformed. The first element is the latitude and the second one is the longitude. locale : str, optional The language of the answer. By default, the answer will be in english. Returns ------- dict A dictionary containing the matching locations' information, including their addresses, and the number of ms it took. """ data = dict() data["reverse"] = "true" CGHError.check_point([latlong], "geocode") data["point"] = "{},{}".format(latlong[0], latlong[1]) data["locale"] = locale return self._url_requests("geocode", data)
[docs] def route(self, l_latlong, request="get", vehicle="car", locale="en", calc_points="true", instructions="true", points_encoded="true", elevation="false"): """This function give an itinerary between given points Parameters ---------- l_latlong : list Tuple list (latitude, longitude) of the considered points request: str, optional Type of request : GET or POST By default, the request will be GET vehicle : str, optional The type of vehicle chosen in the list : ["car", "foot", "bike"] if the account is not premium And can be chosen in the list : ["small_truck", "truck", "scooter", "hike", "mtb", "racingbike"] if it is locale : str, optional The language of the answer. By default, the answer will be in english. calc_points : boolean, optional If the points for the route should be calculated at all. default = true instructions : boolean, optional If instructions should be calculated and returned default = true points_encoded : boolean, optional If false, the coordinates in point and snapped_waypoints are returned as lists of positions using the order [lon,lat,elevation]. If true, the coordinates will be encoded as a string. default = true elevation : boolean, optional If true, a third coordinate, the altitude, is included to all positions in the response Returns ------- dict A dictionary of the matching itinerary containing distance, time, ascend, descend, points (encoded or not), instructions with street name and description what the user has to do in order to follow the route. """ data = dict() CGHError.check_point(l_latlong, "route") l_latlong_handle = self._latlong_handle_request(l_latlong, request) data["points"] = l_latlong_handle CGHError.check_vehicle(vehicle, self.premium) data["vehicle"] = vehicle data["locale"] = locale CGHError.check_boolean(calc_points) data["calc_points"] = calc_points CGHError.check_boolean(instructions) data["instructions"] = instructions CGHError.check_boolean(points_encoded) data["points_encoded"] = points_encoded CGHError.check_boolean(elevation) data["elevation"] = elevation return self._url_requests("route", data, request)
[docs] def matrix_request(self, l_from_points, l_to_points,request="get", vehicle="car"): """This function gives the different possible matrix between the points: distance, temp, weight Parameters ---------- l_from_points : list Tuple list (latitude, longitude) of the starting points for the routes l_to_points : list Tuple list (latitude, longitude) of the destination points for the routes request: str, optional Type of request : GET or POST By default, the request will be GET vehicle : str, optional The type of vehicle chosen in the list : ["car", "foot", "bike"] if the acount is not premium And can be chosen in the list : ["small_truck", "truck", "scooter", "hike", "mtb", "racingbike"] if it is Returns ------- dict A dictionary containing distances, times and weights matrices """ data = dict() CGHError.check_point(l_from_points, "matrix") CGHError.check_point(l_to_points, "matrix") l_from_points_handle = self._latlong_handle_request(l_from_points, request) l_to_points_handle = self._latlong_handle_request(l_to_points, request) if (request.upper() == "GET") and (len(l_from_points) == 1 or len(l_to_points) == 1): data["from_point"] = l_from_points_handle data["to_point"] = l_to_points_handle else: data["from_points"] = l_from_points_handle data["to_points"] = l_to_points_handle CGHError.check_vehicle(vehicle, self.premium) data["vehicle"] = vehicle data["out_arrays"] = ["distances", "times", "weights"] return self._url_requests("matrix", data, request)
[docs] def matrix(self,l_from_address, l_to_address,out_array, format="default", vehicle="car", request="get"): """This function gives one matrix between the points: distances, times or weights Parameters ---------- l_from_address : list The list containing the cities, address of the points l_to_address : list The list containing the cities, address of the points out_array: str Specifies which array should be included in the response from the list ["distances", "times", "weights"] The units of the entries of distances are meters, of times are seconds and of weights is arbitrary and it can differ for different vehicles or versions of this API format: str, optional Specifies how the array should be drawn from the list ["pandas","numpy","default"] By default, the array is values "default" vehicle : str, optional The type of vehicle chosen in the list : ["car", "foot", "bike"] if the acount is not premium And can be chosen in the list : ["small_truck", "truck", "scooter", "hike", "mtb", "racingbike"] if it is request: str, optional Type of request : GET or POST By default, the request will be GET Returns ------- 3 possibilities : dataframe : data frame A data frame with for names columns the address for l_to_address and names rows the address for l_from_address and data of matrix matrix : array A array data of the function matrix list : list A list of data list of the function matrix """ CGHError.check_dim(len(l_from_address), len(l_to_address), self.premium) CGHError.check_out_array(out_array) CGHError.check_format_matrix(format) l_from_points = list() l_to_points = list() for start in l_from_address: l_from_points.append(self.address_to_latlong(start)) for destination in l_to_address: l_to_points.append(self.address_to_latlong(destination)) dic = self.matrix_request(l_from_points, l_to_points, vehicle=vehicle, request=request) if format.lower() == "pandas": return self.matrix_pandas(dic, out_array, l_from_address, l_to_address) elif format.lower() == "numpy": matrix = self.matrix_numpy(dic, out_array) return matrix else: liste = dic[out_array] return liste
[docs] def matrix_pandas(self, dic, out_array, l_from_address, l_to_address): """This function gives a dataframe between the points: distances, times or weights Parameters ---------- dic: dictionary A dictionary containing distances, times and weights matrices out_array: str Specifies which array should be included in the response from the list ["distances", "times", "weights"] The units of the entries of distances are meters, of times are seconds and of weights is arbitrary and it can differ for different vehicles or versions of this API l_from_address : list The list containing the cities, address of the points l_to_address : list The list containing the cities, address of the points Returns ------- dataframe : data frame A data frame with for names columns the address for l_to_address and names rows the address for l_from_address and data of matrix """ try: import pandas import numpy matrix = numpy.array(dic[out_array]) dataframe = pandas.DataFrame(matrix.reshape(len(l_from_address), len(l_to_address)), index=l_from_address, columns=l_to_address) except ImportError: pandas_imported = False numpy_imported = False CGHError.check_package(pandas_imported, "pandas") CGHError.check_package(numpy_imported, "numpy") else: return dataframe
[docs] def matrix_numpy(self, dic, out_array): """This function gives a matrix (numpy) between the points: distances, times or weights Parameters ---------- dic: dictionary A dictionary containing distances, times and weights matrices out_array: str Specifies which array should be included in the response from the list ["distances", "times", "weights"] The units of the entries of distances are meters, of times are seconds and of weights is arbitrary and it can differ for different vehicles or versions of this API Returns ------- dataframe : data frame A data frame with for names columns the address for l_to_address and names rows the address for l_from_address and data of matrix """ try: import numpy matrix = numpy.array(dic[out_array]) except ImportError: numpy_imported = False CGHError.check_package(numpy_imported, "numpy") else: return matrix
[docs] def address_to_latlong(self, address): """This function is a simplified version of the geocoding function. Parameters ---------- address : str The address of the location that needs to be transformed. Returns ------- tuple A tuple corresponding to the geographic coordinates of the location. The first element is the latitude and the second one is the longitude. """ d = self.geocode(address, limit=1) lat = d["hits"][0]["point"]["lat"] lng = d["hits"][0]["point"]["lng"] if abs(lat - 28.62707) < 0.0001 and abs(lng + 80.62087) < 0.0001 : warnings.warn("The coordinates match with Cap Canaveral, Florida\n.It can happen when the function can't find the adress. Try adding more informations to your adress, like state or country.",stacklevel=2) return lat, lng
[docs] def latlong_to_address(self, latlong): """This function is a simplified version of the reverse geocoding function. Parameters ---------- latlong : tuple The geographic coordinates that need to be transformed. The first element is the latitude and the second one is the longitude. Returns ------- str The address of the location. """ d = self.reverse_geocode(latlong) l_elem = [] if "housenumber" in d["hits"][0].keys(): num = d["hits"][0]["housenumber"] l_elem.append(num) if "street" in d["hits"][0].keys(): st = d["hits"][0]["street"] l_elem.append(st) if 'postcode' in d["hits"][0].keys(): pc = d["hits"][0]["postcode"] c = d["hits"][0]["city"] l_elem.append(pc) l_elem.append(c) if 'city' in d["hits"][0].keys(): c = d["hits"][0]["city"] l_elem.append(c) else: n = d["hits"][0]["name"] l_elem.append(n) if 'state' in d["hits"][0].keys(): st = d["hits"][0]["state"] l_elem.append(st) if 'country' in d["hits"][0].keys(): c = d["hits"][0]["country"] l_elem.append(c) a = "" for elt in l_elem: a += "{} ".format(elt) return a.strip()
[docs] def distance(self, l_latlong, unit="m"): """This function gives the distance between precised points for a given itinerary Parameters ---------- l_latlong: list The list of the tuples (latitude, longitude) of the considerated points unit: str The unit of the distance returned chosen between "m" and "km" By default the unit will be in meters Returns ------- float The number of the distance for the itinerary for the unit chosen """ dic = self.route(l_latlong, points_encoded="false") CGHError.check_unitdistance(unit) if unit == "m": return dic["paths"][0]["distance"] elif unit == "km": return (dic["paths"][0]["distance"]) / 1000
[docs] def duration(self, l_latlong, vehicle="car", unit="ms"): """This function give the time between precised points for a given itinerary Parameters ---------- l_latlong: list The list of the tuples (latitude, longitude) of the considerated points vehicle: str The type of vehicle chosen in the list : ["car", "foot", "bike"] if the acount is not premium And can be chosen in the list : ["small_truck", "truck", "scooter", "hike", "mtb", "racingbike"] if it is By default the vehicle will be car unit: str The unit of the duration returned chosen between "ms", "s", "min" and "h" By default the unit will be milliseconds Returns ------- float The number of the time for the itinerary for the unit and vehicle chosen """ dic = self.route(l_latlong, vehicle=vehicle, points_encoded="false") CGHError.check_unittime(unit) if unit == "ms": return dic["paths"][0]["time"] elif unit == "s": return (dic["paths"][0]["time"]) / 1000 elif unit == "min": return ((dic["paths"][0]["time"]) / 1000) / 60 elif unit == "h": return (((dic["paths"][0]["time"]) / 1000) / 60) / 60
[docs] def elevation_point(self, latlong): """This function give an elevation for a given geographic coordinates Parameters ---------- latlong : tuple The geographic coordinates that need to be transformed. The first element is the latitude and the second one is the longitude. Returns ------- float Elevation of one geographic coordinate couple """ dict = self.route([latlong, latlong], instructions="false", elevation="true", points_encoded="false") return dict["paths"][0]["points"]["coordinates"][0][2]