mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
a82935f326
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Daniel Koepping <elkoled@gmail.com> Co-authored-by: Danny <dannyirwin31@yahoo.com> Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: Shane Smiskol <shane@smiskol.com> Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Co-authored-by: stef <19478336+stefpi@users.noreply.github.com> Co-authored-by: whoisdomi <dcantu83@gmail.com> Co-authored-by: Intelli <contact@intelli.software> Co-authored-by: pharmacomaniac <blittle65@gmail.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
105 lines
4.0 KiB
Python
105 lines
4.0 KiB
Python
"""Regional dashboard location, resolved without transmitting precise GPS."""
|
|
import json
|
|
import math
|
|
|
|
|
|
BASE_URL = 'https://nominatim.openstreetmap.org'
|
|
MINIMUM_POPULATION = 100_000
|
|
SEARCH_RADIUS_DEGREES = 1.45
|
|
UNKNOWN = {'city': 'N/A', 'state': 'N/A', 'country': 'N/A'}
|
|
|
|
|
|
def coarse_cell(gps):
|
|
if not isinstance(gps, dict):
|
|
return None
|
|
lat, lon = gps.get('latitude'), gps.get('longitude')
|
|
if (type(lat) not in (int, float) or type(lon) not in (int, float) or
|
|
not math.isfinite(lat) or not math.isfinite(lon) or not -90 <= lat <= 90 or not -180 <= lon <= 180):
|
|
return None
|
|
return float(round(lat)), float(round(lon))
|
|
|
|
|
|
def _population(candidate):
|
|
try:
|
|
return int(str((candidate.get('extratags') or {}).get('population', '0')).replace(',', '').split(';')[0])
|
|
except (AttributeError, TypeError, ValueError):
|
|
return 0
|
|
|
|
|
|
def _label(value):
|
|
if not isinstance(value, str):
|
|
return 'N/A'
|
|
return ''.join(c for c in value if c.isprintable()).strip()[:96] or 'N/A'
|
|
|
|
|
|
def _point(candidate, state, country):
|
|
address = candidate.get('address') or {}
|
|
if not isinstance(address, dict):
|
|
return None
|
|
try:
|
|
lat, lon = float(candidate['lat']), float(candidate['lon'])
|
|
except (KeyError, TypeError, ValueError):
|
|
return None
|
|
if not math.isfinite(lat) or not math.isfinite(lon) or not -90 <= lat <= 90 or not -180 <= lon <= 180:
|
|
return None
|
|
return {'latitude': lat, 'longitude': lon,
|
|
'city': _label(address.get('city') or address.get('town') or str(candidate.get('display_name', '')).split(',')[0]),
|
|
'state': _label(state), 'country': _label(country)}
|
|
|
|
|
|
def resolve_region(gps, *, session=None, allowed=lambda: True):
|
|
cell = coarse_cell(gps)
|
|
if cell is None:
|
|
return dict(UNKNOWN)
|
|
latitude, longitude = cell
|
|
fallback = dict(UNKNOWN, latitude=latitude, longitude=longitude)
|
|
if not allowed():
|
|
return fallback
|
|
import requests
|
|
owned = session is None
|
|
session = session or requests.Session()
|
|
session.headers.update({'Accept-Language': 'en', 'User-Agent': 'StarPilot-stats/1.0 (https://github.com/firestar5683/StarPilot)'})
|
|
|
|
def get(path, **params):
|
|
if not allowed():
|
|
raise InterruptedError('Statistics are no longer available offroad')
|
|
response = session.get(BASE_URL + path, params={'format': 'jsonv2', 'addressdetails': 1, **params},
|
|
timeout=(3, 5), stream=True, allow_redirects=False)
|
|
try:
|
|
if response.status_code != 200:
|
|
raise ValueError('Regional lookup was not available')
|
|
body = bytearray()
|
|
for chunk in response.iter_content(chunk_size=4096):
|
|
body.extend(chunk)
|
|
if len(body) > 262144:
|
|
raise ValueError('Regional lookup response exceeds its bound')
|
|
return json.loads(body)
|
|
finally:
|
|
response.close()
|
|
|
|
try:
|
|
data = get('/reverse', lat=latitude, lon=longitude, zoom=8, namedetails=0, extratags=0)
|
|
address = data.get('address') if isinstance(data, dict) else None
|
|
if not isinstance(address, dict):
|
|
return fallback
|
|
state = address.get('province') or address.get('region') or address.get('state') or address.get('state_district')
|
|
country = address.get('country')
|
|
fallback.update(state=_label(state), country=_label(country))
|
|
candidates = get('/search', q='city', bounded=1, extratags=1, limit=20,
|
|
viewbox=f'{longitude - SEARCH_RADIUS_DEGREES},{latitude + SEARCH_RADIUS_DEGREES},' +
|
|
f'{longitude + SEARCH_RADIUS_DEGREES},{latitude - SEARCH_RADIUS_DEGREES}')
|
|
points = []
|
|
for candidate in candidates[:20] if isinstance(candidates, list) else []:
|
|
if isinstance(candidate, dict) and _population(candidate) >= MINIMUM_POPULATION:
|
|
point = _point(candidate, state, country)
|
|
if point is not None:
|
|
points.append(point)
|
|
if points:
|
|
return min(points, key=lambda p: (p['latitude'] - latitude) ** 2 + (p['longitude'] - longitude) ** 2)
|
|
return fallback
|
|
except (requests.RequestException, OSError, TypeError, ValueError):
|
|
return fallback
|
|
finally:
|
|
if owned:
|
|
session.close()
|