diff --git a/mapsme_subways.py b/mapsme_subways.py index 4ddad90..619d27b 100755 --- a/mapsme_subways.py +++ b/mapsme_subways.py @@ -197,31 +197,80 @@ def dump_data(city, f): write_yaml(result, f) +OSM_TYPES = {'n': (0, 'node'), 'w': (2, 'way'), 'r': (3, 'relation')} + + def prepare_mapsme_data(transfers, cities): + def uid(elid, typ=None): + t = elid[0] + osm_id = int(elid[1:]) + if not typ: + osm_id = osm_id << 2 + OSM_TYPES[t][0] + elif typ != t: + raise Exception('Got {}, expected {}'.format(elid, typ)) + return osm_id << 1 + stops = {} # el_id -> station data networks = [] for city in cities: - network = {'network': city.name, 'routes': []} + agency_id = 0 # TODO + network = {'network': city.name, 'routes': [], 'agency_id': agency_id} for route in city: routes = { 'type': route.mode, 'ref': route.ref, 'name': route.name, - 'route_id': 0, # TODO + 'route_id': uid(route.id, 'r'), 'itineraries': [] } for variant in route: - stops = [] - # TODO + itin = [] + for stop in variant: + stop.mapsme_uid = uid(stop.id) + stops[stop.id] = stop + itin.append(stop.mapsme_uid) + routes['itineraries'].append({'stops': itin}) network['routes'].append(routes) networks.append(network) - # TODO: prepare a set of all stations + + m_stops = [] + for stop in stops.values(): + st = { + 'name': stop.name, + 'int_name': stop.int_name, + 'lat': stop.center[1], + 'lon': stop.center[0], + 'osm_type': OSM_TYPES[stop.id[0]][1], + 'osm_id': int(stop.id[1:]), + 'id': stop.mapsme_uid, + 'entrances': [], + 'exits': [], + } + for e_l, k in ((stop.entrances, 'entrances'), (stop.exits, 'exits')): + for e in e_l: + if e[0] == 'n': + st[k].append({ + 'node_id': int(e[1:]), + 'lon': stop.centers[e][0], + 'lat': stop.centers[e][1], + 'distance': 60 + }) + m_stops.append(st) + c_transfers = [] - for t in transfers: - # TODO: decouple transfers and add travel time - pass + for t_set in transfers: + t = list(t_set) + for t_first in range(len(t) - 1): + for t_second in range(t_first + 1, len(t)): + if t[t_first].id in stops and t[t_second].id in stops: + c_transfers.append([ + uid(t[t_first].id), + uid(t[t_second].id), + 60 + ]) + result = { - 'stops': list(stops.values()), + 'stops': m_stops, 'transfers': c_transfers, 'networks': networks } @@ -323,4 +372,5 @@ if __name__ == '__main__': # Finally, prepare a JSON file for MAPS.ME if options.output: - json.dump(prepare_mapsme_data(transfers, good_cities), options.output) + json.dump(prepare_mapsme_data(transfers, good_cities), options.output, + indent=1, ensure_ascii=False) diff --git a/scripts/process_subways.sh b/scripts/process_subways.sh index 2626952..1792274 100755 --- a/scripts/process_subways.sh +++ b/scripts/process_subways.sh @@ -12,6 +12,7 @@ if [ $# -lt 1 -a -z "${PLANET-}" ]; then echo "- CITY: name of a city to process" echo "- BBOX: bounding box of an extract; x1,y1,x2,y2" echo "- DUMP: file name to dump city data" + echo "- MAPSME: file name for maps.me json output" echo "- OSMCTOOLS: path to osmconvert and osmupdate binaries" echo "- PYTHON: python 3 executable" echo "- GIT_PULL: set to 1 to update the scripts" @@ -70,7 +71,7 @@ QNODES="station=subway =light_rail =monorail railway=subway_entrance subway=yes # Running the validation VALIDATION="$TMPDIR/validation.json" -"$PYTHON" "$SUBWAYS_PATH/mapsme_subways.py" -q -x "$FILTERED_DATA" -l "$VALIDATION" ${CITY+-c "$CITY"${DUMP+ -d "$DUMP"}} +"$PYTHON" "$SUBWAYS_PATH/mapsme_subways.py" -q -x "$FILTERED_DATA" -l "$VALIDATION" ${MAPSME+-o "$MAPSME"} ${CITY+-c "$CITY"${DUMP+ -d "$DUMP"}} rm "$FILTERED_DATA" # Preparing HTML files diff --git a/subway_structure.py b/subway_structure.py index c3fbc81..a4effe5 100644 --- a/subway_structure.py +++ b/subway_structure.py @@ -18,6 +18,8 @@ used_entrances = set() def el_id(el): + if not el: + return None if 'type' not in el: raise Exception('What is this element? {}'.format(el)) return el['type'][0] + str(el.get('id', el.get('ref', ''))) @@ -117,6 +119,7 @@ class StopArea: self.exits = set() # el_id of subway_entrance for leaving the platform self.entrances = set() # el_id of subway_entrance for entering the platform self.center = None # lon, lat of the station centre point + self.centers = {} # el_id -> (lon, lat) for all elements self.modes = station.modes self.name = station.name @@ -195,6 +198,9 @@ class StopArea: for i in range(2): self.center[i] /= len(self.stops_and_platforms) + for el in self.get_elements(): + self.centers[el] = el_center(city.elements[el]) + def get_elements(self): result = set([self.id, self.station.id]) result.update(self.entrances) @@ -240,9 +246,9 @@ class Route: self.mode = relation['tags']['route'] self.rails = [] self.stops = [] - # Add circle_line=yes on a route to disable station order checking + # Add circular=yes on a route to disable station order checking # This is a hack, but few lines actually have repeating stops - is_circle = relation['tags'].get('circle_line') == 'yes' + is_circle = relation['tags'].get('circular') == 'yes' enough_stops = False for m in relation['members']: k = el_id(m) @@ -321,6 +327,8 @@ class RouteMaster: def __init__(self, master=None): self.routes = [] self.best = None + self.id = el_id(master) + self.has_master = master is not None if master: self.ref = master['tags'].get('ref', master['tags'].get('name', None)) self.colour = master['tags'].get('colour', None) @@ -363,6 +371,9 @@ class RouteMaster: self.mode, route.mode), route.element) return + 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): self.best = route @@ -655,10 +666,9 @@ def find_transfers(elements, cities): for m in sag['members']: k = el_id(m) if k not in stations: - transfer = None - break + continue transfer.update(stations[k]) - if transfer: + if len(transfer) > 1: transfers.append(transfer) return transfers