Check if stations are missing/differ in backward direction of some route

This commit is contained in:
Alexey Zakharenkov 2023-11-23 10:07:54 +03:00 committed by Alexey Zakharenkov
parent ed95c94aac
commit 6c796ac8c1
9 changed files with 2215 additions and 95 deletions

View file

@ -12,27 +12,26 @@ ALLOWED_STATIONS_MISMATCH = 0.02 # part of total station count
ALLOWED_TRANSFERS_MISMATCH = 0.07 # part of total interchanges count
ALLOWED_ANGLE_BETWEEN_STOPS = 45 # in degrees
DISALLOWED_ANGLE_BETWEEN_STOPS = 20 # in degrees
SUGGEST_TRANSFER_MIN_DISTANCE = 100 # in meters
# If an object was moved not too far compared to previous script run,
# it is likely the same object
DISPLACEMENT_TOLERANCE = 300 # in meters
MODES_RAPID = set(("subway", "light_rail", "monorail", "train"))
MODES_OVERGROUND = set(("tram", "bus", "trolleybus", "aerialway", "ferry"))
DEFAULT_MODES_RAPID = set(("subway", "light_rail"))
DEFAULT_MODES_OVERGROUND = set(("tram",)) # TODO: bus and trolleybus?
MODES_RAPID = {"subway", "light_rail", "monorail", "train"}
MODES_OVERGROUND = {"tram", "bus", "trolleybus", "aerialway", "ferry"}
DEFAULT_MODES_RAPID = {"subway", "light_rail"}
DEFAULT_MODES_OVERGROUND = {"tram"} # TODO: bus and trolleybus?
ALL_MODES = MODES_RAPID | MODES_OVERGROUND
RAILWAY_TYPES = set(
(
"rail",
"light_rail",
"subway",
"narrow_gauge",
"funicular",
"monorail",
"tram",
)
)
RAILWAY_TYPES = {
"rail",
"light_rail",
"subway",
"narrow_gauge",
"funicular",
"monorail",
"tram",
}
CONSTRUCTION_KEYS = (
"construction",
"proposed",
@ -49,7 +48,7 @@ START_END_TIMES_RE = re.compile(r".*?(\d{2}):(\d{2})-(\d{2}):(\d{2}).*")
def get_start_end_times(opening_hours):
"""Very simplified method to parse OSM opening_hours tag.
We simply take the first HH:MM-HH:MM substring which is the most probable
opening hours interval for the most of weekdays.
opening hours interval for the most of the weekdays.
"""
start_time, end_time = None, None
m = START_END_TIMES_RE.match(opening_hours)
@ -102,9 +101,9 @@ def el_center(el):
if not el:
return None
if "lat" in el:
return (el["lon"], el["lat"])
return el["lon"], el["lat"]
elif "center" in el:
return (el["center"]["lon"], el["center"]["lat"])
return el["center"]["lon"], el["center"]["lat"]
return None
@ -485,7 +484,7 @@ class StopArea:
self.center[i] /= len(self.stops) + len(self.platforms)
def get_elements(self):
result = set([self.id, self.station.id])
result = {self.id, self.station.id}
result.update(self.entrances)
result.update(self.exits)
result.update(self.stops)
@ -1156,6 +1155,12 @@ class Route:
return tracks
def are_tracks_complete(self) -> bool:
return (
self.first_stop_on_rails_index == 0
and self.last_stop_on_rails_index == len(self) - 1
)
def get_tracks_geometry(self):
tracks = self.get_extended_tracks()
tracks = self.get_truncated_tracks(tracks)
@ -1350,6 +1355,36 @@ class Route:
]
return True
def get_end_transfers(self) -> tuple[str, str]:
"""Using transfer ids because a train can arrive at different
stations within a transfer. But disregard transfer that may give
an impression of a circular route (for example,
Simonis / Elisabeth station and route 2 in Brussels).
"""
return (
(self[0].stoparea.id, self[-1].stoparea.id)
if (
self[0].stoparea.transfer is not None
and self[0].stoparea.transfer == self[-1].stoparea.transfer
)
else (
self[0].stoparea.transfer or self[0].stoparea.id,
self[-1].stoparea.transfer or self[-1].stoparea.id,
)
)
def get_transfers_sequence(self) -> list[str]:
"""Return a list of stoparea or transfer (if not None) ids."""
transfer_seq = [
stop.stoparea.transfer or stop.stoparea.id for stop in self
]
if (
self[0].stoparea.transfer is not None
and self[0].stoparea.transfer == self[-1].stoparea.transfer
):
transfer_seq[0], transfer_seq[-1] = self.get_end_transfers()
return transfer_seq
def __len__(self):
return len(self.stops)
@ -1479,13 +1514,75 @@ class RouteMaster:
else:
self.interval = min(self.interval, route.interval)
# Choose minimal id for determinancy
if not self.has_master and (not self.id or self.id > route.id):
self.id = route.id
self.routes.append(route)
if not self.best or len(route.stops) > len(self.best.stops):
if (
not self.best
or len(route.stops) > len(self.best.stops)
or (
# Choose route with minimal id for determinancy
len(route.stops) == len(self.best.stops)
and route.element["id"] < self.best.element["id"]
)
):
self.best = route
def get_meaningful_routes(self) -> list[Route]:
return [route for route in self if len(route) >= 2]
def find_twin_routes(self) -> dict[Route, Route]:
"""Two routes are twins if they have the same end stations
and opposite directions, and the number of stations is
the same or almost the same. We'll then find stops that are present
in one direction and is missing in another direction - to warn.
"""
twin_routes = {} # route => "twin" route
for route in self.get_meaningful_routes():
if route.is_circular:
continue # Difficult to calculate. TODO(?) in the future
if route in twin_routes:
continue
if len(route) < 2:
continue
route_transfer_ids = set(route.get_transfers_sequence())
ends = route.get_end_transfers()
ends_reversed = ends[::-1]
twin_candidates = [
r
for r in self
if not r.is_circular
and r not in twin_routes
and r.get_end_transfers() == ends_reversed
# If absolute or relative difference in station count is large,
# possibly it's an express version of a route - skip it.
and (
abs(len(r) - len(route)) <= 2
or abs(len(r) - len(route)) / max(len(r), len(route))
<= 0.2
)
]
if not twin_candidates:
continue
twin_route = min(
twin_candidates,
key=lambda r: len(
route_transfer_ids ^ set(r.get_transfers_sequence())
),
)
twin_routes[route] = twin_route
twin_routes[twin_route] = route
return twin_routes
def stop_areas(self):
"""Returns a list of all stations on all route variants."""
seen_ids = set()
@ -1521,6 +1618,7 @@ class City:
self.errors = []
self.warnings = []
self.notices = []
self.id = None
self.try_fill_int_attribute(city_data, "id")
self.name = city_data["name"]
self.country = city_data["country"]
@ -1555,7 +1653,7 @@ class City:
else:
self.modes = DEFAULT_MODES_RAPID
else:
self.modes = set([x.strip() for x in networks[0].split(",")])
self.modes = {x.strip() for x in networks[0].split(",")}
# Reversing bbox so it is (xmin, ymin, xmax, ymax)
bbox = city_data["bbox"].split(",")
@ -1627,7 +1725,7 @@ class City:
self.warnings.append(msg)
def error(self, message, el=None):
"""Error if a critical problem that invalidates the city"""
"""Error is a critical problem that invalidates the city."""
msg = City.log_message(message, el)
self.errors.append(msg)
@ -1914,37 +2012,18 @@ class City:
f"relations: {format_elid_list(not_in_sa)}"
)
def check_return_routes(self, rmaster):
variants = {}
have_return = set()
for variant in rmaster:
if len(variant) < 2:
continue
# Using transfer ids because a train can arrive at different
# stations within a transfer. But disregard transfer that may give
# an impression of a circular route (for example,
# Simonis / Elisabeth station and route 2 in Brussels)
if variant[0].stoparea.transfer == variant[-1].stoparea.transfer:
t = (variant[0].stoparea.id, variant[-1].stoparea.id)
else:
t = (
variant[0].stoparea.transfer or variant[0].stoparea.id,
variant[-1].stoparea.transfer or variant[-1].stoparea.id,
)
if t in variants:
continue
variants[t] = variant.element
tr = (t[1], t[0])
if tr in variants:
have_return.add(t)
have_return.add(tr)
def check_return_routes(self, rmaster: RouteMaster) -> None:
"""Check if a route has return direction, and if twin routes
miss stations.
"""
meaningful_routes = rmaster.get_meaningful_routes()
if len(variants) == 0:
if len(meaningful_routes) == 0:
self.error(
"An empty route master {}. Please set construction:route "
"if it is under construction".format(rmaster.id)
f"An empty route master {rmaster.id}. "
"Please set construction:route if it is under construction"
)
elif len(variants) == 1:
elif len(meaningful_routes) == 1:
log_function = (
self.error if not rmaster.best.is_circular else self.notice
)
@ -1954,9 +2033,144 @@ class City:
rmaster.best.element,
)
else:
for t, rel in variants.items():
if t not in have_return:
self.notice("Route does not have a return direction", rel)
all_ends = {
route.get_end_transfers(): route for route in meaningful_routes
}
for route in meaningful_routes:
ends = route.get_end_transfers()
if ends[::-1] not in all_ends:
self.notice(
"Route does not have a return direction", route.element
)
twin_routes = rmaster.find_twin_routes()
for route1, route2 in twin_routes.items():
if route1.id > route2.id:
continue # to process a pair of routes only once
# and to ensure the order of routes in the pair
self.alert_twin_routes_differ(route1, route2)
def alert_twin_routes_differ(self, route1: Route, route2: Route) -> None:
"""Arguments are that route1.id < route2.id"""
(
stops_missing_from_route1,
stops_missing_from_route2,
stops_that_dont_match,
) = self.calculate_twin_routes_diff(route1, route2)
for st in stops_missing_from_route1:
if (
not route1.are_tracks_complete()
or (
projected_point := project_on_line(
st.stoparea.center, route1.tracks
)["projected_point"]
)
is not None
and distance(st.stoparea.center, projected_point)
<= MAX_DISTANCE_STOP_TO_LINE
):
self.notice(
f"Stop {st.stoparea.station.name} {st.stop} is included "
f"into the {route2.id} but not included into {route1.id}",
route1.element,
)
for st in stops_missing_from_route2:
if (
not route2.are_tracks_complete()
or (
projected_point := project_on_line(
st.stoparea.center, route2.tracks
)["projected_point"]
)
is not None
and distance(st.stoparea.center, projected_point)
<= MAX_DISTANCE_STOP_TO_LINE
):
self.notice(
f"Stop {st.stoparea.station.name} {st.stop} is included "
f"into the {route1.id} but not included into {route2.id}",
route2.element,
)
for st1, st2 in stops_that_dont_match:
if (
st1.stoparea.station == st2.stoparea.station
or distance(st1.stop, st2.stop) < SUGGEST_TRANSFER_MIN_DISTANCE
):
self.notice(
"Should there be one stoparea or a transfer between "
f"{st1.stoparea.station.name} {st1.stop} and "
f"{st2.stoparea.station.name} {st2.stop}?",
route1.element,
)
@staticmethod
def calculate_twin_routes_diff(route1: Route, route2: Route) -> tuple:
"""WagnerFischer algorithm for stops diff in two twin routes."""
stops1 = route1.stops
stops2 = route2.stops[::-1]
def stops_match(stop1: RouteStop, stop2: RouteStop) -> bool:
return (
stop1.stoparea == stop2.stoparea
or stop1.stoparea.transfer is not None
and stop1.stoparea.transfer == stop2.stoparea.transfer
)
d = [[0] * (len(stops2) + 1) for _ in range(len(stops1) + 1)]
d[0] = list(range(len(stops2) + 1))
for i in range(len(stops1) + 1):
d[i][0] = i
for i in range(1, len(stops1) + 1):
for j in range(1, len(stops2) + 1):
d[i][j] = (
d[i - 1][j - 1]
if stops_match(stops1[i - 1], stops2[j - 1])
else min((d[i - 1][j], d[i][j - 1], d[i - 1][j - 1])) + 1
)
stops_missing_from_route1: list[RouteStop] = []
stops_missing_from_route2: list[RouteStop] = []
stops_that_dont_match: list[tuple[RouteStop, RouteStop]] = []
i = len(stops1)
j = len(stops2)
while not (i == 0 and j == 0):
action = None
if i > 0 and j > 0:
match = stops_match(stops1[i - 1], stops2[j - 1])
if match and d[i - 1][j - 1] == d[i][j]:
action = "no"
elif not match and d[i - 1][j - 1] + 1 == d[i][j]:
action = "change"
if not action and i > 0 and d[i - 1][j] + 1 == d[i][j]:
action = "add_2"
if not action and j > 0 and d[i][j - 1] + 1 == d[i][j]:
action = "add_1"
match action:
case "add_1":
stops_missing_from_route1.append(stops2[j - 1])
j -= 1
case "add_2":
stops_missing_from_route2.append(stops1[i - 1])
i -= 1
case _:
if action == "change":
stops_that_dont_match.append(
(stops1[i - 1], stops2[j - 1])
)
i -= 1
j -= 1
return (
stops_missing_from_route1,
stops_missing_from_route2,
stops_that_dont_match,
)
def validate_lines(self):
self.found_light_lines = len(

View file

@ -0,0 +1,527 @@
<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6' generator='JOSM'>
<node id='1' visible='true' version='1' lat='0.5' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='2' visible='true' version='1' lat='0.5' lon='1.0'>
<tag k='name' v='Station 2' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='3' visible='true' version='1' lat='0.5' lon='2.0'>
<tag k='name' v='Station 3' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='5' visible='true' version='1' lat='2.0' lon='1.0'>
<tag k='name' v='Station 5' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='8' visible='true' version='1' lat='-2.0' lon='1.5'>
<tag k='name' v='Station 8' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='21' visible='true' version='1' lat='0.0' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='22' visible='true' version='1' lat='0.0' lon='1.0'>
<tag k='name' v='Station 2' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='23' visible='true' version='1' lat='0.0' lon='2.0'>
<tag k='name' v='Station 3' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='24' visible='true' version='1' lat='0.0' lon='3.0'>
<tag k='name' v='Station 4' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='31' visible='true' version='1' lat='0.0003' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='32' visible='true' version='1' lat='0.0003' lon='1.0'>
<tag k='name' v='Station 2' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='33' visible='true' version='1' lat='0.0003' lon='2.0'>
<tag k='name' v='Station 3' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='34' visible='true' version='1' lat='0.0003' lon='3.0'>
<tag k='name' v='Station 4' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='101' visible='true' version='1' lat='0.00015' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='102' visible='true' version='1' lat='0.00015' lon='1.0'>
<tag k='name' v='Station 2' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='103' visible='true' version='1' lat='0.00015' lon='2.0'>
<tag k='name' v='Station 3' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='104' visible='true' version='1' lat='0.00015' lon='3.0'>
<tag k='name' v='Station 4' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='10001' visible='true' version='1' lat='0.0' lon='3.0003' />
<node id='10002' visible='true' version='1' lat='0.0' lon='-0.0003' />
<way id='1' visible='true' version='1'>
<nd ref='21' />
<nd ref='22' />
<nd ref='23' />
<nd ref='24' />
<tag k='railway' v='subway' />
</way>
<way id='2' visible='true' version='1'>
<nd ref='34' />
<nd ref='33' />
<nd ref='32' />
<nd ref='31' />
<tag k='railway' v='subway' />
</way>
<way id='3' visible='true' version='1'>
<nd ref='24' />
<nd ref='8' />
<nd ref='21' />
<tag k='railway' v='subway' />
</way>
<way id='4' visible='true' version='1'>
<nd ref='34' />
<nd ref='10001' />
<nd ref='8' />
<nd ref='10002' />
<nd ref='31' />
<tag k='railway' v='subway' />
</way>
<relation id='102' visible='true' version='1'>
<member type='node' ref='102' role='' />
<member type='node' ref='32' role='stop' />
<member type='node' ref='22' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='103' visible='true' version='1'>
<member type='node' ref='103' role='' />
<member type='node' ref='23' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='104' visible='true' version='1'>
<member type='node' ref='104' role='' />
<member type='node' ref='24' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='107' visible='true' version='1'>
<member type='node' ref='101' role='' />
<member type='node' ref='21' role='stop' />
<member type='node' ref='31' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='113' visible='true' version='1'>
<member type='node' ref='103' role='' />
<member type='node' ref='33' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='114' visible='true' version='1'>
<member type='node' ref='104' role='' />
<member type='node' ref='34' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='151' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='01: 1-2-3' />
<tag k='ref' v='01' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='153' visible='true' version='1'>
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='01: 3-2-1' />
<tag k='ref' v='01' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='155' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='02: 1-2-3' />
<tag k='ref' v='02' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='158' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='02: 1-3 (2)' />
<tag k='ref' v='02' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='159' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='C: 1-3-5-1' />
<tag k='note' v='Circular route without backward' />
<tag k='ref' v='C' />
<tag k='colour' v='gray' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='160' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='C2: 1-5-3-1' />
<tag k='note' v='Circular route with backward' />
<tag k='ref' v='C2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='161' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='C2: 1-3-5-1' />
<tag k='note' v='Circular route with backward' />
<tag k='ref' v='C2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='162' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='03: 1-2-3' />
<tag k='note' v='Route without backward variants and without route_master' />
<tag k='ref' v='03' />
<tag k='colour' v='gray' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='163' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='04: 1-2-3' />
<tag k='ref' v='04' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='164' visible='true' version='1'>
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='04: 2-1' />
<tag k='ref' v='04' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='165' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='05: 1-2-3' />
<tag k='note' v='No route_master. Will find twin by ref.' />
<tag k='ref' v='05' />
<tag k='colour' v='gray' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='166' visible='true' version='1'>
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='05: 3-2-1' />
<tag k='note' v='No route_master. Will find twin by ref.' />
<tag k='ref' v='05' />
<tag k='colour' v='gray' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='167' visible='true' version='1'>
<tag k='note' v='Empty route_master, so that it cannot be assigned to any city' />
<tag k='ref' v='06' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='201' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='1: 1-2-3' />
<tag k='ref' v='1' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='202' visible='true' version='1'>
<member type='node' ref='33' role='' />
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='1: 3-2-1' />
<tag k='ref' v='1' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='203' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='2: 1-2-3' />
<tag k='ref' v='2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='204' visible='true' version='1'>
<member type='node' ref='33' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='2: 3-1' />
<tag k='ref' v='2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='205' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='3: 1-2-3' />
<tag k='ref' v='3' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='206' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='3: 1-2-3' />
<tag k='ref' v='3' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='207' visible='true' version='1'>
<member type='node' ref='34' role='' />
<member type='node' ref='33' role='' />
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='4: 4-3-2-1' />
<tag k='ref' v='4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='208' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='24' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='4: 1-2-3-4' />
<tag k='ref' v='4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='209' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='5: 1-2-3' />
<tag k='ref' v='5' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='210' visible='true' version='1'>
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='5: 2-1' />
<tag k='ref' v='5' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='211' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='21' role='' />
<member type='way' ref='1' role='' />
<member type='way' ref='3' role='' />
<tag k='name' v='C4: 1-2-3-8-1' />
<tag k='ref' v='C4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='212' visible='true' version='1'>
<member type='node' ref='33' role='' />
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='33' role='' />
<member type='way' ref='2' role='' />
<member type='way' ref='4' role='' />
<tag k='name' v='C4: 3-2-1-8-3' />
<tag k='ref' v='C4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='213' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='21' role='' />
<member type='way' ref='1' role='' />
<member type='way' ref='3' role='' />
<tag k='name' v='C3: 1-2-3-8-1' />
<tag k='note' v='Circular route without backward' />
<tag k='ref' v='C3' />
<tag k='colour' v='gray' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='1003' visible='true' version='1'>
<member type='relation' ref='103' role='' />
<member type='relation' ref='113' role='' />
<tag k='public_transport' v='stop_area_group' />
<tag k='type' v='public_transport' />
</relation>
<relation id='10001' visible='true' version='1'>
<member type='relation' ref='201' role='' />
<member type='relation' ref='202' role='' />
<tag k='note' v='Ideal twins. One end is one stop_area, another - two stop_areas combined into a transfer' />
<tag k='ref' v='1' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10002' visible='true' version='1'>
<member type='relation' ref='203' role='' />
<member type='relation' ref='204' role='' />
<tag k='note' v='Routes with matching ends that differ in station count' />
<tag k='ref' v='2' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10003' visible='true' version='1'>
<member type='relation' ref='205' role='' />
<member type='relation' ref='206' role='' />
<tag k='note' v='Two routes with the same ends AND with the same direction' />
<tag k='ref' v='3' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10004' visible='true' version='1'>
<member type='relation' ref='208' role='' />
<member type='relation' ref='207' role='' />
<tag k='note' v='Two routes one end of which belongs to different stop_areas without transfer' />
<tag k='ref' v='4' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10005' visible='true' version='1'>
<member type='relation' ref='209' role='' />
<member type='relation' ref='210' role='' />
<tag k='note' v='Almost twins, but end stations are different' />
<tag k='ref' v='5' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10006' visible='true' version='1'>
<member type='relation' ref='211' role='' />
<member type='relation' ref='212' role='' />
<tag k='note' v='Circular route with forward and backward directions' />
<tag k='ref' v='C4' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10007' visible='true' version='1'>
<member type='relation' ref='163' role='' />
<member type='relation' ref='164' role='' />
<tag k='note' v='Bad: end stations are different' />
<tag k='ref' v='04' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10021' visible='true' version='1'>
<member type='relation' ref='151' role='' />
<member type='relation' ref='153' role='' />
<tag k='note' v='Good route master with full twin routes' />
<tag k='ref' v='01' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10022' visible='true' version='1'>
<member type='relation' ref='155' role='' />
<member type='relation' ref='158' role='' />
<tag k='note' v='Good master, but backward route omits inner stations' />
<tag k='ref' v='02' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10023' visible='true' version='1'>
<member type='relation' ref='161' role='' />
<member type='relation' ref='160' role='' />
<tag k='note' v='Circular route with both directions' />
<tag k='ref' v='C2' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10024' visible='true' version='1'>
<member type='node' ref='2' role='' />
<tag k='note' v='Empty route_master, but center can be calculated due to a spurious member' />
<tag k='ref' v='07' />
<tag k='colour' v='gray' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
</osm>

View file

@ -0,0 +1,578 @@
<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6' generator='JOSM'>
<node id='1' visible='true' version='1' lat='0.5' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='2' visible='true' version='1' lat='0.5' lon='1.0'>
<tag k='name' v='Station 2' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='3' visible='true' version='1' lat='0.5' lon='2.0'>
<tag k='name' v='Station 3' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='4' visible='true' version='1' lat='0.5' lon='3.0'>
<tag k='name' v='Station 4' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='5' visible='true' version='1' lat='2.0' lon='1.5'>
<tag k='name' v='Station 5' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='8' visible='true' version='1' lat='-3.5' lon='3.0'>
<tag k='name' v='Station 8' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='21' visible='true' version='1' lat='0.0' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='22' visible='true' version='1' lat='0.0' lon='1.0'>
<tag k='name' v='Station 2' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='23' visible='true' version='1' lat='0.0' lon='2.0'>
<tag k='name' v='Station 3' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='24' visible='true' version='1' lat='0.0' lon='3.0'>
<tag k='name' v='Station 4' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='25' visible='true' version='1' lat='0.0' lon='4.0' />
<node id='26' visible='true' version='1' lat='0.0' lon='5.0'>
<tag k='name' v='Station 6' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='27' visible='true' version='1' lat='0.0' lon='6.0' />
<node id='31' visible='true' version='1' lat='0.0003' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='32' visible='true' version='1' lat='0.0003' lon='1.0'>
<tag k='name' v='Station 2' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='33' visible='true' version='1' lat='0.0003' lon='2.0'>
<tag k='name' v='Station 3' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='34' visible='true' version='1' lat='0.0003' lon='3.0'>
<tag k='name' v='Station 4' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='35' visible='true' version='1' lat='0.0003' lon='4.0' />
<node id='36' visible='true' version='1' lat='0.0003' lon='5.0'>
<tag k='name' v='Station 6' />
<tag k='public_transport' v='stop_position' />
<tag k='subway' v='yes' />
</node>
<node id='37' visible='true' version='1' lat='0.0003' lon='6.0' />
<node id='101' visible='true' version='1' lat='0.00015' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='102' visible='true' version='1' lat='0.00015' lon='1.0'>
<tag k='name' v='Station 2' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='103' visible='true' version='1' lat='0.00015' lon='2.0'>
<tag k='name' v='Station 3' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='104' visible='true' version='1' lat='0.00015' lon='3.0'>
<tag k='name' v='Station 4' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='105' visible='true' version='1' lat='0.00015' lon='4.0'>
<tag k='name' v='Station 5' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='106' visible='true' version='1' lat='0.00015' lon='5.0'>
<tag k='name' v='Station 6' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='107' visible='true' version='1' lat='0.00015' lon='6.0'>
<tag k='name' v='Station 7' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='10001' visible='true' version='1' lat='0.0' lon='6.0003' />
<node id='10002' visible='true' version='1' lat='0.0' lon='-0.0003' />
<way id='1' visible='true' version='1'>
<nd ref='21' />
<nd ref='22' />
<nd ref='23' />
<nd ref='24' />
<nd ref='25' />
<nd ref='26' />
<nd ref='27' />
<tag k='railway' v='subway' />
</way>
<way id='2' visible='true' version='1'>
<nd ref='37' />
<nd ref='36' />
<nd ref='35' />
<nd ref='34' />
<nd ref='33' />
<nd ref='32' />
<nd ref='31' />
<tag k='railway' v='subway' />
</way>
<way id='3' visible='true' version='1'>
<nd ref='27' />
<nd ref='8' />
<nd ref='21' />
<tag k='railway' v='subway' />
</way>
<way id='4' visible='true' version='1'>
<nd ref='37' />
<nd ref='10001' />
<nd ref='8' />
<nd ref='10002' />
<nd ref='31' />
<tag k='railway' v='subway' />
</way>
<relation id='102' visible='true' version='1'>
<member type='node' ref='102' role='' />
<member type='node' ref='32' role='stop' />
<member type='node' ref='22' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='103' visible='true' version='1'>
<member type='node' ref='103' role='' />
<member type='node' ref='23' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='104' visible='true' version='1'>
<member type='node' ref='104' role='' />
<member type='node' ref='24' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='106' visible='true' version='1'>
<member type='node' ref='36' role='stop' />
<member type='node' ref='106' role='' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='107' visible='true' version='1'>
<member type='node' ref='101' role='' />
<member type='node' ref='21' role='stop' />
<member type='node' ref='31' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='113' visible='true' version='1'>
<member type='node' ref='103' role='' />
<member type='node' ref='33' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='114' visible='true' version='1'>
<member type='node' ref='104' role='' />
<member type='node' ref='34' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='116' visible='true' version='1'>
<member type='node' ref='106' role='' />
<member type='node' ref='26' role='stop' />
<tag k='public_transport' v='stop_area' />
<tag k='type' v='public_transport' />
</relation>
<relation id='151' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='01: 1-2-3' />
<tag k='ref' v='01' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='152' visible='true' version='1'>
<member type='node' ref='3' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='01: 3-1' />
<tag k='ref' v='01' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='153' visible='true' version='1'>
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='01: 3-2-1' />
<tag k='ref' v='01' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='154' visible='true' version='1'>
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='02: 4-3' />
<tag k='ref' v='02' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='155' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='02: 1-3' />
<tag k='ref' v='02' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='156' visible='true' version='1'>
<member type='node' ref='2' role='' />
<member type='node' ref='4' role='' />
<tag k='name' v='02: 2-4' />
<tag k='ref' v='02' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='157' visible='true' version='1'>
<member type='node' ref='4' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='02: 4-1' />
<tag k='ref' v='02' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='158' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='3' role='' />
<tag k='name' v='02: 1-3 (2)' />
<tag k='note' v='This should not be recognized as twin for route &quot;2: 1-3&quot;' />
<tag k='ref' v='02' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='159' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='1' role='' />
<tag k='colour' v='gray' />
<tag k='name' v='C: 1-2-3-4-5-1' />
<tag k='note' v='Circular route without backward' />
<tag k='ref' v='C' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='160' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='C2: 1-5-4-3-2-1' />
<tag k='note' v='Circular route with backward' />
<tag k='ref' v='C2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='161' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='C2: 1-2-3-4-5-1' />
<tag k='note' v='Circular route with backward' />
<tag k='ref' v='C2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='201' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='1: 1-2-3' />
<tag k='ref' v='1' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='202' visible='true' version='1'>
<member type='node' ref='33' role='' />
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='1: 3-2-1' />
<tag k='ref' v='1' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='203' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='105' role='' />
<member type='node' ref='26' role='' />
<member type='node' ref='107' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='2: 1-2-3-5-6-7' />
<tag k='ref' v='2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='204' visible='true' version='1'>
<member type='node' ref='107' role='' />
<member type='node' ref='36' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='2: 7-6-1' />
<tag k='ref' v='2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='205' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='24' role='' />
<member type='node' ref='105' role='' />
<member type='node' ref='26' role='' />
<member type='node' ref='107' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='3: 1-2-3-4-5-6-7' />
<tag k='ref' v='3' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='206' visible='true' version='1'>
<member type='node' ref='107' role='' />
<member type='node' ref='36' role='' />
<member type='node' ref='105' role='' />
<member type='node' ref='33' role='' />
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='3: 7-6-5-3-2-1' />
<tag k='ref' v='3' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='207' visible='true' version='1'>
<member type='node' ref='34' role='' />
<member type='node' ref='33' role='' />
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='4: 4-3-2-1' />
<tag k='ref' v='4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='208' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='24' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='4: 1-2-3-4' />
<tag k='ref' v='4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='209' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='105' role='' />
<member type='node' ref='26' role='' />
<member type='node' ref='107' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='5: 1-2-3-5-6-7' />
<tag k='ref' v='5' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='210' visible='true' version='1'>
<member type='node' ref='36' role='' />
<member type='node' ref='105' role='' />
<member type='node' ref='33' role='' />
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='way' ref='2' role='' />
<tag k='name' v='5: 6-5-3-2-1' />
<tag k='ref' v='5' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='211' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='105' role='' />
<member type='node' ref='26' role='' />
<member type='node' ref='107' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='21' role='' />
<member type='way' ref='1' role='' />
<member type='way' ref='3' role='' />
<tag k='name' v='C4: 1-2-3-5-6-7-8-1' />
<tag k='ref' v='C4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='212' visible='true' version='1'>
<member type='node' ref='107' role='' />
<member type='node' ref='36' role='' />
<member type='node' ref='105' role='' />
<member type='node' ref='33' role='' />
<member type='node' ref='32' role='' />
<member type='node' ref='31' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='107' role='' />
<member type='way' ref='2' role='' />
<member type='way' ref='4' role='' />
<tag k='name' v='C4: 7-6-5-3-2-1-8-7' />
<tag k='ref' v='C4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='213' visible='true' version='1'>
<member type='node' ref='21' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='105' role='' />
<member type='node' ref='26' role='' />
<member type='node' ref='107' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='21' role='' />
<member type='way' ref='1' role='' />
<member type='way' ref='3' role='' />
<tag k='colour' v='gray' />
<tag k='name' v='C3: 1-2-3-5-6-7-8-1' />
<tag k='note' v='Circular route without backward' />
<tag k='ref' v='C3' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='1003' visible='true' version='1'>
<member type='relation' ref='103' role='' />
<member type='relation' ref='113' role='' />
<tag k='public_transport' v='stop_area_group' />
<tag k='type' v='public_transport' />
</relation>
<relation id='1006' visible='true' version='1'>
<member type='relation' ref='116' role='' />
<member type='relation' ref='106' role='' />
<tag k='public_transport' v='stop_area_group' />
<tag k='type' v='public_transport' />
</relation>
<relation id='10001' visible='true' version='1'>
<member type='relation' ref='201' role='' />
<member type='relation' ref='202' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Ideal twins. One end is one stop_area, another - two stop_areas combined into a transfer' />
<tag k='ref' v='1' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10002' visible='true' version='1'>
<member type='relation' ref='203' role='' />
<member type='relation' ref='204' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Twin candidates that differ by 50% in station count' />
<tag k='ref' v='2' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10003' visible='true' version='1'>
<member type='relation' ref='205' role='' />
<member type='relation' ref='206' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Twins that differ by 15% in station count' />
<tag k='ref' v='3' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10004' visible='true' version='1'>
<member type='relation' ref='207' role='' />
<member type='relation' ref='208' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Two routes one end of which belongs to different stop_areas without transfer' />
<tag k='ref' v='4' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10005' visible='true' version='1'>
<member type='relation' ref='209' role='' />
<member type='relation' ref='210' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Almost twins, but end stations are different' />
<tag k='ref' v='5' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10006' visible='true' version='1'>
<member type='relation' ref='211' role='' />
<member type='relation' ref='212' role='' />
<tag k='colour' v='gray' />
<tag k='ref' v='C4' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10021' visible='true' version='1'>
<member type='relation' ref='151' role='' />
<member type='relation' ref='152' role='' />
<member type='relation' ref='153' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='There is a Forward 1 route (1-2-3) and two backward (3-1 and 3-2-1). Twin for forward should be 3-station backward variant.' />
<tag k='ref' v='01' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10022' visible='true' version='1'>
<member type='relation' ref='154' role='' />
<member type='relation' ref='155' role='' />
<member type='relation' ref='156' role='' />
<member type='relation' ref='157' role='' />
<member type='relation' ref='158' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='At least two routes in each direction, but no twins. Duplicated route is not a twin.' />
<tag k='ref' v='02' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='10023' visible='true' version='1'>
<member type='relation' ref='161' role='' />
<member type='relation' ref='160' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Circular route with both directions' />
<tag k='ref' v='C2' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
</osm>

View file

@ -0,0 +1,680 @@
<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6' generator='JOSM'>
<node id='1' visible='true' version='1' lat='0.0' lon='0.0'>
<tag k='name' v='Station 1' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='2' visible='true' version='1' lat='0.0' lon='0.01'>
<tag k='name' v='Station 2' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='3' visible='true' version='1' lat='0.0' lon='0.02'>
<tag k='name' v='Station 3' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='4' visible='true' version='1' lat='0.0' lon='0.03'>
<tag k='name' v='Station 4' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='5' visible='true' version='1' lat='0.0' lon='0.04'>
<tag k='name' v='Station 5' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='6' visible='true' version='1' lat='0.0' lon='0.05'>
<tag k='name' v='Station 6' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='7' visible='true' version='1' lat='0.0' lon='0.06'>
<tag k='name' v='Station 7' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='8' visible='true' version='1' lat='0.0' lon='0.07'>
<tag k='name' v='Station 8' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='9' visible='true' version='1' lat='0.0' lon='0.08'>
<tag k='name' v='Station 9' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='10' visible='true' version='1' lat='0.0' lon='0.09'>
<tag k='name' v='Station 10' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='11' visible='true' version='1' lat='0.0' lon='0.1'>
<tag k='name' v='Station 11' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='12' visible='true' version='1' lat='0.0' lon='0.11'>
<tag k='name' v='Station 12' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='13' visible='true' version='1' lat='0.0' lon='0.12'>
<tag k='name' v='Station 13' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='14' visible='true' version='1' lat='0.0' lon='0.13'>
<tag k='name' v='Station 14' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='15' visible='true' version='1' lat='0.0' lon='0.14'>
<tag k='name' v='Station 15' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='16' visible='true' version='1' lat='0.0' lon='0.15'>
<tag k='name' v='Station 16' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='17' visible='true' version='1' lat='0.0' lon='0.16'>
<tag k='name' v='Station 17' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='18' visible='true' version='1' lat='0.0' lon='0.17'>
<tag k='name' v='Station 18' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='19' visible='true' version='1' lat='0.0' lon='0.18'>
<tag k='name' v='Station 19' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='20' visible='true' version='1' lat='0.0' lon='0.19'>
<tag k='name' v='Station 20' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='21' visible='true' version='1' lat='0.0003' lon='0.09'>
<tag k='name' v='Station 10(1)' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='22' visible='true' version='1' lat='0.0003' lon='0.1'>
<tag k='name' v='Station 11(1)' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='23' visible='true' version='1' lat='0.004' lon='0.09'>
<tag k='name' v='Station 10(2)' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<node id='24' visible='true' version='1' lat='0.004' lon='0.1'>
<tag k='name' v='Station 11(2)' />
<tag k='railway' v='station' />
<tag k='station' v='subway' />
</node>
<way id='1' visible='true' version='1'>
<nd ref='1' />
<nd ref='2' />
<nd ref='3' />
<nd ref='4' />
<nd ref='5' />
<nd ref='6' />
<nd ref='7' />
<nd ref='8' />
<nd ref='9' />
<tag k='railway' v='subway' />
</way>
<way id='2' visible='true' version='1'>
<nd ref='9' />
<nd ref='10' />
<nd ref='11' />
<nd ref='12' />
<tag k='railway' v='subway' />
</way>
<way id='3' visible='true' version='1'>
<nd ref='12' />
<nd ref='13' />
<nd ref='14' />
<nd ref='15' />
<nd ref='16' />
<nd ref='17' />
<nd ref='18' />
<nd ref='19' />
<nd ref='20' />
<tag k='railway' v='subway' />
</way>
<way id='4' visible='true' version='1'>
<nd ref='9' />
<nd ref='21' />
<nd ref='22' />
<nd ref='12' />
<tag k='railway' v='subway' />
</way>
<way id='5' visible='true' version='1'>
<nd ref='9' />
<nd ref='23' />
<nd ref='24' />
<nd ref='12' />
<tag k='railway' v='subway' />
</way>
<relation id='101' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='10' role='' />
<member type='node' ref='11' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='20' role='' />
<tag k='name' v='1: 1-...-9-10-11-...-20' />
<tag k='ref' v='1' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='102' visible='true' version='1'>
<member type='node' ref='20' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='21' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='1: 20-...12-11(1)-10(1)-9-...-1' />
<tag k='ref' v='1' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='103' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='10' role='' />
<member type='node' ref='11' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='20' role='' />
<tag k='name' v='2: 1-...-9-10-11-...-20' />
<tag k='ref' v='2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='104' visible='true' version='1'>
<member type='node' ref='20' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='24' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='2: 20-...12-11(2)-10(2)-9-...-1' />
<tag k='ref' v='2' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='105' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='10' role='' />
<member type='node' ref='11' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='20' role='' />
<tag k='name' v='3: 1-...-9-10-11-...-20' />
<tag k='ref' v='3' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='106' visible='true' version='1'>
<member type='node' ref='20' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='3: 20-...-12-11(1)-9-...-1' />
<tag k='note' v='Miss Station 10(1) compared to the twin route' />
<tag k='ref' v='3' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='107' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='10' role='' />
<member type='node' ref='11' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='20' role='' />
<tag k='name' v='4: 1-...-9-10-11-...-20' />
<tag k='ref' v='4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='108' visible='true' version='1'>
<member type='node' ref='20' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='24' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<tag k='name' v='4: 20-...12-11(2)-9-...-1' />
<tag k='note' v='Miss Station 10(2) compared to the twin route' />
<tag k='ref' v='4' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='201' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='10' role='' />
<member type='node' ref='11' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='20' role='' />
<member type='way' ref='1' role='' />
<member type='way' ref='2' role='' />
<member type='way' ref='3' role='' />
<tag k='name' v='11: 1-...-9-10-11-...-20' />
<tag k='ref' v='11' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='202' visible='true' version='1'>
<member type='node' ref='20' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='21' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<member type='way' ref='3' role='' />
<member type='way' ref='4' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='11: 20-...-12-11(1)-10(1)-9-...-1' />
<tag k='ref' v='11' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='203' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='10' role='' />
<member type='node' ref='11' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='20' role='' />
<member type='way' ref='1' role='' />
<member type='way' ref='2' role='' />
<member type='way' ref='3' role='' />
<tag k='name' v='12: 1-...-9-10-11-...-20' />
<tag k='ref' v='12' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='204' visible='true' version='1'>
<member type='node' ref='20' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='24' role='' />
<member type='node' ref='23' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<member type='way' ref='3' role='' />
<member type='way' ref='5' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='12: 20-...12-11(2)-10(2)-9-...-1' />
<tag k='ref' v='12' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='205' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='10' role='' />
<member type='node' ref='11' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='20' role='' />
<member type='way' ref='1' role='' />
<member type='way' ref='2' role='' />
<member type='way' ref='3' role='' />
<tag k='name' v='13: 1-...-9-10-11-...-20' />
<tag k='ref' v='13' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='206' visible='true' version='1'>
<member type='node' ref='20' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='22' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<member type='way' ref='3' role='' />
<member type='way' ref='4' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='13: 20-...-12-11(1)-9-...-1' />
<tag k='note' v='Miss Station 10(1) compared to the twin route' />
<tag k='ref' v='13' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='207' visible='true' version='1'>
<member type='node' ref='1' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='10' role='' />
<member type='node' ref='11' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='20' role='' />
<member type='way' ref='1' role='' />
<member type='way' ref='2' role='' />
<member type='way' ref='3' role='' />
<tag k='name' v='14: 1-...-9-10-11-...-20' />
<tag k='ref' v='14' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='208' visible='true' version='1'>
<member type='node' ref='20' role='' />
<member type='node' ref='19' role='' />
<member type='node' ref='18' role='' />
<member type='node' ref='17' role='' />
<member type='node' ref='16' role='' />
<member type='node' ref='15' role='' />
<member type='node' ref='14' role='' />
<member type='node' ref='13' role='' />
<member type='node' ref='12' role='' />
<member type='node' ref='24' role='' />
<member type='node' ref='9' role='' />
<member type='node' ref='8' role='' />
<member type='node' ref='7' role='' />
<member type='node' ref='6' role='' />
<member type='node' ref='5' role='' />
<member type='node' ref='4' role='' />
<member type='node' ref='3' role='' />
<member type='node' ref='2' role='' />
<member type='node' ref='1' role='' />
<member type='way' ref='3' role='' />
<member type='way' ref='5' role='' />
<member type='way' ref='1' role='' />
<tag k='name' v='14: 20-...12-11(2)-9-...-1' />
<tag k='note' v='Miss Station 10(2) compared to the twin route' />
<tag k='ref' v='14' />
<tag k='route' v='subway' />
<tag k='type' v='route' />
</relation>
<relation id='1101' visible='true' version='1'>
<member type='relation' ref='101' role='' />
<member type='relation' ref='102' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='No rails, no omitted stations, close paths' />
<tag k='ref' v='1' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='1102' visible='true' version='1'>
<member type='relation' ref='103' role='' />
<member type='relation' ref='104' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='No rails, no omitted stations, distant paths' />
<tag k='ref' v='2' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='1103' visible='true' version='1'>
<member type='relation' ref='105' role='' />
<member type='relation' ref='106' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='No rails, close paths, one route is missing a station inside a divergent rail span' />
<tag k='ref' v='3' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='1104' visible='true' version='1'>
<member type='relation' ref='107' role='' />
<member type='relation' ref='108' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='No rails, distant paths, one route is missing a station inside a divergent rail span' />
<tag k='ref' v='4' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='1201' visible='true' version='1'>
<member type='relation' ref='201' role='' />
<member type='relation' ref='202' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Rails, no omitted stations, close paths' />
<tag k='ref' v='11' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='1202' visible='true' version='1'>
<member type='relation' ref='203' role='' />
<member type='relation' ref='204' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Rails, no omitted stations, distant paths' />
<tag k='ref' v='12' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='1203' visible='true' version='1'>
<member type='relation' ref='205' role='' />
<member type='relation' ref='206' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Rails, close paths, one route is missing a station inside a divergent rail span' />
<tag k='ref' v='13' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
<relation id='1204' visible='true' version='1'>
<member type='relation' ref='207' role='' />
<member type='relation' ref='208' role='' />
<tag k='colour' v='gray' />
<tag k='note' v='Rails, distant paths, one route is missing a station inside a divergent rail span' />
<tag k='ref' v='14' />
<tag k='route_master' v='subway' />
<tag k='type' v='route_master' />
</relation>
</osm>

View file

@ -42,11 +42,11 @@ metro_samples = [
"cities_info": [
{
"num_stations": 2,
"num_lines": 1,
"num_light_lines": 0,
"num_interchanges": 0,
},
],
"num_lines": 1,
"num_light_lines": 0,
"num_interchanges": 0,
"errors": [],
"warnings": [],
"notices": [],
@ -110,14 +110,9 @@ metro_samples = [
"num_stations": 4,
},
],
"num_lines": 1,
"num_light_lines": 0,
"num_interchanges": 0,
"errors": [
'Angle between stops around "Station 3" (2.0, 0.0) '
'is too narrow, 0 degrees (relation 1, "Forward")',
'Angle between stops around "Station 2" (1.0, 0.0) '
'is too narrow, 0 degrees (relation 1, "Forward")',
'Angle between stops around "Station 3" (2.0, 0.0) is too narrow, 0 degrees (relation 1, "Forward")', # noqa: E501
'Angle between stops around "Station 2" (1.0, 0.0) is too narrow, 0 degrees (relation 1, "Forward")', # noqa: E501
],
"warnings": [],
"notices": [],
@ -175,14 +170,9 @@ metro_samples = [
"num_stations": 3,
},
],
"num_lines": 1,
"num_light_lines": 0,
"num_interchanges": 0,
"errors": [
'Angle between stops around "Station 2" (1.0, 0.0) '
'is too narrow, 11 degrees (relation 1, "Forward")',
'Angle between stops around "Station 2" (1.0, 0.0) '
'is too narrow, 11 degrees (relation 2, "Backward")',
'Angle between stops around "Station 2" (1.0, 0.0) is too narrow, 11 degrees (relation 1, "Forward")', # noqa: E501
'Angle between stops around "Station 2" (1.0, 0.0) is too narrow, 11 degrees (relation 2, "Backward")', # noqa: E501
],
"warnings": [],
"notices": [],
@ -240,16 +230,11 @@ metro_samples = [
"num_stations": 3,
},
],
"num_lines": 1,
"num_light_lines": 0,
"num_interchanges": 0,
"errors": [],
"warnings": [],
"notices": [
'Angle between stops around "Station 2" (1.0, 0.0) '
'is too narrow, 27 degrees (relation 1, "Forward")',
'Angle between stops around "Station 2" (1.0, 0.0) '
'is too narrow, 27 degrees (relation 2, "Backward")',
'Angle between stops around "Station 2" (1.0, 0.0) is too narrow, 27 degrees (relation 1, "Forward")', # noqa: E501
'Angle between stops around "Station 2" (1.0, 0.0) is too narrow, 27 degrees (relation 2, "Backward")', # noqa: E501
],
},
{
@ -326,16 +311,45 @@ metro_samples = [
"num_stations": 4,
},
],
"num_lines": 1,
"num_light_lines": 0,
"num_interchanges": 0,
"errors": [
'Stops on tracks are unordered near "Station 2" (1.0, 0.0) '
'(relation 1, "Forward")',
'Stops on tracks are unordered near "Station 3" (0.0, 0.5) '
'(relation 2, "Backward")',
'Stops on tracks are unordered near "Station 2" (1.0, 0.0) (relation 1, "Forward")', # noqa: E501
'Stops on tracks are unordered near "Station 3" (0.0, 0.5) (relation 2, "Backward")', # noqa: E501
],
"warnings": [],
"notices": [],
},
{
"name": (
"Many different route masters, both on naked stations and "
"stop_positions/stop_areas/transfers, both linear and circular"
),
"xml_file": "assets/route_masters.osm",
"cities_info": [
{
"num_stations": (3 + 3 + 3 + 5 + 3 + 3 + 4)
+ (3 + 3 + 3 + 3 + 3 + 3 + 4),
"num_lines": 7 + 7,
"num_interchanges": 0 + 1,
},
],
"errors": [
'Only one route in route_master. Please check if it needs a return route (relation 162, "03: 1-2-3")' # noqa: E501
],
"warnings": [],
"notices": [
'Route does not have a return direction (relation 155, "02: 1-2-3")', # noqa: E501
'Route does not have a return direction (relation 158, "02: 1-3 (2)")', # noqa: E501
'Only one route in route_master. Please check if it needs a return route (relation 159, "C: 1-3-5-1")', # noqa: E501
'Route does not have a return direction (relation 163, "04: 1-2-3")', # noqa: E501
'Route does not have a return direction (relation 164, "04: 2-1")', # noqa: E501
'Stop Station 2 (1.0, 0.0) is included into the r203 but not included into r204 (relation 204, "2: 3-1")', # noqa: E501
'Route does not have a return direction (relation 205, "3: 1-2-3")', # noqa: E501
'Route does not have a return direction (relation 206, "3: 1-2-3")', # noqa: E501
'Route does not have a return direction (relation 207, "4: 4-3-2-1")', # noqa: E501
'Route does not have a return direction (relation 208, "4: 1-2-3-4")', # noqa: E501
'Route does not have a return direction (relation 209, "5: 1-2-3")', # noqa: E501
'Route does not have a return direction (relation 210, "5: 2-1")', # noqa: E501
'Only one route in route_master. Please check if it needs a return route (relation 213, "C3: 1-2-3-8-1")', # noqa: E501
],
},
]

