transitous rewrite: bug fixes in the info embed

This commit is contained in:
Kaaninchen
2026-08-15 22:55:57 +02:00
parent 697a0f6218
commit 46fe9eb61e
17 changed files with 853 additions and 399 deletions
+2
View File
@@ -3,3 +3,5 @@ __pycache__/
src/data/announcements/* src/data/announcements/*
!src/data/announcements/.gitkeep !src/data/announcements/.gitkeep
config.json config.json
src/legacy/
main_legacy.py
-9
View File
@@ -1,9 +0,0 @@
# Secrets
.env*
# Dependencies
node_modules
# OS files
.DS_Store
Thumbs.db
@@ -1,29 +0,0 @@
info:
name: Geocode München HBF
type: http
seq: 1
http:
method: GET
url: https://api.transitous.org/api/v1/geocode?text=München Hbf
headers:
- name: User-Agent
value: Gleiswechsel-Discord-Bot
params:
- name: text
value: München Hbf
type: query
auth: inherit
runtime:
scripts:
- type: after-response
code: |-
const data = res.getBody();
bru.setEnvVar("stop_id", data[0].id);
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
@@ -1,32 +0,0 @@
info:
name: Stoptimes for Station
type: http
seq: 3
http:
method: GET
url: https://api.transitous.org/api/v1/stoptimes?stopId={{stop_id}}&n=10
headers:
- name: User-Agent
value: Gleiswechsel-Discord-Bot
params:
- name: stopId
value: "{{stop_id}}"
type: query
- name: n
value: "10"
type: query
auth: inherit
runtime:
scripts:
- type: after-response
code: |-
const data = res.getBody();
bru.setEnvVar("trip_id", data.stopTimes[0].tripId);
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
-22
View File
@@ -1,22 +0,0 @@
info:
name: Trip Details
type: http
seq: 4
http:
method: GET
url: https://api.transitous.org/api/v2/trip?tripId={{trip_id}}
headers:
- name: User-Agent
value: Gleiswechsel-Discord-Bot
params:
- name: tripId
value: "{{trip_id}}"
type: query
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
@@ -1,6 +0,0 @@
name: Transitous API
variables:
- name: stop_id
value: at-Railway-Current-Reference-Data-2026_de:09162:100:11:11
- name: trip_id
value: 20260815_11:52_de-DELFI_3338623109
-10
View File
@@ -1,10 +0,0 @@
opencollection: 1.0.0
info:
name: Gleiswechsel-API
bundled: false
extensions:
bruno:
ignore:
- node_modules
- .git
+1 -1
View File
@@ -30,5 +30,5 @@ TODO
- text announcements - text announcements
- voice announcements - voice announcements
- improved error handling (retry connection) - improved error handling (retry connection)
- Footer Notice Slogangs - multi language support
''' '''
+12 -7
View File
@@ -1,6 +1,6 @@
import requests import requests
import random import random
from datetime import datetime import json
from src.utils import logger, config, get_train_name, convert_iso_string from src.utils import logger, config, get_train_name, convert_iso_string
@@ -73,10 +73,15 @@ def get_random_connection(stop_id: str) -> str:
logger("Couldn't find any connection", "fatal") logger("Couldn't find any connection", "fatal")
return None return None
trip_id = random.choice(trip_id) trip_id = random.choice(trip_ids)
from_station = stop_times[0]["place"]["name"] for trip in stop_times:
if trip.get("tripId") == trip_id:
from_station = trip.get("place").get("name")
break
print(from_station)
return { return {
"trip_id": random.choice(trip_ids), "trip_id": trip_id,
"from_station": from_station "from_station": from_station
} }
@@ -85,7 +90,7 @@ def get_trip_details(trip_id: str, from_station: str) -> dict:
try: try:
response = requests.get(req, headers=headers) response = requests.get(req, headers=headers)
response.raise_for_status response.raise_for_status()
data = response.json() data = response.json()
except requests.RequestException as e: except requests.RequestException as e:
logger(f"An error occured while trying to get the route details: {e}", "fatal") logger(f"An error occured while trying to get the route details: {e}", "fatal")
@@ -120,8 +125,8 @@ def get_trip_details(trip_id: str, from_station: str) -> dict:
trip_details["stops"][trip_from] = departure trip_details["stops"][trip_from] = departure
for stop in legs["intermediateStops"]: for stop in legs["intermediateStops"]:
arrival = convert_iso_string(stop["arrival"]) stop_arrival = convert_iso_string(stop["arrival"])
trip_details["stops"][stop["name"]] = arrival trip_details["stops"][stop["name"]] = stop_arrival
if stop.get("name") == from_station: if stop.get("name") == from_station:
departure_time = stop["departure"] departure_time = stop["departure"]
trip_details["departure"] = convert_iso_string(departure_time) trip_details["departure"] = convert_iso_string(departure_time)
-12
View File
@@ -1,12 +0,0 @@
import discord
from discord.ext import commands
from src.embeds import build_info_embed, build_error_embed
def setup_commands(bot: discord.Bot):
@bot.slash_command(description="Informationen über die aktuelle Fahrt")
async def info(ctx):
embed = build_info_embed()
if embed is None:
await build_error_embed("Noch keine Verbindung gesetzt.")
return
await ctx.respond(embed=embed)
-4
View File
@@ -1,4 +0,0 @@
import json
with open("config.json", "r") as file:
config = json.load(file)
+3 -1
View File
@@ -3,5 +3,7 @@ emoji_list = {
"BUS": "🚎", "BUS": "🚎",
"TRAM": "🚈", "TRAM": "🚈",
"REGIONAL_RAIL": "🚊", "REGIONAL_RAIL": "🚊",
"HIGHSPEED_RAIL": "🚅" "HIGHSPEED_RAIL": "🚅",
"METRO": "🚇",
"SUBWAY": "🚇"
} }
+2
View File
@@ -45,4 +45,6 @@ def build_info_embed() -> discord.Embed:
embed.set_author(name=agency) embed.set_author(name=agency)
embed.set_thumbnail(url=metadata["logo"]) embed.set_thumbnail(url=metadata["logo"])
color = trip.get("color", "keine farbe :(")
print(color)
return embed return embed
-109
View File
@@ -1,109 +0,0 @@
import random
import discord
from datetime import datetime, timedelta
import src.handlers as handlers
from src.config import config
from src.utils import operator_metadata, format_via_list, resolve_operator, logger
def format_timestamp(timestr):
parsed_time = datetime.strptime(timestr, "%H:%M")
now = datetime.now()
final_datetime = datetime.now().replace(
hour=parsed_time.hour,
minute=parsed_time.minute,
second=0,
microsecond=0
)
if final_datetime <= now:
final_datetime += timedelta(days=1)
return discord.utils.format_dt(final_datetime, style="t")
def format_iso_timestamp(isostr):
parsed_time = discord.utils.parse_time(isostr)
return discord.utils.format_dt(parsed_time, style="t")
def build_embed_footer(operator_slogans):
dbf = config.get("dbf", "https://dbf.finalrewind.org")
footer_notice = f"Daten großzügig bereitgestellt von {dbf} • Typ: {handlers.train_type}"
footer_text = (
f"{random.choice(operator_slogans)}{footer_notice}"
if operator_slogans else
footer_notice
)
icon = f"{dbf}/static/icons/icon-96x96.png"
return {
"text": footer_text,
"icon": icon
}
def build_info_embed() -> discord.Embed | None:
conn = handlers.current
info = handlers.train_info
if handlers.current is None and handlers.train_info is None:
return None
current_operator = resolve_operator(info["operators"])
arrival = format_iso_timestamp(info["arrival"])
departure = format_timestamp(conn['departure'])
operator_infos = operator_metadata(current_operator)
embed = discord.Embed(
title=handlers.train_name,
description=f"Abfahrt von {conn['station']} um {departure}. Ankunft um {arrival}",
color=operator_infos["color"]
)
if len(conn['via']) > 0:
embed.add_field(name="Über", value=format_via_list(conn['via']), inline=False)
if operator_infos.get("unknown"):
logger(f"Metadaten für {current_operator} konten in operators.py nicht gefunden werden")
embed.set_author(name=current_operator)
embed.set_thumbnail(url=operator_infos["logo"])
route_lines = []
for stop in conn['route']:
stop_name = stop["name"]
if stop_name == conn['station']:
route_lines.append(f"• **{stop_name}**")
else:
route_lines.append(f"{stop_name}")
embed.add_field(name="Route", value="\n".join(route_lines))
footer = build_embed_footer(operator_infos.get("slogan"))
embed.set_footer(text=footer["text"], icon_url=footer["icon"])
return embed
def build_announcement_embed(msg):
current_operator = resolve_operator(handlers.train_info["operators"])
operator_infos = operator_metadata(current_operator)
embed = discord.Embed(
title = "Informationen zu Ihrer Fahrt",
description=msg,
color=operator_infos["color"]
)
embed.set_author(name=current_operator)
embed.set_thumbnail(url=operator_infos["logo"])
footer = build_embed_footer(operator_infos.get("slogan"))
embed.set_footer(text=footer["text"], icon_url=footer["icon"])
return embed
def build_error_embed(errormsg) -> discord.Embed:
embed = discord.Embed(
title="Ein Fehler ist aufgetreten!",
description=errormsg,
color=discord.Colour.red()
)
return embed
-154
View File
@@ -1,154 +0,0 @@
import discord
from datetime import datetime, timedelta
import asyncio
import random
from pathlib import Path
from src.config import config
from src.utils import random_connection, get_train_info, get_channel_formatting, logger
from src.embeds import build_announcement_embed, build_info_embed
current = None
_scheduled_task: asyncio.Task | None = None
async def announcer(bot, announcement):
voice_channel = bot.get_channel(config["vc"])
if len(voice_channel.members) > 0:
match announcement:
case "ende":
destination = current['destination']
embed = build_announcement_embed(
f'Sehr geehrte Fahrgäste,\nIn wenigen Minuten erreichen wir {destination}. Dieser Zug endet dort.\n\nWir wünschen Ihnen eine angenehme Weiterreise.\n\nVielen Dank für Ihr Vertrauen und auf Wiedersehen.')
if config["voice_announcements"][0]["enabled"]:
await voice_announcer(bot, destination, voice_channel)
case "umstieg":
embed = build_info_embed()
case _:
embed = None
if embed is None:
logger(f"Unbekanntes Announcement: {announcement}")
else:
await voice_channel.send(embed=embed)
else:
logger(f"Announcement {announcement} wird geskipped, keiner da")
return
async def voice_announcer(bot: discord.Bot, destination, voice_channel):
voice_announcement_config = config["voice_announcements"][0]
voice_stations = voice_announcement_config["stations"]
if destination in voice_stations:
announcement_for = destination
else:
if voice_stations.get("general", "") == "":
return
announcement_for = "general"
if voice_stations.values() == list:
sound_file = random.choice(voice_stations.get(announcement_for))
else:
sound_file = voice_stations.get(announcement_for)
sound_path = f"src/data/announcements/{sound_file}"
if Path(sound_path).is_file() is False:
logger(f"Konnte Datei {sound_path} nicht finden", "error")
return
logger(f"VC wird betreten, spiele {sound_path}")
vc = await voice_channel.connect(timeout=15, reconnect=True)
audio_source = discord.FFmpegPCMAudio(sound_path)
if not vc.is_playing():
def after_playing(error):
if error:
logger(f"Player error: {error}", "error")
bot.loop.create_task(vc.disconnect())
logger("VC wird verlassen")
vc.play(audio_source, after=after_playing)
async def rename_vc(bot: discord.Bot, from_scheduler: bool = False):
global current, train_name, train_info, train_type, _scheduled_task
guild = bot.get_guild(int(config["server"]))
if guild is None:
logger(f"Es konnte kein Server mit der ID {config['server']} gefunden werden! Ist der Bot ein Member?", "fatal")
return False
channel = guild.get_channel(int(config["vc"]))
if not isinstance(channel, discord.VoiceChannel):
logger(f"Es konnte kein VC mit der ID {config['vc']} auf dem Server gefunden werden", "fatal")
return False
if not from_scheduler and _scheduled_task and not _scheduled_task.done():
_scheduled_task.cancel()
attempt = 0
while True:
if attempt == 20:
logger("Zu viele Fehlversuche. Füge einen anderen Bahnhof hinzu.", "fatal")
return "Es konnte kein Zug gefunden werden."
attempt += 1
current = random_connection()
if current == None:
return None
parts = current['train'].split()
train_type = parts[0]
if parts[1].isdigit():
train = current['train']
train_ID = parts[1]
else:
train = parts[1]
train_ID = current['train_number']
station = current['station']
train_info = get_train_info(station=station, train_ID=train_ID, train_type=train_type)
if train_info and train_info.get('operators') and train_info.get('arrival'):
break
logger(f"Versuch {attempt}: Fehler bei {current['train']} von {station}, versuche neue Verbindung...")
train_name = f"{train} nach {current['destination']} von {current['station']}"
logger(f"Vorbereitung auf {train_name} (typ: {train_type})")
arrival = datetime.fromisoformat((train_info["arrival"]))
formatting = get_channel_formatting(train_type)
print("-----------------------------------------")
logger(f"Umstieg: {train_name}")
logger(f"Betreiber: {''.join(train_info['operators'])}")
logger(f"Train-Type: {train_type}")
logger(f"Wenn der Name nicht geändert wird bin ich im Cooldown")
await channel.edit(name=f"{formatting}{train_name}")
await channel.set_status(f"Ankunft um {arrival.strftime('%H:%M')}")
logger(f"Name geändert!")
if config.get("announcements", True):
await announcer(bot, "umstieg")
_scheduled_task = asyncio.create_task(_schedule_next_umstieg(bot, arrival))
return True
async def _schedule_next_umstieg(bot, arrival):
announcement = config.get("announcements", True)
announcement_countdown = random.randrange(180, 300) # letzte station announcement ist meistens 3-5min vor ankunft
wait_seconds = (arrival - datetime.now()).total_seconds()
if wait_seconds > 0:
remaining = str(timedelta(seconds=wait_seconds))
logger(f"Nächster Umstieg in {remaining.split('.')[0]} ({arrival.strftime('%H:%M:%S')} Uhr)")
if announcement and wait_seconds > announcement_countdown:
wait_until_end_announcement = wait_seconds - announcement_countdown
await asyncio.sleep(wait_until_end_announcement)
await announcer(bot, "ende")
await asyncio.sleep(announcement_countdown)
else:
await asyncio.sleep(wait_seconds)
logger("Zug angekommen, wähle neue Verbindung...")
await rename_vc(bot, from_scheduler=True)
+6 -3
View File
@@ -1,6 +1,6 @@
import json import json
import os import os
from datetime import datetime from datetime import datetime, timedelta
import src.data.operators as operators import src.data.operators as operators
from src.data.emojis import emoji_list from src.data.emojis import emoji_list
@@ -17,8 +17,11 @@ def logger(msg, log_type="info") -> str:
os._exit(1) os._exit(1)
def convert_iso_string(isostring) -> str: def convert_iso_string(isostring) -> str:
datetime_isostring = datetime.fromisoformat(isostring) dt = datetime.fromisoformat(isostring.replace('Z', '+00:00'))
return datetime_isostring.strftime('%H:%M') if dt.second >= 30:
dt += timedelta(minutes=1)
return dt.strftime('%H:%M')
def channel_formatting(mode: str) -> str: def channel_formatting(mode: str) -> str:
formatting = config.get("formatting", "") formatting = config.get("formatting", "")
+827
View File
@@ -0,0 +1,827 @@
{
"duration": 3240,
"startTime": "2026-08-15T20:16:00Z",
"endTime": "2026-08-15T21:10:00Z",
"transfers": 0,
"id": "CqEBCgIxMhIiMjAyNjA4MTVfMjI6MTZfZGUtREVMRklfMzMwNTA4MDkwNhofZGUtREVMRklfZGU6MDIwMDA6MTAwNTY6OjEwMDAxOSH0ona/CsZKQClzRSkhWB0kQDIfZGUtREVMRklfZGU6MDIwMDA6ODAwMDU6OjgwMDA1NDk1e6AVGMZKQEGc/1cdObojQEiAkoPUBlCwqoPUBloDQlVTYAE=",
"legs": [
{
"mode": "BUS",
"from": {
"name": "Osterbrookplatz",
"stopId": "de-DELFI_de:02000:10056::100019",
"importance": 0.00038864786620251834,
"lat": 53.547202999999996,
"lon": 10.057312999999999,
"level": 0.0,
"tz": "Europe/Berlin",
"departure": "2026-08-15T20:16:00Z",
"scheduledDeparture": "2026-08-15T20:16:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 100017",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
"to": {
"name": "Teufelsbr\u00fcck (F\u00e4hre)",
"stopId": "de-DELFI_de:02000:80005::800054",
"importance": 0.001101788366213441,
"lat": 53.54761,
"lon": 9.863717000000001,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:10:00Z",
"scheduledArrival": "2026-08-15T21:08:00Z",
"scheduledTrack": "5",
"track": "5",
"description": "VHH 800019",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
"duration": 3240,
"startTime": "2026-08-15T20:16:00Z",
"endTime": "2026-08-15T21:10:00Z",
"scheduledStartTime": "2026-08-15T20:16:00Z",
"scheduledEndTime": "2026-08-15T21:08:00Z",
"realTime": true,
"scheduled": true,
"interlineWithPreviousLeg": false,
"headsign": "Teufelsbr\u00fcck (F\u00e4hre)",
"tripFrom": {
"name": "Osterbrookplatz",
"stopId": "de-DELFI_de:02000:10056::100019",
"importance": 0.00038864786620251834,
"lat": 53.547202999999996,
"lon": 10.057312999999999,
"level": 0.0,
"tz": "Europe/Berlin",
"departure": "2026-08-15T20:16:00Z",
"scheduledDeparture": "2026-08-15T20:16:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 100017",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
"tripTo": {
"name": "Teufelsbr\u00fcck (F\u00e4hre)",
"stopId": "de-DELFI_de:02000:80005::800054",
"importance": 0.001101788366213441,
"lat": 53.54761,
"lon": 9.863717000000001,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:10:00Z",
"scheduledArrival": "2026-08-15T21:08:00Z",
"scheduledTrack": "5",
"track": "5",
"description": "VHH 800019",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
"routeId": "de-DELFI_de:hvv:12:_3",
"routeUrl": "",
"directionId": "1",
"routeColor": "ca3d2a",
"routeTextColor": "ffffff",
"routeType": 3,
"agencyName": "Hochbahn Bus",
"agencyUrl": "",
"agencyFareUrl": "",
"agencyId": "15140",
"tripId": "20260815_22:16_de-DELFI_3305080906",
"routeShortName": "12",
"routeLongName": "",
"tripShortName": "1200086",
"displayName": "12",
"cancelled": false,
"source": "de_DELFI.gtfs.zip/stop_times.txt:33330623:33330654",
"intermediateStops": [
{
"name": "Schadesweg",
"stopId": "de-DELFI_de:02000:10042::100197",
"importance": 0.00038864786620251834,
"lat": 53.547714,
"lon": 10.05187,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:17:00Z",
"departure": "2026-08-15T20:17:00Z",
"scheduledArrival": "2026-08-15T20:17:00Z",
"scheduledDeparture": "2026-08-15T20:17:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 100196",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Luisenweg",
"stopId": "de-DELFI_de:02000:10041::100224",
"importance": 0.00038864786620251834,
"lat": 53.548298,
"lon": 10.046362999999998,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:18:00Z",
"departure": "2026-08-15T20:18:00Z",
"scheduledArrival": "2026-08-15T20:18:00Z",
"scheduledDeparture": "2026-08-15T20:18:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 100195",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Ausschl\u00e4ger Weg (LBV)",
"stopId": "de-DELFI_de:02000:10040::100181",
"importance": 0.001163465902209282,
"lat": 53.547276,
"lon": 10.037443,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:22:00Z",
"departure": "2026-08-15T20:22:00Z",
"scheduledArrival": "2026-08-15T20:20:00Z",
"scheduledDeparture": "2026-08-15T20:20:00Z",
"scheduledTrack": "1",
"track": "1",
"description": "HHA-B 100181",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "S\u00fcderstra\u00dfe",
"stopId": "de-DELFI_de:02000:10039::100172",
"importance": 0.0010683928849175572,
"lat": 53.545147,
"lon": 10.029789000000001,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:22:00Z",
"departure": "2026-08-15T20:22:00Z",
"scheduledArrival": "2026-08-15T20:21:00Z",
"scheduledDeparture": "2026-08-15T20:21:00Z",
"scheduledTrack": "3",
"track": "3",
"description": "HHA-B 100164",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "S Hammerbrook (S\u00fcd)",
"stopId": "de-DELFI_de:02000:10038::100207",
"importance": 0.0006706778658553958,
"lat": 53.54553200000001,
"lon": 10.024811999999999,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:24:00Z",
"departure": "2026-08-15T20:24:00Z",
"scheduledArrival": "2026-08-15T20:22:00Z",
"scheduledDeparture": "2026-08-15T20:22:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "SBAHNS 3921901",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "S Hammerbrook (Nord)",
"stopId": "de-DELFI_de:02000:10060::100030",
"importance": 0.00039489471237175167,
"lat": 53.547832,
"lon": 10.02298,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:25:00Z",
"departure": "2026-08-15T20:25:00Z",
"scheduledArrival": "2026-08-15T20:23:00Z",
"scheduledDeparture": "2026-08-15T20:23:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 100029",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Albertstra\u00dfe",
"stopId": "de-DELFI_000011005802",
"importance": 0.00039489471237175167,
"lat": 53.54898,
"lon": 10.019054,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:26:00Z",
"departure": "2026-08-15T20:26:00Z",
"scheduledArrival": "2026-08-15T20:24:00Z",
"scheduledDeparture": "2026-08-15T20:24:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 100026",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Repsoldstra\u00dfe",
"stopId": "de-DELFI_000011005702",
"importance": 0.00039489471237175167,
"lat": 53.54862,
"lon": 10.013898,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:28:00Z",
"departure": "2026-08-15T20:28:00Z",
"scheduledArrival": "2026-08-15T20:26:00Z",
"scheduledDeparture": "2026-08-15T20:26:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 100020",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "U Steinstra\u00dfe (Deichtorplatz)",
"stopId": "de-DELFI_de:02000:10024::100050",
"importance": 0.0013284930028021336,
"lat": 53.548954,
"lon": 10.006442000000002,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:30:00Z",
"departure": "2026-08-15T20:30:00Z",
"scheduledArrival": "2026-08-15T20:28:00Z",
"scheduledDeparture": "2026-08-15T20:28:00Z",
"scheduledTrack": "3",
"track": "3",
"description": "HHA-B 100033",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "HBF/Spitalerstra\u00dfe",
"stopId": "de-DELFI_de:02000:10011::100133",
"importance": 0.2917599081993103,
"lat": 53.552642999999996,
"lon": 10.005606000000002,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:32:00Z",
"departure": "2026-08-15T20:32:00Z",
"scheduledArrival": "2026-08-15T20:30:00Z",
"scheduledDeparture": "2026-08-15T20:30:00Z",
"scheduledTrack": "1",
"track": "1",
"description": "HHA-B 100133",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"HIGHSPEED_RAIL",
"LONG_DISTANCE",
"COACH",
"NIGHT_RAIL",
"REGIONAL_RAIL",
"SUBURBAN",
"SUBWAY",
"BUS"
]
},
{
"name": "Kunsthalle",
"stopId": "de-DELFI_000011103302",
"importance": 0.0003962125920224935,
"lat": 53.555546,
"lon": 10.001356999999999,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:35:00Z",
"departure": "2026-08-15T20:35:00Z",
"scheduledArrival": "2026-08-15T20:33:00Z",
"scheduledDeparture": "2026-08-15T20:33:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 100213",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "U Stephansplatz",
"stopId": "de-DELFI_de:02000:11013:1:110071",
"importance": 0.0023285001516342163,
"lat": 53.558370000000004,
"lon": 9.98852,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:38:00Z",
"departure": "2026-08-15T20:38:00Z",
"scheduledArrival": "2026-08-15T20:36:00Z",
"scheduledDeparture": "2026-08-15T20:36:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 110071",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Johannes-Brahms-Platz",
"stopId": "de-DELFI_de:02000:11009:1:110056",
"importance": 0.0004172989574726671,
"lat": 53.555110000000006,
"lon": 9.978620999999999,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:40:00Z",
"departure": "2026-08-15T20:40:00Z",
"scheduledArrival": "2026-08-15T20:38:00Z",
"scheduledDeparture": "2026-08-15T20:38:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 110056",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Handwerkskammer",
"stopId": "de-DELFI_de:02000:11008::110054",
"importance": 0.0004172989574726671,
"lat": 53.55314,
"lon": 9.976393,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:41:00Z",
"departure": "2026-08-15T20:41:00Z",
"scheduledArrival": "2026-08-15T20:39:00Z",
"scheduledDeparture": "2026-08-15T20:39:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 110053",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Museum f\u00fcr Hamburgische Geschichte",
"stopId": "de-DELFI_de:02000:11007::110052",
"importance": 0.0004172989574726671,
"lat": 53.55146799999999,
"lon": 9.974794000000001,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:42:00Z",
"departure": "2026-08-15T20:42:00Z",
"scheduledArrival": "2026-08-15T20:40:00Z",
"scheduledDeparture": "2026-08-15T20:40:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 110051",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "U St. Pauli",
"stopId": "de-DELFI_de:02000:80017:3:800037",
"importance": 0.0012117273872718215,
"lat": 53.550117,
"lon": 9.969799000000002,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:45:00Z",
"departure": "2026-08-15T20:45:00Z",
"scheduledArrival": "2026-08-15T20:43:00Z",
"scheduledDeparture": "2026-08-15T20:43:00Z",
"scheduledTrack": "3",
"track": "3",
"description": "HHA-B 800037",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Davidstra\u00dfe",
"stopId": "de-DELFI_de:02000:80018:2:800039",
"importance": 0.001252002315595746,
"lat": 53.549583,
"lon": 9.961643,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:48:00Z",
"departure": "2026-08-15T20:48:00Z",
"scheduledArrival": "2026-08-15T20:46:00Z",
"scheduledDeparture": "2026-08-15T20:46:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 800033",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "S Reeperbahn",
"stopId": "de-DELFI_de:02000:80019:2:800041",
"importance": 0.0014315921580418944,
"lat": 53.549746999999996,
"lon": 9.954717,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:52:00Z",
"departure": "2026-08-15T20:52:00Z",
"scheduledArrival": "2026-08-15T20:50:00Z",
"scheduledDeparture": "2026-08-15T20:50:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 800031",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Bl\u00fccherstra\u00dfe",
"stopId": "de-DELFI_de:02000:80107::800356",
"importance": 0.0009377760579809546,
"lat": 53.549186999999996,
"lon": 9.949956,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:53:00Z",
"departure": "2026-08-15T20:53:00Z",
"scheduledArrival": "2026-08-15T20:51:00Z",
"scheduledDeparture": "2026-08-15T20:51:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 800356",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Gro\u00dfe Bergstra\u00dfe",
"stopId": "de-DELFI_de:02000:80023:2:800323",
"importance": 0.0009272329043596983,
"lat": 53.551525,
"lon": 9.946254999999999,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:55:00Z",
"departure": "2026-08-15T20:55:00Z",
"scheduledArrival": "2026-08-15T20:53:00Z",
"scheduledDeparture": "2026-08-15T20:53:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 800323",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Goethestra\u00dfe",
"stopId": "de-DELFI_de:02000:80024::800082",
"importance": 0.0004593530611600727,
"lat": 53.55199399999999,
"lon": 9.940056,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:56:00Z",
"departure": "2026-08-15T20:56:00Z",
"scheduledArrival": "2026-08-15T20:54:00Z",
"scheduledDeparture": "2026-08-15T20:54:00Z",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Bf. Altona",
"stopId": "de-DELFI_de:02000:80026::800091",
"importance": 0.00639944477006793,
"lat": 53.551548,
"lon": 9.934171999999998,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:58:00Z",
"departure": "2026-08-15T20:58:00Z",
"scheduledArrival": "2026-08-15T20:56:00Z",
"scheduledDeparture": "2026-08-15T20:56:00Z",
"scheduledTrack": "5",
"track": "5",
"description": "VHH 800096",
"vertexType": "TRANSIT",
"pickupType": "NOT_ALLOWED",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Bf. Altona",
"stopId": "de-DELFI_de:02000:80026::800094",
"importance": 0.00639944477006793,
"lat": 53.55177,
"lon": 9.934406000000001,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T20:59:00Z",
"departure": "2026-08-15T20:59:00Z",
"scheduledArrival": "2026-08-15T20:57:00Z",
"scheduledDeparture": "2026-08-15T20:57:00Z",
"scheduledTrack": "8",
"track": "8",
"description": "Z 69275700",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NOT_ALLOWED",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Rathaus Altona",
"stopId": "de-DELFI_de:02000:80015::800044",
"importance": 0.0019747898913919926,
"lat": 53.547134,
"lon": 9.936131,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:01:00Z",
"departure": "2026-08-15T21:01:00Z",
"scheduledArrival": "2026-08-15T20:59:00Z",
"scheduledDeparture": "2026-08-15T20:59:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 800205",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Susettestra\u00dfe",
"stopId": "de-DELFI_de:02000:80098::800329",
"importance": 0.00032002496300265193,
"lat": 53.546482,
"lon": 9.924938,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:03:00Z",
"departure": "2026-08-15T21:03:00Z",
"scheduledArrival": "2026-08-15T21:01:00Z",
"scheduledDeparture": "2026-08-15T21:01:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 800328",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Hohenzollernring (S\u00fcd)",
"stopId": "de-DELFI_de:02000:80010::800047",
"importance": 0.00032002496300265193,
"lat": 53.546079999999996,
"lon": 9.915613,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:04:00Z",
"departure": "2026-08-15T21:04:00Z",
"scheduledArrival": "2026-08-15T21:02:00Z",
"scheduledDeparture": "2026-08-15T21:02:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 800026",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Liebermannstra\u00dfe",
"stopId": "de-DELFI_de:02000:80009::800048",
"importance": 0.00032002496300265193,
"lat": 53.54596,
"lon": 9.899587000000002,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:06:00Z",
"departure": "2026-08-15T21:06:00Z",
"scheduledArrival": "2026-08-15T21:04:00Z",
"scheduledDeparture": "2026-08-15T21:04:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "HHA-B 800025",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Halbmondsweg",
"stopId": "de-DELFI_de:02000:80008::800050",
"importance": 0.0005810476723127067,
"lat": 53.546510000000005,
"lon": 9.887433000000001,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:07:00Z",
"departure": "2026-08-15T21:07:00Z",
"scheduledArrival": "2026-08-15T21:05:00Z",
"scheduledDeparture": "2026-08-15T21:05:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 800023",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Zedernweg",
"stopId": "de-DELFI_de:02000:80007::800051",
"importance": 0.0005810476723127067,
"lat": 53.54658,
"lon": 9.88216,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:08:00Z",
"departure": "2026-08-15T21:08:00Z",
"scheduledArrival": "2026-08-15T21:06:00Z",
"scheduledDeparture": "2026-08-15T21:06:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 800022",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
},
{
"name": "Holztwiete",
"stopId": "de-DELFI_de:02000:89006::800110",
"importance": 0.0005810476723127067,
"lat": 53.547146,
"lon": 9.874039000000002,
"level": 0.0,
"tz": "Europe/Berlin",
"arrival": "2026-08-15T21:09:00Z",
"departure": "2026-08-15T21:09:00Z",
"scheduledArrival": "2026-08-15T21:07:00Z",
"scheduledDeparture": "2026-08-15T21:07:00Z",
"scheduledTrack": "2",
"track": "2",
"description": "VHH 800021",
"vertexType": "TRANSIT",
"pickupType": "NORMAL",
"dropoffType": "NORMAL",
"cancelled": false,
"modes": [
"BUS"
]
}
],
"legGeometry": {
"points": "cfgceBsczdREx@q@tN{@|R_Dbt@q@xNgB~^e@nIM`B_@`DWlDMzBIxCBpCF`E@vEMpFsAtVsCfi@}@jOYpFaDnk@_@zGANCp@WtEa@pH]tJ]xKuArH[rEcA^{@`Ai@zAUjBApBPjBd@|At@dAIhF\\nGeG`iAuBzb@kJxmBUtEeAdTAPq@bOYfGWxHQdFMdG[~RYlZGvRCvFmAhG?tE?rBJfHJ|H\\pPRlNpBhNJpN\\~LNpDf@rL|@hOn@|KRjCh@lHxCp]PnB~Cx[TbClBtQPdBp@zF~@pHdB|LdDnUlAtIpIrh@lJxi@xBlMDZxArIjEdWrB|L|BbMpWjxAxEbWzKxl@|EvWdJxf@dC`N|Gx^bLtm@jAtNdClMj@zCtAfH~BxL~AjIhGz\\PdAlCxNpFvSfA|Fv@fEjCrNrCjOpB~Kf@jC|E~WhA`GtAfHzAlIbDbQfAxIfDrXVtBtA`LjAbGvAtD_BdE}AvCu@fAuAzAsLjIeBlAkYhSsN`KuF|DqA~@wB~AmDjCwG|EgIpFqBxAc_@xWuHnF}a@bZ_DzBqN`KeD~BiHhFgAv@cExCu@j@sHnF_R|MuWtRmGpEgChBwVnQmG`FyBfBaGtEza@d~BrEbW|C~PxFn[pAnG_ObKkChBaFjD}F~DuIbGaBhAuDfClBpKjFhZ`C|M|Kpn@rI|e@bKvk@~@hF^tB~F`\\t@zD\\vBh@zCnC~NtJnh@RlApDtRt@dF~@jHd@dGPvGDhJDfHLbINzHPvHr@jr@BfQGrYM|POfV]fg@Y|a@OxSGdJIvTCdBMzDSnEWxD{@bJsGrCgLImG_DsGcA}HuAkBYeEq@cT_EwLsAqJa@cK@_BHoDf@{Er@u@HkEp@_JzB{Br@aOnEuOxEoJ~CmNrEcA\\w@RmGrAQFuFnAaGvAwPlE_IrCsE~BeAh@_Az@iB~A}EzEyBlBwIfK}KhNsHhKmEpFeV`[qA`BuE|GoDlFcG~IiEdHkCdFk`@~v@qFlMqBvT}KtVgIhQgEdJw@nB{@tB{@vBgCfGsDjJuBbGeIbTkFjPkCtI{DxNyDz[a@`CeAdFeGx[sIhe@gM`s@{D|VcA`GwIxm@{Fvf@m@rFqEla@wCbV[pCaB|MeAxIi@pFi@dGm@xHkIffA}Cpa@sIhmAqB|X_@dFGl@K`BWtJ?hEGlJ?pD?pGZtOLrFt@tWlAz\\nF|dBdBhe@~@tQl@~I|@rJ^pC~A~LlB`M^~BbAlFrCzMxBbLtCtNlEzR`CdKdCtJvB~H`CrHrDxKbEnLvFzNjHhPzk@joApItPlAbCbBrD|AbDdGjIn@dAlDhFvAfB`@f@|GvH~LfJNJfNhK|aAvqApXp]nA|A|JjL~@fAtBbCbM|KxF`FvEtEhGdGp@p@jQfQlWdW|OhNtAjAhJbInYpUpUzPv@f@pGjExIfLpDlHvAvB~BfDdBxAfEn@xE|CdBgOdAmJjAiO~@iLNiBr@{Lh@oLJ_CTuQxCotAtAow@H_LBqDPmPLaIJ{GNsCj@mXFiB_@mQbB{SfD__@|Kq_AvL}aAzJux@zDgLTkBtA_KjAgJDe@nCkUvCwTdBsMlCqQnF__@nCgP^_TzBsPXkCPmDLyDA_MQgCWmAyAdO_AvIiJ~y@{Fj`@cClRmF~b@iC~SUlBgArIsAtKSlB@rO{Jtx@wL|aA}Kp_AgD~^cBzSyDnOoAtQ_@`KKpDKxHGxHOjO?vBIjUE|I_@p`@c@|Rm@|T}Ab^k@zKq@`MQlBcClVsEx_@_@fDeAhKoAjOgBvY{AfU_A`KmAvKm@nFwAnLmAx[KzCKhDE`DMrHA~B^ba@TfYFfF?\\HxILxJJ~JVlWN|Sp@fi@^xRNlHr@zSFbB~JdwBHhBd@rLzKnbCHdBl@fL^fLf@``@@x@?fA@nN]vm@KtQEzLChFCnCChIIdJEnHKbMA~AuAbb@E|@KnDY~G{Cnk@{Ah^QtHS`Is@lYCja@?vALbFXrMTxHp@d\\HlCPbHTdJPpGtBpd@DhBHdCt@bZ\\rM`@fL`AvRjAtR`If~@~AvPPfBT|BxEvg@B`@|AtPMdMPfF\\dEn@rIrB`VtAxPs@jDcA|E{@nDmN`o@aCbCsAr@uA`@sBHgJr@qEM{SL}K`@gZ`@wC_A_Nr@u@Ce@GYI]O]YOg@Sc@U_@W[[S[M[I]?[B]JYPYTUXQ\\Q`@Md@Kf@Gf@Ej@Ch@?j@@T@TDh@Hf@?v@Cn@Cr@Ej@MjBwDzf@{BdZKjAY~D}Ftx@WhEeCn`@s@lOAV_@~PUdTIxJOnRi@vy@SbVu@zh@o@`Ug@pFUjCe@lGi@rHc@d@m@r@R~AHpBCbC_JtnAcCbZ}Cj_@a@zEcAtG_AdG`F|G~AjBjBjBpD~CbDbCnIzE|A~@nPvJ_@|EGxB@`b@P|IZhFZ|DJrDGhSAxCEfb@?r@Qlb@E|NO|@Sp@Y^a@RaGOm@[Si@Oi@I{@DkU?yIB{GHqT?gBJkXBiNHsB`@{Ax@gBvGeMb@eAhBoFPe_@DoAp@_HdRfFrGpArKdA|Rh@vA?jGK`EGtEI|dBM~WD`C?n@?~IXdEJfJL~J?jJ@r@?t\\@zCZ|ARfAb@dAbAx@tAp@hBl@zC`@|L?nTCjIaAjXYpJNpOuF~sAsAn\\k@xUCzIA~LBpHFzEJ|FPfFj@|K`@lHxFhhANrCl@`MNpGEnHOzFwBze@o@tPYtLChIF~OJdHX~NNhHNxYK`Q@nDIhOA~F?hC@pCT`^BzC^lWhCn^pAhRvBtXtA~Tj@bPf@xQV`Rf@nd@b@xOfA~TNzCPfCt@~KdCbXx@lKn@vKl@dPf@dTF`NHpNQ|WAz@KjIYtLSjGYtIe@dMe@rLOjEq@jTg@jT[dQWjNAdACxBmArr@o@x]E|DQtL?lNAvBMzj@KjbAHvKJxFRxIh@nPfAdXTpFdAhWv@nRFjAFpBJdCv@dSlAhYp@tP^vKp@~[n@tXh@hPj@nOnAnU`AfPnCrc@|@vOhBbZdAlPPpCn@bKl@lL^pKRfJD~M?rJEvIM`MEnDKhJCl@QfJc@dJ_@dF]|Da@bEc@tDk@jE}AzJ}D~UyA|Ky@rH_@`G_A`No@zMIdBItBm@dN{Ab_@]|HE|@IhBK|Bq@dOQtDy@tQMzDk@nNu@`Ra@zJkB~X_D|j@m@jK{Bte@UhFm@zL{ElfAmG`uAGxAmApXm@dSKjJ_@v`@@pQDdGGt\\F~JR`PV`LN`Dl@lMt@fK`BxOvCdVVxGFlGBzFYpk@Q|LGhEYfRMbIKhIa@zc@o@lu@EnDO|QQ`RmAhwACtKEnO\\rPRlK@Xx@zT^~JT`O@~@D|KQxMWhIS`F]pGO|A]|Dk@lGa@vEc@|Ee@lI[lGa@t[i@x]InG_Alr@QrKSfQI~XOrb@GnIOxH[xHm@bIi@zFy@lHyB~OiAzIo@xE}AdM_AzIq@tHc@tGQxCk@bKq@vKWxEGlAGfA{@~Oc@fJeAfWiAj^sBlv@}Adl@QlI[|G_@bFy@vHcB|KkHhb@uB~NgBnOcBbScDza@M`BIlAe@hIi@bNUhKMnMB|NNfOXvN`@bNX`H\\hHVjEVlDlAvLnDn[^lDj@bFh@jEdAhInB|MfBjL",
"precision": 6,
"length": 972
},
"bikesAllowed": false,
"wheelchairAccessible": "NOT_ACCESSIBLE",
"reservation": "NONE",
"alternatives": []
}
]
}