Add location search and polish running route planner
This commit is contained in:
@@ -5,6 +5,7 @@ import json
|
||||
import math
|
||||
from collections import Counter
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.parse import urlencode
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
|
||||
@@ -12,6 +13,59 @@ class PlannerError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def search_locations(data, key):
|
||||
if not isinstance(data, dict) or not isinstance(data.get("text"), str):
|
||||
raise PlannerError("Enter a place name or address.")
|
||||
text = data["text"].strip()
|
||||
if not 3 <= len(text) <= 200:
|
||||
raise PlannerError("Use 3–200 characters to search for a location.")
|
||||
if not key:
|
||||
raise PlannerError("Location search is not connected yet.")
|
||||
params = {"text": text, "size": 5}
|
||||
if data.get("focus") is not None:
|
||||
focus = point(data["focus"])
|
||||
params.update({"focus.point.lon": focus[0], "focus.point.lat": focus[1]})
|
||||
req = Request(
|
||||
"https://api.openrouteservice.org/geocode/search?" + urlencode(params),
|
||||
headers={"Authorization": key, "Accept": "application/json"},
|
||||
)
|
||||
try:
|
||||
with urlopen(req, timeout=8) as response:
|
||||
raw = response.read(1_000_001)
|
||||
if len(raw) > 1_000_000:
|
||||
raise PlannerError("Location search returned too much data. Try a more specific place.")
|
||||
result = json.loads(raw)
|
||||
except HTTPError as error:
|
||||
message = (
|
||||
"Location search is busy. Try again shortly."
|
||||
if error.code == 429
|
||||
else ("Location search is unavailable. You can still select a point on the map.")
|
||||
)
|
||||
raise PlannerError(message) from None
|
||||
except (URLError, TimeoutError, OSError, json.JSONDecodeError):
|
||||
raise PlannerError(
|
||||
"Location search could not connect. Try selecting a point on the map."
|
||||
) from None
|
||||
if not isinstance(result, dict) or not isinstance(result.get("features"), list):
|
||||
raise PlannerError("Location search returned an incomplete response.")
|
||||
locations = []
|
||||
for feature in result["features"][:5]:
|
||||
if not isinstance(feature, dict):
|
||||
continue
|
||||
geometry, props = feature.get("geometry"), feature.get("properties")
|
||||
if not isinstance(geometry, dict) or not isinstance(props, dict):
|
||||
continue
|
||||
label = props.get("label") or props.get("name")
|
||||
if geometry.get("type") != "Point" or not isinstance(label, str):
|
||||
continue
|
||||
try:
|
||||
coordinates = point(geometry.get("coordinates"))
|
||||
except PlannerError:
|
||||
continue
|
||||
locations.append({"label": label[:300], "coordinates": coordinates})
|
||||
return locations
|
||||
|
||||
|
||||
def number(value, low, high, label):
|
||||
if isinstance(value, bool):
|
||||
raise PlannerError(f"Choose a valid {label}.")
|
||||
|
||||
Reference in New Issue
Block a user