View file

@ -6,25 +6,17 @@ metro_samples = [
{
"id": 1,
"name": "Intersecting 2 metro lines",
"country": "World",
"continent": "Africa",
"num_stations": 6,
"num_stations": 4 + 2,
"num_lines": 2,
"num_light_lines": 0,
"num_interchanges": 1,
"bbox": "-179, -89, 179, 89",
"networks": "network-1",
},
{
"id": 2,
"name": "One light rail line",
"country": "World",
"continent": "Africa",
"num_stations": 2,
"num_lines": 0,
"num_light_lines": 1,
"num_interchanges": 0,
"bbox": "-179, -89, 179, 89",
"networks": "network-2",
},
],

View file

@ -0,0 +1,78 @@
metro_samples = [
{
"name": (
"Many different routes, both on naked stations and stop_positions/stop_areas/transfers, both linear and circular" # noqa: E501
),
"xml_file": "assets/twin_routes.osm",
"cities_info": [
{
"num_stations": (3 + 4 + 5 + 5) + (3 + 6 + 7 + 5 + 6 + 7 + 7),
"num_lines": 4 + 7,
"num_interchanges": 0 + 2,
},
],
"twin_routes": { # route master => twin routes
"r10021": {"r151": "r153", "r153": "r151"},
"r10022": {},
"r10023": {},
"C": {},
"r10001": {"r201": "r202", "r202": "r201"},
"r10002": {},
"r10003": {"r205": "r206", "r206": "r205"},
"r10004": {},
"r10005": {},
"r10006": {},
"C3": {},
},
"errors": [],
"warnings": [],
"notices": [
'Route does not have a return direction (relation 154, "02: 4-3")',
'Route does not have a return direction (relation 155, "02: 1-3")',
'Route does not have a return direction (relation 156, "02: 2-4")',
'Route does not have a return direction (relation 157, "02: 4-1")',
'Route does not have a return direction (relation 158, "02: 1-3 (2)")', # noqa: E501
'Only one route in route_master. Please check if it needs a return route (relation 159, "C: 1-2-3-4-5-1")', # noqa: E501
'Stop Station 4 (3.0, 0.0) is included into the r205 but not included into r206 (relation 206, "3: 7-6-5-3-2-1")', # noqa: E501
'Route does not have a return direction (relation 207, "4: 4-3-2-1")', # noqa: E501
'Route does not have a return direction (relation 208, "4: 1-2-3-4")', # noqa: E501
'Route does not have a return direction (relation 209, "5: 1-2-3-5-6-7")', # noqa: E501
'Route does not have a return direction (relation 210, "5: 6-5-3-2-1")', # noqa: E501
'Only one route in route_master. Please check if it needs a return route (relation 213, "C3: 1-2-3-5-6-7-8-1")', # noqa: E501
],
},
{
"name": "Twin routes diverging for some extent",
"xml_file": "assets/twin_routes_with_divergence.osm",
"cities_info": [
{
"num_stations": (22 + 22 + 21 + 21) * 2,
"num_lines": 4 * 2,
"num_interchanges": 0,
},
],
"twin_routes": { # route master => twin routes
"r1101": {"r101": "r102", "r102": "r101"},
"r1102": {"r103": "r104", "r104": "r103"},
"r1103": {"r105": "r106", "r106": "r105"},
"r1104": {"r107": "r108", "r108": "r107"},
"r1201": {"r201": "r202", "r202": "r201"},
"r1202": {"r203": "r204", "r204": "r203"},
"r1203": {"r205": "r206", "r206": "r205"},
"r1204": {"r207": "r208", "r208": "r207"},
},
"errors": [],
"warnings": [],
"notices": [
'Should there be one stoparea or a transfer between Station 11 (0.1, 0.0) and Station 11(1) (0.1, 0.0003)? (relation 101, "1: 1-...-9-10-11-...-20")', # noqa: E501
'Should there be one stoparea or a transfer between Station 10 (0.09, 0.0) and Station 10(1) (0.09, 0.0003)? (relation 101, "1: 1-...-9-10-11-...-20")', # noqa: E501
'Stop Station 10 (0.09, 0.0) is included into the r105 but not included into r106 (relation 106, "3: 20-...-12-11(1)-9-...-1")', # noqa: E501
'Should there be one stoparea or a transfer between Station 11 (0.1, 0.0) and Station 11(1) (0.1, 0.0003)? (relation 105, "3: 1-...-9-10-11-...-20")', # noqa: E501
'Stop Station 10 (0.09, 0.0) is included into the r107 but not included into r108 (relation 108, "4: 20-...12-11(2)-9-...-1")', # noqa: E501
'Should there be one stoparea or a transfer between Station 11 (0.1, 0.0) and Station 11(1) (0.1, 0.0003)? (relation 201, "11: 1-...-9-10-11-...-20")', # noqa: E501
'Should there be one stoparea or a transfer between Station 10 (0.09, 0.0) and Station 10(1) (0.09, 0.0003)? (relation 201, "11: 1-...-9-10-11-...-20")', # noqa: E501
'Stop Station 10 (0.09, 0.0) is included into the r205 but not included into r206 (relation 206, "13: 20-...-12-11(1)-9-...-1")', # noqa: E501
'Should there be one stoparea or a transfer between Station 11 (0.1, 0.0) and Station 11(1) (0.1, 0.0003)? (relation 205, "13: 1-...-9-10-11-...-20")', # noqa: E501
],
},
]

