def format_location_message(x, y, extra_text, html=False, extra=True, osm=True, gmaps=False): """Format coordinates of a point into a location message.""" if osm: osm_url = short_osm(y, x) if gmaps: gm = 'http://maps.google.com/?q={y:.5f},{x:.5f}'.format(x=x, y=y) plus = olc_encode(y, x) if html: if extra_text and extra: r = extra_text + '<br>' else: r = '' r += ('<a href="geo:{y:.5f},{x:.5f}">geo:{y:.5f},{x:.5f}</a>'.format( x=x, y=y)) r += ('<br>{desc}: {plus}'.format(desc=_("plus code"), plus=plus)) if osm: r += ('<br><a href="{osm}">{osm}</a>'.format(osm=osm_url)) if gmaps: r += ('<br><a href="{gm}">{gm}</a>'.format(gm=gm)) else: if extra_text and extra: r = extra_text + '; ' else: r = '' r += ('geo:{y:.5f},{x:.5f}'.format(x=x, y=y)) r += (' {desc}: {plus}'.format(desc=_("plus code"), plus=plus)) if osm: r += (' {osm}'.format(osm=osm_url)) if gmaps: r += (' {gm}'.format(gm=gm)) return r
def _process_route(self, route): if "maneuvers" in route: mnew = [] for m in route["maneuvers"]: sign = m.get("sign", None) street = m.get("street", None) if (street is None or len(street) == 0) and sign is not None: if "exit_toward" in sign and sign[ "exit_toward"] is not None and len( sign["exit_toward"]) > 0: street = "⇨ " + ("; ".join(sign["exit_toward"])) elif "exit_branch" in sign and sign[ "exit_branch"] is not None and len( sign["exit_branch"]) > 0: street = "⇨ " + ("; ".join(sign["exit_branch"])) elif "exit_number" in sign and sign[ "exit_number"] is not None and len( sign["exit_number"]) > 0: street = _("Exit: ") + ("; ".join(sign["exit_number"])) elif "exit_name" in sign and sign[ "exit_name"] is not None and len( sign["exit_name"]) > 0: street = _("Exit: ") + ("; ".join(sign["exit_name"])) elif street is not None and len(street) > 0: street = "; ".join(street) mn = copy.deepcopy(m) mn["street"] = street mnew.append(mn) route["maneuvers"] = mnew
def geocode(self, query, params=None, x=0, y=0): """ Return a list of dictionaries of places matching `query`. `params` can be used to specify a dictionary of geocoder-specific parameters. If the current position as `x` and `y` are provided, the results will include correct distance and bearing. """ params = params or {} # Parse position if query is a geo URI. match = RE_GEO_URI.search(query) if match is not None: qy, qx = map(float, match.groups()) return [ dict(title=_("Point from geo link"), description=match.group(0), x=qx, y=qy, distance=self._format_distance(x, y, qx, qy)) ] try: results = self._provider.geocode(query, params) except socket.timeout: return dict(error=True, message=_("Connection timed out")) except Exception: print("Geocoding failed:", file=sys.stderr) traceback.print_exc() return [] for result in results: result["distance"] = self._format_distance(x, y, result["x"], result["y"]) return results
def format_time(seconds): """Format `seconds` to format ``# h # min``.""" seconds = 60 * round(seconds / 60) hours = int(seconds / 3600) minutes = round((seconds % 3600) / 60) if hours == 0: return _("{:d} min").format(minutes) return _("{:d} h {:d} min").format(hours, minutes)
def format_time(seconds): """Format `seconds` to format ``# h # min``.""" seconds = 60 * round(seconds/60) hours = int(seconds/3600) minutes = round((seconds % 3600) / 60) if hours == 0: return _("{:d} min").format(minutes) return _("{:d} h {:d} min").format(hours, minutes)
def geocode(self, query, params=None, x=0, y=0): """ Return a list of dictionaries of places matching `query`. `params` can be used to specify a dictionary of geocoder-specific parameters. If the current position as `x` and `y` are provided, the results will include correct distance and bearing. """ params = params or {} # Parse coordinates if query is a geo URI. match = RE_GEO_URI.search(query) if match is not None: qy = float(match.group(1)) qx = float(match.group(2)) return [dict(title=_("Point from geo link"), description=match.group(0), x=qx, y=qy, distance=self._format_distance(x, y, qx, qy), provider=self.id)] # Parse coordinates if query is "LAT,LON". match = RE_LAT_LON.search(query) if match is not None: qy = float(match.group(1)) qx = float(match.group(3)) return [dict(title=_("Point from coordinates"), description=match.group(0), x=qx, y=qy, distance=self._format_distance(x, y, qx, qy), provider=self.id)] # Parse if query is a Plus code qtrimmed = query.strip() if olc_isFull(qtrimmed): latlng = olc_decode(qtrimmed).latlng() return [dict(title=qtrimmed.upper(), description=_("Point from Plus code"), x=latlng[1], y=latlng[0], distance=self._format_distance(x, y, latlng[1], latlng[0]), provider=self.id)] try: results = self._provider.geocode(query, params) except socket.timeout: return dict(error=True, message=_("Connection timed out")) except Exception: print("Geocoding failed:", file=sys.stderr) traceback.print_exc() return [] for result in results: result["distance"] = self._format_distance( x, y, result["x"], result["y"]) result["provider"] = self.id return results
def format_distance_metric(meters, n=2, short=True, lang=None): """Format `meters` to `n` significant digits and unit label.""" if meters >= 1000: distance = format_decimal(meters / 1000, 0, lang) units = _("km") if short else _("kilometers") return "{distance} {units}".format(**locals()) else: distance = format_decimal(meters, n, lang) units = _("m") if short else _("meters") return "{distance} {units}".format(**locals())
def format_distance_american(feet, n=2, short=True, lang=None): """Format `feet` to `n` significant digits and unit label.""" if (n > 1 and feet >= 1000) or feet >= 5280: distance = format_decimal(feet / 5280, 0, lang) units = _("mi") if short else _("miles") return "{distance} {units}".format(**locals()) else: distance = format_decimal(feet, n, lang) units = _("ft") if short else _("feet") return "{distance} {units}".format(**locals())
def format_distance_british(yards, n=2, short=True, lang=None): """Format `yards` to `n` significant digits and unit label.""" if (n > 1 and yards >= 400) or yards >= 1760: distance = format_decimal(yards / 1760, 0, lang) units = _("mi") if short else _("miles") return "{distance} {units}".format(**locals()) else: distance = format_decimal(yards, n, lang) units = _("yd") if short else _("yards") return "{distance} {units}".format(**locals())
def get_display(self, x, y, accuracy=None, navigating=False): """Return a dictionary of status details to display.""" if not self.ready: return None if self.mode == "transit": return self._get_display_transit(x, y) node = self._get_closest_segment_node(x, y) seg_dists = self._get_distances_from_route(x, y, node) seg_dist = min(seg_dists) dest_dist, dest_time = self._get_display_destination( x, y, node, seg_dist) progress = (max(self.time) - dest_time) / max(self.time) dest_dist = poor.util.format_distance(dest_dist) dest_eta = (datetime.datetime.now()+datetime.timedelta(seconds=dest_time)).strftime("%H:%M") dest_time = poor.util.format_time(dest_time) man = self._get_display_maneuver(x, y, node, seg_dists) man_node, man_dist, man_time, icon, narrative, sign, street = man if (street is None or len(street)==0) and sign is not None: if "exit_toward" in sign and sign["exit_toward"] is not None and len(sign["exit_toward"]) > 0: street = ["⇨ " + ("; ".join(sign["exit_toward"]))] elif "exit_branch" in sign and sign["exit_branch"] is not None and len(sign["exit_branch"]) > 0: street = ["⇨ " + ("; ".join(sign["exit_branch"]))] elif "exit_number" in sign and sign["exit_number"] is not None and len(sign["exit_number"]) > 0: street = [_("Exit: ") + ("; ".join(sign["exit_number"]))] elif "exit_name" in sign and sign["exit_name"] is not None and len(sign["exit_name"]) > 0: street = [_("Exit: ") + ("; ".join(sign["exit_name"]))] sign = ( sign if seg_dist < 100 and navigating and (man_dist < 500 or man_time < 300) else None) voice_uri = ( self._get_voice_uri(man_node, man_dist, man_time) if seg_dist < 100 and navigating else None) man_dist = poor.util.format_distance(man_dist) man_time = poor.util.format_time(man_time) if seg_dist > 100: # Don't show the narrative or details calculated # from nodes along the route if far off route. dest_time = man_time = icon = narrative = sign = street = None # Don't provide route direction to auto-rotate by if off route. direction = self._get_direction(x, y, node) if seg_dist < 50 else None # Trigger rerouting if off route (usually after missed a turn). reroute = seg_dist > 100 + (accuracy or 40000000) return dict(total_dist=poor.util.format_distance(max(self.dist)), total_time=poor.util.format_time(max(self.time)), dest_dist=dest_dist, dest_eta=dest_eta, dest_time=dest_time, man_dist=man_dist, man_time=man_time, progress=progress, icon=icon, narrative=narrative, direction=direction, voice_uri=voice_uri, reroute=reroute, sign=sign, street=street)
def parse_extras(result): """Parse description from geocoding result.""" description = "" with poor.util.silent(Exception): t = ", ".join([c.name for c in result.foodTypes]) description = merge(description, t, categ=_("Food types: {}")) with poor.util.silent(Exception): t = [] for i in result.openingHours: if "isOpen" not in i or i.isOpen: t.extend(i.text) t = "; ".join(t) description = merge(description, t, categ=_("Opening hours: {}")) return description
def nearby(self, query, near, radius, params=None): """ Return a list of dictionaries of places matching `query`. `near` can be either a string (usually an address) or a two-element tuple or list of (x,y) coordinates. `radius` should be meters around `near` to search places for. `params` can be used to specify a dictionary of guide-specific parameters. """ params = params or {} try: x, y, results = self._provider.nearby(query, near, radius, params) except socket.timeout: return dict(error=True, message=_("Connection timed out")) except Exception: print("Nearby failed:", file=sys.stderr) traceback.print_exc() return [] for result in results: result["distance"] = poor.util.calculate_distance( x, y, result["x"], result["y"]) # Enforce radius in case the provider didn't. results = [z for z in results if z["distance"] <= radius] for result in results: result["distance"] = self._format_distance( x, y, result["x"], result["y"]) return results
def reverse(self, x, y, radius, limit=1, params=None): """ Return a closest object near given coordinates. `params` can be used to specify a dictionary of geocoder-specific parameters. """ params = params or {} try: results = self._provider.reverse(x=x, y=y, radius=radius, limit=limit, params=params) except socket.timeout: return dict(error=True, message=_("Connection timed out")) except Exception: print("Geocoding failed:", file=sys.stderr) traceback.print_exc() return [] results_filtered = [] for result in results: if "distance" not in result: result["distance"] = poor.util.calculate_distance( x, y, result["x"], result["y"]) if result["distance"] < radius: result["provider"] = self.id results_filtered.append(result) return results_filtered
def route(self, locations, params=dict()): """Find route and return its properties as a dictionary. `locations` is a list of either strings (usually addresses) or two-element tuples or lists of (x,y) coordinates. `heading` is the initial direction as an angle with zero being north, increasing clockwise, with 360 being north again. `heading` is mostly useful for rerouting, to avoid suggesting U-turns, and will be ``None`` in non-rerouting context. `params` can be used to specify a dictionary of router-specific parameters. """ params = AttrDict(params) try: route = self._provider.route(locations=locations, params=params) except socket.timeout: return dict(error=True, message=_("Connection timed out")) except Exception: print("Routing failed:", file=sys.stderr) traceback.print_exc() return {} if isinstance(route, dict): route["provider"] = self.id self._process_route(route) if isinstance(route, list): for alternative in route: if isinstance(alternative, dict): alternative["provider"] = self.id self._process_route(alternative) return route
def nearby(self, query, near, radius, params=None): """ Return a list of dictionaries of places matching `query`. `near` can be either a string (usually an address) or a two-element tuple or list of (x,y) coordinates. `radius` should be meters around `near` to search places for. `params` can be used to specify a dictionary of guide-specific parameters. """ params = params or {} try: x, y, results = self._provider.nearby(query, near, radius, params) except socket.timeout: return dict(error=True, message=_("Connection timed out")) except Exception: print("Nearby failed:", file=sys.stderr) traceback.print_exc() return [] for result in results: if "distance" not in result: result["distance"] = poor.util.calculate_distance( x, y, result["x"], result["y"]) result["provider"] = self.id for result in results: result["distance"] = self._format_distance( x, y, result["x"], result["y"], result["distance"]) return results
def parse_text(venue): """Parse blurb text from venue details.""" lines = [] subtitle = [] with poor.util.silent(Exception): subtitle.append(('{:.1f} / 10').format(venue.rating)) lines.append(" ".join(subtitle)) with poor.util.silent(Exception): tip = parse_tip(venue) or venue.get("description") or "" if not tip: raise ValueError("No tip") lines.append("{}".format(tip)) lines.append("") with poor.util.silent(Exception): hours = venue.hours open_lines = [] for i in hours.timeframes: s = i.days + ": " for j in i.open: s += j.renderedTime + " " open_lines.append(s.strip()) if len(open_lines) > 0: lines.append(_("Opening hours:")) lines.extend(open_lines) lines.append("") with poor.util.silent(Exception): for i in venue.attributes.groups: for j in i['items']: if j.displayName != j.displayValue: lines.append("{}: {}".format(j.displayName, j.displayValue)) else: lines.append(j.displayName) return "\n".join(lines)
def format_distance_metric(meters, n=2, short=True): """Format `meters` to `n` significant digits and unit label.""" if meters >= 1000: distance = meters / 1000 ndigits = n - get_ndigits(distance) distance = round(distance, ndigits) distance = "{{:.{:d}f}}".format(max(0, ndigits)).format(distance) units = _("km") if short else _("kilometers") return "{distance} {units}".format(**locals()) else: distance = meters ndigits = n - get_ndigits(distance) ndigits = min(0, ndigits) distance = round(distance, ndigits) distance = "{{:.{:d}f}}".format(max(0, ndigits)).format(distance) units = _("m") if short else _("meters") return "{distance} {units}".format(**locals())
def format_distance_american(feet, n=2, short=True): """Format `feet` to `n` significant digits and unit label.""" if (n > 1 and feet >= 1000) or feet >= 5280: distance = feet / 5280 ndigits = n - get_ndigits(distance) distance = round(distance, ndigits) distance = "{{:.{:d}f}}".format(max(0, ndigits)).format(distance) units = _("mi") if short else _("miles") return "{distance} {units}".format(**locals()) else: distance = feet ndigits = n - get_ndigits(distance) ndigits = min(0, ndigits) distance = round(distance, ndigits) distance = "{{:.{:d}f}}".format(max(0, ndigits)).format(distance) units = _("ft") if short else _("feet") return "{distance} {units}".format(**locals())
def format_distance_british(yards, n=2, short=True): """Format `yards` to `n` significant digits and unit label.""" if (n > 1 and yards >= 400) or yards >= 1760: distance = yards / 1760 ndigits = n - get_ndigits(distance) distance = round(distance, ndigits) distance = "{{:.{:d}f}}".format(max(0, ndigits)).format(distance) units = _("mi") if short else _("miles") return "{distance} {units}".format(**locals()) else: distance = yards ndigits = n - get_ndigits(distance) ndigits = min(0, ndigits) distance = round(distance, ndigits) distance = "{{:.{:d}f}}".format(max(0, ndigits)).format(distance) units = _("yd") if short else _("yards") return "{distance} {units}".format(**locals())
def format_distance_british(yards, n=2, short=True): """Format `yards` to `n` significant digits and unit label.""" if (n > 1 and yards >= 400) or yards >= 1760: distance = yards / 1760 units = "mi" else: # Let's not use units less than a yard. distance = yards units = "yd" ndigits = n - math.ceil(math.log10(abs(max(1, distance)) + 1/1000000)) if units == "yd": ndigits = min(0, ndigits) distance = round(distance, ndigits) if not short: units = re.sub("^mi$", _("miles"), units) units = re.sub("^yd$", _("yards"), units) fstring = "{{:.{:d}f}} {{}}".format(max(0, ndigits)) return fstring.format(distance, units)
def format_distance_metric(meters, n=2, short=True): """Format `meters` to `n` significant digits and unit label.""" if meters >= 1000: distance = meters / 1000 units = "km" else: # Let's not use units less than a meter. distance = meters units = "m" ndigits = n - math.ceil(math.log10(abs(max(1, distance)) + 1/1000000)) if units == "m": ndigits = min(0, ndigits) distance = round(distance, ndigits) if not short: units = re.sub("^m$", _("meters"), units) units = re.sub("^km$", _("kilometers"), units) fstring = "{{:.{:d}f}} {{}}".format(max(0, ndigits)) return fstring.format(distance, units)
def format_distance_american(feet, n=2, short=True): """Format `feet` to `n` significant digits and unit label.""" if (n > 1 and feet >= 1000) or feet >= 5280: distance = feet / 5280 units = "mi" else: # Let's not use units less than a foot. distance = feet units = "ft" ndigits = n - math.ceil(math.log10(abs(max(1, distance)) + 1/1000000)) if units == "ft": ndigits = min(0, ndigits) distance = round(distance, ndigits) if not short: units = re.sub("^mi$", _("miles"), units) units = re.sub("^ft$", _("feet"), units) fstring = "{{:.{:d}f}} {{}}".format(max(0, ndigits)) return fstring.format(distance, units)
def format_distance_metric(meters, n=2, short=True): """Format `meters` to `n` significant digits and unit label.""" if meters >= 1000: distance = meters / 1000 units = "km" else: # Let's not use units less than a meter. distance = meters units = "m" ndigits = n - math.ceil(math.log10(abs(max(1, distance)) + 1 / 1000000)) if units == "m": ndigits = min(0, ndigits) distance = round(distance, ndigits) if not short: units = re.sub("^m$", _("meters"), units) units = re.sub("^km$", _("kilometers"), units) fstring = "{{:.{:d}f}} {{}}".format(max(0, ndigits)) return fstring.format(distance, units)
def format_distance_american(feet, n=2, short=True): """Format `feet` to `n` significant digits and unit label.""" if (n > 1 and feet >= 1000) or feet >= 5280: distance = feet / 5280 units = "mi" else: # Let's not use units less than a foot. distance = feet units = "ft" ndigits = n - math.ceil(math.log10(abs(max(1, distance)) + 1 / 1000000)) if units == "ft": ndigits = min(0, ndigits) distance = round(distance, ndigits) if not short: units = re.sub("^mi$", _("miles"), units) units = re.sub("^ft$", _("feet"), units) fstring = "{{:.{:d}f}} {{}}".format(max(0, ndigits)) return fstring.format(distance, units)
def format_distance_british(yards, n=2, short=True): """Format `yards` to `n` significant digits and unit label.""" if (n > 1 and yards >= 400) or yards >= 1760: distance = yards / 1760 units = "mi" else: # Let's not use units less than a yard. distance = yards units = "yd" ndigits = n - math.ceil(math.log10(abs(max(1, distance)) + 1 / 1000000)) if units == "yd": ndigits = min(0, ndigits) distance = round(distance, ndigits) if not short: units = re.sub("^mi$", _("miles"), units) units = re.sub("^yd$", _("yards"), units) fstring = "{{:.{:d}f}} {{}}".format(max(0, ndigits)) return fstring.format(distance, units)
def format_distance_and_bearing(meters, bearing, n=2, short=True): """Format `meters` and `bearing` to a human readable string.""" distance = format_distance(meters, n, short) f = lambda x: x.format(distance=distance) bearing = (bearing + 360) % 360 bearing = int(round(bearing/45) * 45) if bearing == 0: return f(_("{distance} north")) if bearing == 45: return f(_("{distance} north-east")) if bearing == 90: return f(_("{distance} east")) if bearing == 135: return f(_("{distance} south-east")) if bearing == 180: return f(_("{distance} south")) if bearing == 225: return f(_("{distance} south-west")) if bearing == 270: return f(_("{distance} west")) if bearing == 315: return f(_("{distance} north-west")) if bearing == 360: return f(_("{distance} north")) raise ValueError("Unexpected bearing: {}".format(repr(bearing)))
def format_distance_and_bearing(meters, bearing, n=2, short=True): """Format `meters` and `bearing` to a human readable string.""" distance = format_distance(meters, n, short) f = lambda x: x.format(distance=distance) bearing = (bearing + 360) % 360 bearing = int(round(bearing / 45) * 45) if bearing == 0: return f(_("{distance} north")) if bearing == 45: return f(_("{distance} north-east")) if bearing == 90: return f(_("{distance} east")) if bearing == 135: return f(_("{distance} south-east")) if bearing == 180: return f(_("{distance} south")) if bearing == 225: return f(_("{distance} south-west")) if bearing == 270: return f(_("{distance} west")) if bearing == 315: return f(_("{distance} north-west")) if bearing == 360: return f(_("{distance} north")) raise ValueError("Unexpected bearing: {}".format(repr(bearing)))
def route(self, fm, to, params=None): """ Find route and return its properties as a dictionary. `fm` and `to` can be either strings (usually addresses) or two-element tuples or lists of (x,y) coordinates. `params` can be used to specify a dictionary of router-specific parameters. """ params = params or {} try: return self._provider.route(fm, to, params) except socket.timeout: return dict(error=True, message=_("Connection timed out")) except Exception: print("Routing failed:", file=sys.stderr) traceback.print_exc() return {}
def parse_maneuvers(route): """Parse list of maneuvers from the legs of `route`.""" if not route.legs: return [] maneuvers = [] prev_mode = "w" modes = dict(WALK="w", BICYCLE="b") for i, leg in enumerate(route.legs): this_mode = modes.get(leg.mode, "t") key = prev_mode + this_mode # Handle the last leg differently since OpenTripPlanner # gives "Destination" as the destination name. if i == len(route.legs) - 1: key = key[0] + "a" narrative = NARRATIVE[key].format(**leg) narrative = re.sub(r"\.\.+$", ".", narrative) maneuvers.append( poor.AttrDict(x=leg.dep_x, y=leg.dep_y, icon="flag", narrative=narrative, duration=leg.duration)) if this_mode == "t": # Add intermediate stops as passive maneuver points. maneuvers.extend([ poor.AttrDict( x=leg.stops_x[i], y=leg.stops_y[i], passive=True, ) for i in range(len(leg.stops_x)) ]) prev_mode = this_mode maneuvers.append( poor.AttrDict(x=route.legs[-1].arr_x, y=route.legs[-1].arr_y, icon="flag", narrative=_("Arrive at your destination."), duration=0)) # For clarity, move stops to the nearest point # on the route polyline. for maneuver in maneuvers: i = poor.util.find_closest(route.x, route.y, maneuver.x, maneuver.y) maneuver.x = route.x[i] maneuver.y = route.y[i] return maneuvers
def parse_maneuvers(route): """Parse list of maneuvers from the legs of `route`.""" if not route.legs: return [] maneuvers = [] prev_mode = "w" modes = dict(WALK="w", BICYCLE="b") for i, leg in enumerate(route.legs): this_mode = modes.get(leg.mode, "t") key = prev_mode + this_mode # Handle the last leg differently since OpenTripPlanner # gives "Destination" as the destination name. if i == len(route.legs) - 1: key = key[0] + "a" narrative = NARRATIVE[key].format(**leg) narrative = re.sub(r"\.\.+$", ".", narrative) maneuvers.append(poor.AttrDict( x=leg.dep_x, y=leg.dep_y, icon="flag", narrative=narrative, duration=leg.duration)) if this_mode == "t": # Add intermediate stops as passive maneuver points. maneuvers.extend([poor.AttrDict( x=leg.stops_x[i], y=leg.stops_y[i], passive=True, ) for i in range(len(leg.stops_x))]) prev_mode = this_mode maneuvers.append(poor.AttrDict( x=route.legs[-1].arr_x, y=route.legs[-1].arr_y, icon="flag", narrative=_("Arrive at your destination."), duration=0)) # For clarity, move stops to the nearest point # on the route polyline. for maneuver in maneuvers: i = poor.util.find_closest(route.x, route.y, maneuver.x, maneuver.y) maneuver.x = route.x[i] maneuver.y = route.y[i] return maneuvers
def route(self, fm, to, heading=None, params=None): """Find route and return its properties as a dictionary. `fm` and `to` can be either strings (usually addresses) or two-element tuples or lists of (x,y) coordinates. `heading` is the initial direction as an angle with zero being north, increasing clockwise, with 360 being north again. `heading` is mostly useful for rerouting, to avoid suggesting U-turns, and will be ``None`` in non-rerouting context. `params` can be used to specify a dictionary of router-specific parameters. """ params = params or {} try: return self._provider.route(fm, to, heading, params) except socket.timeout: return dict(error=True, message=_("Connection timed out")) except Exception: print("Routing failed:", file=sys.stderr) traceback.print_exc() return {}
class HistoryManager: """Managing a history of search queries.""" _places_blacklist = ["Current position", _("Current position")] def __init__(self): """Initialize a :class:`HistoryManager` instance.""" self._path = os.path.join(poor.CONFIG_HOME_DIR, "search-history.json") self._place_names = [] self._place_types = [] self._places = [] self._read() def add_place(self, place): """Add `place` to the list of places.""" place = place.strip() if not place: return if place in self._places_blacklist: return self.remove_place(place) self._places.insert(0, place) def add_place_name(self, place_name): """Add `place_name` to the list of place names.""" place_name = place_name.strip() if not place_name: return self.remove_place_name(place_name) self._place_names.insert(0, place_name) def add_place_type(self, place_type): """Add `place_type` to the list of place types.""" place_type = place_type.strip() if not place_type: return self.remove_place_type(place_type) self._place_types.insert(0, place_type) @property def place_names(self): """Return a list of place names.""" return self._place_names[:] @property def place_types(self): """Return a list of place types.""" return self._place_types[:] @property def places(self): """Return a list of places.""" return self._places[:] def _read(self): """Read list of queries from file.""" with poor.util.silent(Exception, tb=True): if os.path.isfile(self._path): history = poor.util.read_json(self._path) self._places = history.get("places", []) self._place_names = history.get("place_names", []) self._place_types = history.get("place_types", []) for place in self._places_blacklist: self.remove_place(place) if not self._place_types: # Provide some examples of place types. self._place_types = [ "ATM", "Café", "Gas station", "Grocery store", "Hotel", "Pub", "Restaurant" ] def remove_place(self, place): """Remove `place` from the list of places.""" place = place.strip().lower() for i in reversed(range(len(self._places))): if self._places[i].lower() == place: del self._places[i] def remove_place_name(self, place_name): """Remove `place_name` from the list of place names.""" place_name = place_name.strip().lower() for i in reversed(range(len(self._place_names))): if self._place_names[i].lower() == place_name: del self._place_names[i] def remove_place_type(self, place_type): """Remove `place_type` from the list of place types.""" place_type = place_type.strip().lower() for i in reversed(range(len(self._place_types))): if self._place_types[i].lower() == place_type: del self._place_types[i] def write(self): """Write list of queries to file.""" with poor.util.silent(Exception, tb=True): poor.util.write_json( { "places": self._places[:1000], "place_names": self._place_names[:1000], "place_types": self._place_types[:1000], }, self._path)
def get_routing_attribution(service, engine=None): """Return translated routing attribution string.""" if engine is None: return _("Routing courtesy of {}.").format(service) return (_("Routing by {engine}, courtesy of {service}.") .format(engine=engine, service=service))
# maptiler.com "MAPTILER_KEY", # http://open.mapquestapi.com "MAPQUEST_KEY", # https://geocoder.opencagedata.com/api "OPENCAGE_KEY", # https://docs.stadiamaps.com/ "STADIAMAPS_KEY" ] HEADERS = { "FOURSQUARE": HeaderDesc(_('Register at <a href="https://developer.foursquare.com">https://developer.foursquare.com</a> and create your own Client ID and Client Secret keys'), "Foursquare"), "MAPBOX": HeaderDesc(_('Register at <a href="https://www.mapbox.com">https://www.mapbox.com</a> and create your own API key'), "Mapbox"), "MAPTILER": HeaderDesc(_('Register at <a href="https://maptiler.com">https://maptiler.com</a> and create your own API key'), "MapTiler"), "MAPQUEST": HeaderDesc(_('Register at <a href="https://developer.mapquest.com">https://developer.mapquest.com</a> and create your own API key'), "MapQuest"), "OPENCAGE": HeaderDesc(_('Register at <a href="https://opencagedata.com">https://opencagedata.com</a> and create your own API key'), "OpenCage"), "STADIAMAPS": HeaderDesc(_('Register at <a href="https://stadiamaps.com">https://stadiamaps.com</a> and create your own API key'), "Stadia Maps"), "HERE": HeaderDesc(_('Register at <a href="https://developer.here.com">https://developer.here.com</a> and create your own App API Key'), "HERE") } KEYDESC = { "FOURSQUARE_CLIENT": _("Foursquare Client ID"), "FOURSQUARE_SECRET": _("Foursquare Client Secret"), # mapbox.com "MAPBOX_KEY": _("Mapbox API key"),
def translate(value): if isinstance(value, list): return list(map(translate, value)) return _(value)
class HistoryManager: """Managing a history of search queries and destinations.""" _places_blacklist = ["Current position", _("Current position")] def __init__(self): """Initialize a :class:`HistoryManager` instance.""" self._destinations = [] self._path = os.path.join(poor.CONFIG_HOME_DIR, "search-history.json") self._place_names = [] self._place_types = [] self._places = [] self._routes = [] self._read() def add_destination(self, dest): """Add `dest` to the history list of destinations.""" d = {'text': dest['text'].strip(), 'x': dest['x'], 'y': dest['y']} if not d['text']: return self.remove_destination(d['text']) self._destinations.insert(0, d) def add_place(self, place): """Add `place` to the list of places.""" place = place.strip() if not place: return if place in self._places_blacklist: return self.remove_place(place) self._places.insert(0, place) def add_place_name(self, place_name): """Add `place_name` to the list of place names.""" place_name = place_name.strip() if not place_name: return self.remove_place_name(place_name) self._place_names.insert(0, place_name) def add_place_type(self, place_type): """Add `place_type` to the list of place types.""" place_type = place_type.strip() if not place_type: return self.remove_place_type(place_type) self._place_types.insert(0, place_type) def add_route(self, route): """Add `route` to the list of routes.""" route = AttrDict(route) for r in route.locations: r['text'] = r['text'].strip() if len(route.locations) < 2 or not route.locations[0][ 'text'] or not route.locations[-1]['text']: return self.remove_route(route) self._routes.insert(0, route) def clear(self): """Clear all history""" self._destinations = [] self._place_names = [] self._place_types = [] self._places = [] self._routes = [] self.write() @property def destinations(self): """Return a list of destinations.""" return self._destinations[:] @property def place_names(self): """Return a list of place names.""" return self._place_names[:] @property def place_types(self): """Return a list of place types.""" return self._place_types[:] @property def places(self): """Return a list of places.""" return self._places[:] def _read(self): """Read list of queries, destinations, and routes from file.""" with poor.util.silent(Exception, tb=True): if os.path.isfile(self._path): history = poor.util.read_json(self._path) self._destinations = history.get("destinations", []) self._places = history.get("places", []) self._place_names = history.get("place_names", []) self._place_types = history.get("place_types", []) self._routes = history.get("routes", []) for place in self._places_blacklist: self.remove_place(place) if not self._place_types: # Provide some examples of place types. self._place_types = [ "ATM", "Café", "Gas station", "Grocery store", "Hotel", "Pub", "Restaurant" ] # convert old routes format to the new one for i in range(len(self._routes)): if isinstance(self._routes[i], dict) and "from" in self._routes[i]: # old format, pre 2.2 r = self._routes[i] self._routes[i] = dict(locations=[r['from'], r['to']], optimized=False) elif isinstance(self._routes[i], list): # old format, 2.2 self._routes[i] = dict(locations=self._routes[i], optimized=False) self._routes[i] = AttrDict(self._routes[i]) @property def routes(self): """Return a list of routes.""" return self._routes[:] def remove_destination(self, dtxt): """Remove destination with the text `dtxt` from the list of destinations.""" t = dtxt.strip() for i in reversed(range(len(self._destinations))): if self._destinations[i]['text'] == t: del self._destinations[i] def remove_place(self, place): """Remove `place` from the list of places.""" place = place.strip().lower() for i in reversed(range(len(self._places))): if self._places[i].lower() == place: del self._places[i] def remove_place_name(self, place_name): """Remove `place_name` from the list of place names.""" place_name = place_name.strip().lower() for i in reversed(range(len(self._place_names))): if self._place_names[i].lower() == place_name: del self._place_names[i] def remove_place_type(self, place_type): """Remove `place_type` from the list of place types.""" place_type = place_type.strip().lower() for i in reversed(range(len(self._place_types))): if self._place_types[i].lower() == place_type: del self._place_types[i] def remove_route(self, route): """Remove route with the same text for origin and target from the list of routes.""" def rkey(route): return ' - '.join([r['text'] for r in route.locations ]) + ('#opt' if route.optimized else '#no') if isinstance(route, dict): route = AttrDict(route) key = rkey(route) for i in reversed(range(len(self._routes))): if rkey(self._routes[i]) == key: del self._routes[i] def write(self): """Write list of queries to file.""" with poor.util.silent(Exception, tb=True): poor.util.write_json( { "destinations": self._destinations[:25], "places": self._places[:1000], "place_names": self._place_names[:1000], "place_types": self._place_types[:1000], "routes": self._routes[:25] }, self._path)
def get_routing_attribution(service, engine=None): """Return translated routing attribution string.""" if engine is None: return _("Routing courtesy of {}.").format(service) return (_("Routing by {engine}, courtesy of {service}.").format( engine=engine, service=service))
"SUBWAY": "#ff6319", "TRAM": "#00985f", "WALK": "#888888", } CONF_DEFAULTS = { # See digitransit_settings.qml for all possible values. "modes": ["AIRPLANE", "BUS", "FERRY", "RAIL", "SUBWAY", "TRAM", "WALK"], "optimize": "default", "region": "hsl", } HEADERS = {"Content-Type": "application/graphql"} MODE_NAMES = { "AIRPLANE": _("airplane"), "BICYCLE": _("bicycle"), "BUS": _("bus"), "FERRY": _("ferry"), "RAIL": _("train"), "SUBWAY": _("metro"), "TRAM": _("tram"), "WALK": _("walk"), } NARRATIVE = { "ww": _("Walk towards {arr_name}."), "wb": _("Get a city bike at {dep_name}."), "wt": _("Board {mode_name} {line_desc} at {dep_name} at {dep_time}."), "wa": _("Walk towards your destination."), "bw": _("Leave the city bike at {dep_name} and walk towards {arr_name}."),
"SUBWAY": "#ff6319", "TRAM": "#00985f", "WALK": "#888888", } CONF_DEFAULTS = { # See digitransit_settings.qml for all possible values. "modes": ["AIRPLANE", "BUS", "FERRY", "RAIL", "SUBWAY", "TRAM", "WALK"], "optimize": "default", "region": "hsl", } HEADERS = {"Content-Type": "application/graphql"} MODE_NAMES = { "AIRPLANE": _("airplane"), "BICYCLE": _("bicycle"), "BUS": _("bus"), "FERRY": _("ferry"), "RAIL": _("train"), "SUBWAY": _("metro"), "TRAM": _("tram"), "WALK": _("walk"), } NARRATIVE = { "ww": _("Walk towards {arr_name}."), "wb": _("Get a city bike at {dep_name}."), "wt":