aboutsummaryrefslogtreecommitdiff
path: root/src/stop_downloader
diff options
context:
space:
mode:
Diffstat (limited to 'src/stop_downloader')
-rw-r--r--src/stop_downloader/README.md113
-rw-r--r--src/stop_downloader/santiago/download-stops.py196
-rw-r--r--src/stop_downloader/santiago/overrides/.gitkeep0
-rw-r--r--src/stop_downloader/vigo/download-stops.py187
-rw-r--r--src/stop_downloader/vigo/overrides/amenities.yaml23
-rw-r--r--src/stop_downloader/vigo/overrides/example-new-stops.yaml31
-rw-r--r--src/stop_downloader/vigo/overrides/fix-gregorio-espino.yaml20
-rw-r--r--src/stop_downloader/vigo/overrides/hide-virtual-stops.yaml17
-rw-r--r--src/stop_downloader/vigo/overrides/improve-coordinates-misc.yaml35
-rw-r--r--src/stop_downloader/vigo/overrides/navidad-2025.yaml88
10 files changed, 710 insertions, 0 deletions
diff --git a/src/stop_downloader/README.md b/src/stop_downloader/README.md
new file mode 100644
index 0000000..2cf8151
--- /dev/null
+++ b/src/stop_downloader/README.md
@@ -0,0 +1,113 @@
+# Bus Stop Overrides and Manual Stops
+
+This directory contains YAML files for overriding properties of existing bus stops and manually adding new stops.
+
+## Overrides Format
+
+Overrides modify or extend properties of existing stops from the transit API.
+
+```yaml
+stopId: # Numeric ID of the stop to override
+ name: # Override the name (string)
+ alternateNames: # Additional names for the stop (map)
+ key: # e.g. name used in metro maps
+ location: # Override location coordinates (map)
+ latitude: # New latitude value (float)
+ longitude: # New longitude value (float)
+ hide: # Hide the stop from the map and list (boolean)
+ amenities: # List of amenities available at this stop (list)
+ - shelter
+ - display
+ cancelled: # Mark stop as cancelled/out of service (boolean)
+ title: # Alert title shown to users (string)
+ message: # Alert message shown to users (string)
+ alternateCodes: # Alternative stop codes (list of strings)
+ - "ALT-123"
+```
+
+## Adding New Stops
+
+New stops that don't exist in the transit API can be added directly in override files using the `new: true` parameter. The `new` parameter is automatically removed after the stop is added to the list.
+
+```yaml
+stopId: # Numeric ID for the new stop (must not conflict with existing stops)
+ new: true # Mark this as a new stop (required, will be removed after processing)
+ name: # Name of the stop (string)
+ location: # Location coordinates (required for new stops)
+ latitude: # Latitude coordinate (float)
+ longitude: # Longitude coordinate (float)
+ lines: # List of lines serving this stop (list of strings)
+ - "1"
+ - "2"
+ amenities: # Optional: List of amenities (list)
+ - shelter
+ title: # Optional: Alert title (string)
+ message: # Optional: Alert message (string)
+ cancelled: # Optional: Mark as cancelled (boolean)
+ alternateCodes: # Optional: Alternative stop codes (list)
+```
+
+## Field Descriptions
+
+- **stopId** (integer): Unique identifier of the bus stop.
+- **new** (boolean): Set to `true` to add a new stop that doesn't exist in the API. This parameter is removed after processing.
+- **name** (string): Override or set the stop name.
+- **alternateNames** (object): Other names used in different contexts.
+ - **key** (string): Name used in a specific context, such as `metro`.
+- **location** (object):
+ - **latitude** (float): Override/set latitude coordinate.
+ - **longitude** (float): Override/set longitude coordinate.
+- **lines** (array of strings): List of line numbers serving this stop (required for new stops).
+- **hide** (boolean): Set to `true` to exclude the stop from maps and listings.
+- **cancelled** (boolean): Set to `true` to mark the stop as cancelled or out of service.
+- **title** (string): Alert title displayed to users (e.g., "Stop Temporarily Closed").
+- **message** (string): Detailed message about the stop status or alert.
+- **alternateCodes** (array of strings): Alternative stop codes or identifiers.
+- **amenities** (array of strings): Amenities available at this stop, such as `shelter` or `display`.
+
+## Examples
+
+### Override Example
+
+```yaml
+12345:
+ name: "Central Station"
+ alternateNames:
+ metro: "Main Hub"
+ location:
+ latitude: 40.712776
+ longitude: -74.005974
+ hide: false
+ amenities:
+ - shelter
+ - display
+ title: "Stop Relocated"
+ message: "This stop has been temporarily moved 50 meters north."
+```
+
+### New Stop Example
+
+```yaml
+99999:
+ new: true
+ name: "New Development Stop"
+ location:
+ latitude: 42.229188
+ longitude: -8.722469
+ lines:
+ - "5"
+ - "12"
+ amenities:
+ - shelter
+```
+
+### Cancelled Stop Example
+
+```yaml
+54321:
+ cancelled: true
+ title: "Stop Out of Service"
+ message: "This stop is temporarily closed for construction. Use stop 54322 (100m south) instead."
+ alternateCodes:
+ - "54322"
+```
diff --git a/src/stop_downloader/santiago/download-stops.py b/src/stop_downloader/santiago/download-stops.py
new file mode 100644
index 0000000..14ffc32
--- /dev/null
+++ b/src/stop_downloader/santiago/download-stops.py
@@ -0,0 +1,196 @@
+# /// script
+# requires-python = ">=3.12"
+# dependencies = [
+# "PyYAML>=6.0.2", # For YAML support
+# ]
+# ///
+import json
+import os
+import sys
+import urllib.request
+import yaml # Add YAML support for overrides
+
+OVERRIDES_DIR = "overrides"
+OUTPUT_FILE = "../../frontend/public/stops/santiago.json"
+
+def load_stop_overrides(file_path):
+ """Load stop overrides from a YAML file"""
+ if not os.path.exists(file_path):
+ print(f"Warning: Overrides file {file_path} not found")
+ return {}
+
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ overrides = yaml.safe_load(f)
+ print(f"Loaded {len(overrides) if overrides else 0} stop overrides")
+ return overrides or {}
+ except Exception as e:
+ print(f"Error loading overrides: {e}", file=sys.stderr)
+ return {}
+
+def apply_overrides(stops, overrides):
+ """Apply overrides to the stop data and add new stops"""
+ # Track existing stop IDs
+ existing_stop_ids = {stop.get("stopId") for stop in stops}
+
+ # Apply overrides to existing stops
+ for stop in stops:
+ stop_id = stop.get("stopId")
+ if stop_id in overrides:
+ override = overrides[stop_id]
+
+ # Override name if provided
+ if "name" in override:
+ stop["name"]["original"] = override["name"]
+
+ # Apply or add alternate names
+ if "alternateNames" in override:
+ for key, value in override["alternateNames"].items():
+ stop["name"][key] = value
+
+ # Apply location override
+ if "location" in override:
+ if "latitude" in override["location"]:
+ stop["latitude"] = override["location"]["latitude"]
+ if "longitude" in override["location"]:
+ stop["longitude"] = override["location"]["longitude"]
+
+ # Add amenities
+ if "amenities" in override:
+ stop["amenities"] = override["amenities"]
+
+ # Mark stop as hidden if needed
+ if "hide" in override:
+ stop["hide"] = override["hide"]
+
+ # Mark stop as cancelled
+ if "cancelled" in override:
+ stop["cancelled"] = override["cancelled"]
+
+ # Add alert title
+ if "title" in override:
+ stop["title"] = override["title"]
+
+ # Add alert message
+ if "message" in override:
+ stop["message"] = override["message"]
+
+ # Add alternate codes
+ if "alternateCodes" in override:
+ stop["alternateCodes"] = override["alternateCodes"]
+
+ # Add new stops (those with "new: true" parameter)
+ new_stops_added = 0
+ for stop_id, override in overrides.items():
+ # Check if this is a new stop
+ if override.get("new") and stop_id not in existing_stop_ids:
+ # Ensure stop_id is an integer for consistency
+ stop_id_int = int(stop_id) if isinstance(stop_id, str) else stop_id
+
+ # Create the new stop
+ new_stop = {
+ "stopId": stop_id_int,
+ "name": {
+ "original": override.get("name", f"Stop {stop_id_int}")
+ },
+ "latitude": override.get("location", {}).get("latitude"),
+ "longitude": override.get("location", {}).get("longitude"),
+ "lines": override.get("lines", [])
+ }
+
+ # Add optional fields (excluding the 'new' parameter)
+ if "alternateNames" in override:
+ for key, value in override["alternateNames"].items():
+ new_stop["name"][key] = value
+ if "amenities" in override:
+ new_stop["amenities"] = override["amenities"]
+ if "cancelled" in override:
+ new_stop["cancelled"] = override["cancelled"]
+ if "title" in override:
+ new_stop["title"] = override["title"]
+ if "message" in override:
+ new_stop["message"] = override["message"]
+ if "alternateCodes" in override:
+ new_stop["alternateCodes"] = override["alternateCodes"]
+
+ stops.append(new_stop)
+ new_stops_added += 1
+
+ if new_stops_added > 0:
+ print(f"Added {new_stops_added} new stops from overrides")
+
+ return stops
+
+def main():
+ print("Fetching stop list data...")
+
+ # Download stop list data
+ url = "https://app.tussa.org/tussa/api/paradas"
+ body = json.dumps({ "nombre": "" }).encode('utf-8')
+ req = urllib.request.Request(url, data=body, headers={'Content-Type': 'application/json'}, method='POST')
+
+ try:
+ with urllib.request.urlopen(req) as response:
+ content = response.read().decode('utf-8')
+ data = json.loads(content)
+
+ print(f"Downloaded {len(data)} stops")
+
+ # Process the data
+ processed_stops = []
+ for stop in data:
+ name = stop.get("nombre", "").strip()
+
+ lines_sinoptic: list[str] = [line.get("sinoptico").strip() for line in stop.get("lineas", [])] if stop.get("lineas") else []
+ lines_id: list[str] = []
+
+ for line in lines_sinoptic:
+ line_code = line.lstrip('L')
+ lines_id.append(line_code)
+
+ processed_stop = {
+ "stopId": stop.get("id"),
+ "name": {
+ "original": name
+ },
+ "latitude": stop.get("coordenadas").get("latitud"),
+ "longitude": stop.get("coordenadas").get("longitud"),
+ "lines": lines_id,
+ "hide": len(lines_id) == 0
+ }
+ processed_stops.append(processed_stop)
+
+ # Load and apply overrides
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ overrides_dir = os.path.join(script_dir, OVERRIDES_DIR)
+ # For each YML/YAML file in the overrides directory, load and apply the overrides
+ for filename in os.listdir(overrides_dir):
+ if not filename.endswith(".yml") and not filename.endswith(".yaml"):
+ continue
+
+ print(f"Loading overrides from {filename}")
+ overrides_file = os.path.join(overrides_dir, filename)
+ overrides = load_stop_overrides(overrides_file)
+ processed_stops = apply_overrides(processed_stops, overrides)
+
+ # Filter out hidden stops
+ visible_stops = [stop for stop in processed_stops if not stop.get("hide")]
+ print(f"Removed {len(processed_stops) - len(visible_stops)} hidden stops")
+
+ # Sort stops by ID ascending
+ visible_stops.sort(key=lambda x: x["stopId"])
+
+ output_file = os.path.join(script_dir, OUTPUT_FILE)
+
+ with open(output_file, 'w', encoding='utf-8') as f:
+ json.dump(visible_stops, f, ensure_ascii=False, indent=2)
+
+ print(f"Saved processed stops data to {output_file}")
+ return 0
+
+ except Exception as e:
+ print(f"Error processing stops data: {e}", file=sys.stderr)
+ return 1
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/src/stop_downloader/santiago/overrides/.gitkeep b/src/stop_downloader/santiago/overrides/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/stop_downloader/santiago/overrides/.gitkeep
diff --git a/src/stop_downloader/vigo/download-stops.py b/src/stop_downloader/vigo/download-stops.py
new file mode 100644
index 0000000..8771e53
--- /dev/null
+++ b/src/stop_downloader/vigo/download-stops.py
@@ -0,0 +1,187 @@
+# /// script
+# requires-python = ">=3.12"
+# dependencies = [
+# "PyYAML>=6.0.2", # For YAML support
+# ]
+# ///
+import json
+import os
+import sys
+import urllib.request
+import yaml # Add YAML support for overrides
+
+OVERRIDES_DIR = "overrides"
+OUTPUT_FILE = "../../frontend/public/stops/vigo.json"
+
+def load_stop_overrides(file_path):
+ """Load stop overrides from a YAML file"""
+ if not os.path.exists(file_path):
+ print(f"Warning: Overrides file {file_path} not found")
+ return {}
+
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ overrides = yaml.safe_load(f)
+ print(f"Loaded {len(overrides) if overrides else 0} stop overrides")
+ return overrides or {}
+ except Exception as e:
+ print(f"Error loading overrides: {e}", file=sys.stderr)
+ return {}
+
+def apply_overrides(stops, overrides):
+ """Apply overrides to the stop data and add new stops"""
+ # Track existing stop IDs
+ existing_stop_ids = {stop.get("stopId") for stop in stops}
+
+ # Apply overrides to existing stops
+ for stop in stops:
+ stop_id = stop.get("stopId")
+ if stop_id in overrides:
+ override = overrides[stop_id]
+
+ # Override name if provided
+ if "name" in override:
+ stop["name"]["original"] = override["name"]
+
+ # Apply or add alternate names
+ if "alternateNames" in override:
+ for key, value in override["alternateNames"].items():
+ stop["name"][key] = value
+
+ # Apply location override
+ if "location" in override:
+ if "latitude" in override["location"]:
+ stop["latitude"] = override["location"]["latitude"]
+ if "longitude" in override["location"]:
+ stop["longitude"] = override["location"]["longitude"]
+
+ # Add amenities
+ if "amenities" in override:
+ stop["amenities"] = override["amenities"]
+
+ # Mark stop as hidden if needed
+ if "hide" in override:
+ stop["hide"] = override["hide"]
+
+ # Mark stop as cancelled
+ if "cancelled" in override:
+ stop["cancelled"] = override["cancelled"]
+
+ if "alert" in override:
+ stop["alert"] = override["alert"]
+
+ if "title" in override:
+ stop["title"] = override["title"]
+
+ if "message" in override:
+ stop["message"] = override["message"]
+
+ # Add new stops (those with "new: true" parameter)
+ new_stops_added = 0
+ for stop_id, override in overrides.items():
+ # Check if this is a new stop
+ if override.get("new") and stop_id not in existing_stop_ids:
+ # Ensure stop_id is an integer for consistency
+ stop_id_int = int(stop_id) if isinstance(stop_id, str) else stop_id
+
+ # Create the new stop
+ new_stop = {
+ "stopId": stop_id_int,
+ "name": {
+ "original": override.get("name", f"Stop {stop_id_int}")
+ },
+ "latitude": override.get("location", {}).get("latitude"),
+ "longitude": override.get("location", {}).get("longitude"),
+ "lines": override.get("lines", [])
+ }
+
+ # Add optional fields (excluding the 'new' parameter)
+ if "alternateNames" in override:
+ for key, value in override["alternateNames"].items():
+ new_stop["name"][key] = value
+ if "amenities" in override:
+ new_stop["amenities"] = override["amenities"]
+ if "cancelled" in override:
+ new_stop["cancelled"] = override["cancelled"]
+ if "title" in override:
+ new_stop["title"] = override["title"]
+ if "message" in override:
+ new_stop["message"] = override["message"]
+ if "alternateCodes" in override:
+ new_stop["alternateCodes"] = override["alternateCodes"]
+
+ stops.append(new_stop)
+ new_stops_added += 1
+
+ if new_stops_added > 0:
+ print(f"Added {new_stops_added} new stops from overrides")
+
+ return stops
+
+def main():
+ print("Fetching stop list data...")
+
+ # Download stop list data
+ url = "https://datos.vigo.org/vci_api_app/api2.jsp?tipo=TRANSPORTE_PARADAS"
+ req = urllib.request.Request(url)
+
+ try:
+ with urllib.request.urlopen(req) as response:
+ # Read the response and decode from ISO-8859-1 to UTF-8
+ content = response.read().decode('iso-8859-1')
+ data = json.loads(content)
+
+ print(f"Downloaded {len(data)} stops")
+
+ # Process the data
+ processed_stops = []
+ for stop in data:
+ name = stop.get("nombre", "").strip()
+ # Fix double space equals comma-space: "Castrelos 202" -> "Castrelos, 202"; and remove quotes
+ name = name.replace(" ", ", ").replace('"', '').replace("'", "")
+
+ processed_stop = {
+ "stopId": stop.get("id"),
+ "name": {
+ "original": name
+ },
+ "latitude": stop.get("lat"),
+ "longitude": stop.get("lon"),
+ "lines": [line.strip() for line in stop.get("lineas", "").split(",")] if stop.get("lineas") else []
+ }
+ processed_stops.append(processed_stop)
+
+ # Load and apply overrides
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ overrides_dir = os.path.join(script_dir, OVERRIDES_DIR)
+ # For each YML/YAML file in the overrides directory, load and apply the overrides
+ for filename in os.listdir(overrides_dir):
+ if not filename.endswith(".yml") and not filename.endswith(".yaml"):
+ continue
+
+ print(f"Loading overrides from {filename}")
+ overrides_file = os.path.join(overrides_dir, filename)
+ overrides = load_stop_overrides(overrides_file)
+ processed_stops = apply_overrides(processed_stops, overrides)
+
+ # Filter out hidden stops
+ visible_stops = [stop for stop in processed_stops if not stop.get("hide")]
+ print(f"Removed {len(processed_stops) - len(visible_stops)} hidden stops")
+
+ # Sort stops by ID ascending
+ visible_stops.sort(key=lambda x: x["stopId"])
+
+ output_file = os.path.join(script_dir, OUTPUT_FILE)
+
+ with open(output_file, 'w', encoding='utf-8') as f:
+ json.dump(visible_stops, f, ensure_ascii=False, indent=2)
+
+ print(f"Saved processed stops data to {output_file}")
+ return 0
+
+ except Exception as e:
+ print(f"Error processing stops data: {e}", file=sys.stderr)
+ return 1
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/src/stop_downloader/vigo/overrides/amenities.yaml b/src/stop_downloader/vigo/overrides/amenities.yaml
new file mode 100644
index 0000000..fa2067a
--- /dev/null
+++ b/src/stop_downloader/vigo/overrides/amenities.yaml
@@ -0,0 +1,23 @@
+5520: # García Barbón, 7
+ amenities:
+ - shelter
+ - display
+5530: # García Barbón, 18
+ amenities:
+ - shelter
+ - display
+6620: #Policarpo Sanz, 40
+ amenities:
+ - shelter
+ - display
+14264: # Urzáiz - Príncipe
+ amenities:
+ - shelter
+ - display
+20193: # Policarpo Sanz, 25
+ amenities:
+ - shelter
+ - display
+20198: # Policarpo Sanz, 26
+ amenities:
+ - shelter
diff --git a/src/stop_downloader/vigo/overrides/example-new-stops.yaml b/src/stop_downloader/vigo/overrides/example-new-stops.yaml
new file mode 100644
index 0000000..6937471
--- /dev/null
+++ b/src/stop_downloader/vigo/overrides/example-new-stops.yaml
@@ -0,0 +1,31 @@
+# Example: Adding a new stop using the 'new' parameter
+# New stops are added directly in override files with new: true
+
+# Example 1: New stop with basic information (commented out to avoid affecting production)
+# 99001:
+# new: true
+# name: "New Development Stop"
+# location:
+# latitude: 42.229188
+# longitude: -8.722469
+# lines:
+# - "5"
+# - "12"
+# amenities:
+# - shelter
+
+# Example 2: New stop with alert information
+# 99002:
+# new: true
+# name: "Temporary Event Stop"
+# location:
+# latitude: 42.230000
+# longitude: -8.723000
+# lines:
+# - "EVENT"
+# title: "Special Event Stop"
+# message: "This stop is active during special events only."
+
+# Note: The 'new: true' parameter tells the system to create a new stop.
+# This parameter is automatically removed after the stop is added to the dataset.
+# Choose stop IDs in the 90000+ range to avoid conflicts with existing stops.
diff --git a/src/stop_downloader/vigo/overrides/fix-gregorio-espino.yaml b/src/stop_downloader/vigo/overrides/fix-gregorio-espino.yaml
new file mode 100644
index 0000000..2e035a2
--- /dev/null
+++ b/src/stop_downloader/vigo/overrides/fix-gregorio-espino.yaml
@@ -0,0 +1,20 @@
+# Fix the position of the stops in Gregorio Espino, which are "opposite" to the actual location of the bus stops.
+5720: # Gregorio Espino, 33
+ location:
+ latitude: 42.23004933454558
+ longitude: -8.706947409683313
+
+5710: # Gregorio Espino, 22
+ location:
+ latitude: 42.23003666347398
+ longitude: -8.707266671978003
+
+5730: # Gregorio Espino, 44
+ location:
+ latitude: 42.227850036119314
+ longitude: -8.708105429626789
+
+5740: # Gregorio Espino, 57
+ location:
+ latitude: 42.22783722597372
+ longitude: -8.707849091551859 \ No newline at end of file
diff --git a/src/stop_downloader/vigo/overrides/hide-virtual-stops.yaml b/src/stop_downloader/vigo/overrides/hide-virtual-stops.yaml
new file mode 100644
index 0000000..a2bf0b1
--- /dev/null
+++ b/src/stop_downloader/vigo/overrides/hide-virtual-stops.yaml
@@ -0,0 +1,17 @@
+# The Vitrasa network has several virtual stops created for internal purposes, like
+# end of certain lines with a "nice" name.
+
+20223: # Castrelos (Pavillón) - Final U1
+ hide: true
+20146: # García Barbón 7 - final líneas A y 18A
+ hide: true
+20220: # (Samil) COIA-SAMIL - Final L15A
+ hide: true
+20001: # (Samil) Samil por Beiramar - Final L15B
+ hide: true
+20002: # (Samil) Samil por Torrecedeira - Final L15C
+ hide: true
+20144: # (Samil) Samil por Coia - Final C3D+C3i
+ hide: true
+20145: # (Samil) Samil por Bouzas - Final C3D+C3i
+ hide: true \ No newline at end of file
diff --git a/src/stop_downloader/vigo/overrides/improve-coordinates-misc.yaml b/src/stop_downloader/vigo/overrides/improve-coordinates-misc.yaml
new file mode 100644
index 0000000..922f103
--- /dev/null
+++ b/src/stop_downloader/vigo/overrides/improve-coordinates-misc.yaml
@@ -0,0 +1,35 @@
+# Improves coordinates for some locations in the dataset to be more accurate, and avoid clustering
+6620: # Policarpo Sanz, 40
+ location:
+ latitude: 42.23757846151978
+ longitude: -8.721031378896738
+
+20193: # Policarpo Sanz, 25
+ location:
+ latitude: 42.23767601188501
+ longitude: -8.721582630122455
+
+3130: #Avda. de Cesáreo Vázquez 169
+ location:
+ latitude: 42.191024803868736
+ longitude: -8.799397387002196
+
+3090: # Avda. de Cesáreo Vázquez 182
+ location:
+ latitude: 42.191019711713736
+ longitude: -8.799628565094565
+
+14294: # Avda. de Ricardo Mella 406
+ location:
+ latitude: 42.190684424876565
+ longitude: -8.799308812770041
+
+3120: # Cesáreo Vázquez 141
+ location:
+ latitude: 42.187488521491225
+ longitude: -8.801226626055183
+
+3080: # Cesáreo Vázquez 136
+ location:
+ latitude: 42.1873653089623
+ longitude: -8.800886236766305
diff --git a/src/stop_downloader/vigo/overrides/navidad-2025.yaml b/src/stop_downloader/vigo/overrides/navidad-2025.yaml
new file mode 100644
index 0000000..cb20222
--- /dev/null
+++ b/src/stop_downloader/vigo/overrides/navidad-2025.yaml
@@ -0,0 +1,88 @@
+20208:
+ new: true
+ name: "Colón 12"
+ title: "Parada provisional sin datos"
+ message: "Parada provisional donde paran las líneas de Policarpo Sanz 40 *exceptuando 9B*"
+ location:
+ latitude: 42.23805815883466
+ longitude: -8.72057889828808
+ lines:
+ - "C1"
+ - "A"
+ - "5A"
+ - "9B"
+ - "15B"
+ - "15C"
+ - "24"
+ - "28"
+ - "N4"
+
+20194: # Cánovas del Castillo 28
+ cancelled: true
+ alert: "error"
+ title: "Cerrada por navidad"
+ message: |
+ Con motivo de las atracciones navideñas, esta parada queda anulada.
+ La parada más cercana es Cánovas del Castillo, 18 (frente CC A Laxe).
+
+14121: # Reconquista, 2 (Correos)
+ cancelled: true
+ alert: "error"
+ title: "Cerrada por navidad"
+ message: |
+ Con motivo de las atracciones navideñas, esta parada queda anulada.
+ Línea 9B comienza en Urzaiz-Príncipe.
+ Línea 24 se puede tomar en Colón 12 (provisional).
+ Resto de líneas en Marqués de Valladares (fronte 19) - subiendo Reconquista, primera calle a la derecha.
+
+20198: # Policarpo Sanz, 26
+ cancelled: true
+ alert: "error"
+ title: "Parada movida a Marqués de Valladares (fronte 19)"
+ message: "Con motivo del corte por las atracciones navideñas, las líneas se deben coger en Marqués de Valladares (fronte 19), excepto la línea 9B que arranca en Urzaiz-Príncipe y la 24 que para en Colón 12 (provisional)."
+
+6620: # Policarpo Sanz, 40
+ cancelled: true
+ title: "Parada movida a Colón 12"
+ message: "Con motivo del corte por las atracciones navideñas, las líneas se deben coger en Colón 12 (20208), excepto la línea 9B que arranca en Urzaiz-Príncipe."
+ alternateCodes:
+ - 20208
+
+6860: # Praza de Compostela
+ alert: "warning"
+ title: L24 no realiza parada
+ message: |
+ Con motivo de las atracciones navideñas, la línea 24 no realiza parada en esta ubicación.
+ Se puede tomar en Areal (aduana), antes del semáforo de Colón.
+
+3230: # Colón, 27
+ alert: warning
+ title: L17 no realiza parada
+ message: |
+ Con motivo de las atracciones navideñas, la línea 17 no realiza parada en esta ubicación.
+ Se puede tomar en Urzaiz, 13 (cruce República Argentina).
+
+5520: # García Barbón 7 (hacia Urzaiz/P.Sanz)
+ cancelled: true
+ alert: "error"
+ title: "Cerrada por navidad"
+ message: |
+ Con motivo de las atracciones navideñas, esta parada queda anulada.
+ Líneas C3, 10, N1, 5B, 16 y 17 en García Barbón, 43 (restaurante Porto Santo).
+ Líneas 4A y 4C (dir. Coia), 7, 12B, 16 y 17 se pueden tomar en Colón, 26 (oficina Banco Sabadell).
+
+5530: # García Barbón 18 (hacia Teis)
+ cancelled: true
+ alert: "error"
+ title: "Cerrada por navidad"
+ message: |
+ Con motivo de las atracciones navideñas, esta parada queda anulada.
+ Líneas C3 y 10 se pueden tomar en Rosalía de Castro 4 (detrás de ABANCA).
+ Resto de líneas en García Barbón 28 (pasando Alfonso XIII)
+
+5540: # García Barbón 28 (hacia Teis)
+ alert: "warning"
+ title: "Líneas C3 y 10 no realizan parada"
+ message: |
+ Con motivo de las atracciones navideñas, las líneas C3 y 10 realizan
+ provisionalmente la parada en Rúa Oporto 28 (calle perpendicular a la parada).