View file

@ -1,4 +1,11 @@
from tests.sample_data_for_error_messages import metro_samples
import itertools
from tests.sample_data_for_error_messages import (
metro_samples as metro_samples_error,
)
from tests.sample_data_for_twin_routes import (
metro_samples as metro_samples_route_masters,
)
from tests.util import TestCase
@ -20,6 +27,10 @@ class TestValidationMessages(TestCase):
)
def test_validation_messages(self) -> None:
for sample in metro_samples:
for sample in itertools.chain(
metro_samples_error, metro_samples_route_masters
):
if "errors" not in sample:
continue
with self.subTest(msg=sample["name"]):
self._test_validation_messages_for_network(sample)

View file

@ -0,0 +1,26 @@
from tests.util import TestCase
from tests.sample_data_for_twin_routes import metro_samples
class TestRouteMaster(TestCase):
def _test_find_twin_routes_for_network(self, metro_sample: dict) -> None:
cities, transfers = self.prepare_cities(metro_sample)
city = cities[0]
self.assertTrue(city.is_good)
for route_master_id, expected_twin_ids in metro_sample[
"twin_routes"
].items():
route_master = city.routes[route_master_id]
calculated_twins = route_master.find_twin_routes()
calculated_twin_ids = {
r1.id: r2.id for r1, r2 in calculated_twins.items()
}
self.assertDictEqual(expected_twin_ids, calculated_twin_ids)
def test_find_twin_routes(self) -> None:
for sample in metro_samples:
with self.subTest(msg=sample["name"]):
self._test_find_twin_routes_for_network(sample)