From 3d2fb3fb46aa247ebe1d8912e31d43691f038838 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 8 Dec 2019 19:38:59 +0100 Subject: [PATCH 01/18] First version of Addis Ababa generator * no schedule * trips copied from gh_accra class --- osm2gtfs/creators/et_addisababa/__init__.py | 2 + osm2gtfs/creators/et_addisababa/config.json | 35 ++++++ .../routes_creator_et_addisababa.py | 11 ++ .../schedule_creator_et_addisababa.py | 10 ++ .../stops_creator_et_addisababa.py | 12 ++ .../trips_creator_et_addisababa.py | 112 ++++++++++++++++++ 6 files changed, 182 insertions(+) create mode 100644 osm2gtfs/creators/et_addisababa/__init__.py create mode 100644 osm2gtfs/creators/et_addisababa/config.json create mode 100644 osm2gtfs/creators/et_addisababa/routes_creator_et_addisababa.py create mode 100644 osm2gtfs/creators/et_addisababa/schedule_creator_et_addisababa.py create mode 100644 osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py create mode 100644 osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py diff --git a/osm2gtfs/creators/et_addisababa/__init__.py b/osm2gtfs/creators/et_addisababa/__init__.py new file mode 100644 index 00000000..5c9136ad --- /dev/null +++ b/osm2gtfs/creators/et_addisababa/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# coding=utf-8 diff --git a/osm2gtfs/creators/et_addisababa/config.json b/osm2gtfs/creators/et_addisababa/config.json new file mode 100644 index 00000000..d7df2813 --- /dev/null +++ b/osm2gtfs/creators/et_addisababa/config.json @@ -0,0 +1,35 @@ +{ + "query": { + "bbox": { + "n": "9.098851", + "s": "8.829752", + "e": "38.905763", + "w": "38.653249" + }, + "tags": { + "route": "bus" + } + }, + "stops": { + "name_without": "Add a name to the stop with JungleBus app", + "name_auto": "yes" + }, + "agency": { + "agency_id": "AM3", + "agency_name": "Accra Tro tro", + "agency_url": "https://ama.gov.gh/welcome/transport/", + "agency_timezone": "Africa/Addis_Ababa", + "agency_lang": "en", + "agency_phone": "", + "agency_fare_url": "" + }, + "feed_info": { + "publisher_name": "AddisMap", + "publisher_url": "http://junglebus.io", + "version": "0.1", + "start_date": "20190101", + "end_date": "20201231" + }, + "output_file": "data/et-addisababa.zip", + "selector": "et_addisababa" +} diff --git a/osm2gtfs/creators/et_addisababa/routes_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/routes_creator_et_addisababa.py new file mode 100644 index 00000000..e8c4cc3f --- /dev/null +++ b/osm2gtfs/creators/et_addisababa/routes_creator_et_addisababa.py @@ -0,0 +1,11 @@ +# coding=utf-8 + +import logging + +from osm2gtfs.core.elements import Line, Itinerary, Station, Stop +from osm2gtfs.creators.routes_creator import RoutesCreator + + +class RoutesCreatorEtAddisababa(RoutesCreator): + """Empty extend""" + diff --git a/osm2gtfs/creators/et_addisababa/schedule_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/schedule_creator_et_addisababa.py new file mode 100644 index 00000000..773bfb41 --- /dev/null +++ b/osm2gtfs/creators/et_addisababa/schedule_creator_et_addisababa.py @@ -0,0 +1,10 @@ +# coding=utf-8 + +from osm2gtfs.creators.schedule_creator import ScheduleCreator + + +class ScheduleCreatorEtAddisababa(ScheduleCreator): + + def add_schedule_to_data(self, data): + # Don't use any schedule source + data.schedule = None diff --git a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py new file mode 100644 index 00000000..b6ae2cc7 --- /dev/null +++ b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py @@ -0,0 +1,12 @@ +# coding=utf-8 + +from osm2gtfs.creators.stops_creator import StopsCreator + + +class StopsCreatorEtAddisababa(StopsCreator): + + # Override construction of stop_id + def _define_stop_id(self, stop): + # Simply returns osm_id regardless of the osm_type as only map + # objects of type nodes are assumed. + return str(stop.osm_id) diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py new file mode 100644 index 00000000..96de0b35 --- /dev/null +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -0,0 +1,112 @@ +# coding=utf-8 + +import sys +import re +import logging +from datetime import timedelta, datetime +import transitfeed +from osm2gtfs.creators.trips_creator import TripsCreator +from osm2gtfs.core.helper import Helper +from osm2gtfs.core.elements import Line, Itinerary, Stop + +class TripsCreatorEtAddisababa(TripsCreator): + service_weekday = None + + def add_trips_to_feed(self, feed, data): + self.service_weekday = feed.GetDefaultServicePeriod() + self.service_weekday.SetStartDate( + self.config['feed_info']['start_date']) + self.service_weekday.SetEndDate(self.config['feed_info']['end_date']) + self.service_weekday.SetWeekdayService(True) + self.service_weekday.SetWeekendService(True) + + lines = data.routes + for route_ref, line in sorted(lines.iteritems()): + if not isinstance(line, Line): + continue + print("Generating schedule for line: " + route_ref) + + line_gtfs = feed.AddRoute( + short_name=str(line.route_id), + long_name=line.name, + # we change the route_long_name with the 'from' and 'to' tags + # of the last route as the route_master name tag contains + # the line code (route_short_name) + route_type="Bus", + route_id=line.osm_id) + line_gtfs.agency_id = feed.GetDefaultAgency().agency_id + line_gtfs.route_desc = "" + line_gtfs.route_color = "1779c2" + line_gtfs.route_text_color = "ffffff" + + route_index = 0 + itineraries = line.get_itineraries() + for a_route in itineraries: + trip_gtfs = line_gtfs.AddTrip(feed) + trip_gtfs.shape_id = self._add_shape_to_feed( + feed, a_route.osm_id, a_route) + trip_gtfs.direction_id = route_index % 2 + route_index += 1 + + if a_route.fr and a_route.to: + trip_gtfs.trip_headsign = a_route.to + line_gtfs.route_long_name = a_route.fr.decode( + 'utf8') + " ↔ ".decode( + 'utf8') + a_route.to.decode('utf8') + + DEFAULT_ROUTE_FREQUENCY = 30 + DEFAULT_TRAVEL_TIME = 120 + + frequency = None + if "frequency" in line.tags: + frequency = line.tags['frequency'] + try: + ROUTE_FREQUENCY = int(frequency) + if not ROUTE_FREQUENCY > 0: + print("frequency is invalid for route_master " + str( + line.osm_id)) + ROUTE_FREQUENCY = DEFAULT_ROUTE_FREQUENCY + except (ValueError, TypeError) as e: + print("frequency not a number for route_master " + str( + line.osm_id)) + ROUTE_FREQUENCY = DEFAULT_ROUTE_FREQUENCY + trip_gtfs.AddFrequency( + "05:00:00", "22:00:00", ROUTE_FREQUENCY * 60) + if 'travel_time' in a_route.tags: + try: + TRAVEL_TIME = int(a_route.tags['travel_time']) + if not TRAVEL_TIME > 0: + print("travel_time is invalid for route " + str( + a_route.osm_id)) + TRAVEL_TIME = DEFAULT_TRAVEL_TIME + except (ValueError, TypeError) as e: + print("travel_time not a number for route " + str( + a_route.osm_id)) + TRAVEL_TIME = DEFAULT_TRAVEL_TIME + else: + TRAVEL_TIME = DEFAULT_TRAVEL_TIME + + for index_stop, a_stop in enumerate(a_route.stops): + stop_id = a_stop.split('/')[-1] + departure_time = datetime(2008, 11, 22, 6, 0, 0) + + if index_stop == 0: + trip_gtfs.AddStopTime(feed.GetStop( + str(stop_id)), stop_time=departure_time.strftime( + "%H:%M:%S")) + elif index_stop == len(a_route.stops) - 1: + departure_time += timedelta(minutes=TRAVEL_TIME) + trip_gtfs.AddStopTime(feed.GetStop( + str(stop_id)), stop_time=departure_time.strftime( + "%H:%M:%S")) + else: + trip_gtfs.AddStopTime(feed.GetStop(str(stop_id))) + + for secs, stop_time, is_timepoint in trip_gtfs.GetTimeInterpolatedStops(): + if not is_timepoint: + stop_time.arrival_secs = secs + stop_time.departure_secs = secs + trip_gtfs.ReplaceStopTimeObject(stop_time) + + Helper.interpolate_stop_times(trip_gtfs) + From 0220d8db88942e3d456aec477adab10388ae70d1 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 8 Dec 2019 20:09:49 +0100 Subject: [PATCH 02/18] Add osm_type to the stop_id --- osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py | 3 ++- osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py index b6ae2cc7..f272a408 100644 --- a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py @@ -9,4 +9,5 @@ class StopsCreatorEtAddisababa(StopsCreator): def _define_stop_id(self, stop): # Simply returns osm_id regardless of the osm_type as only map # objects of type nodes are assumed. - return str(stop.osm_id) + stop_id = stop.osm_type + "/" + str(stop.osm_id) + return stop_id \ No newline at end of file diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index 96de0b35..97f2ebaf 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -87,7 +87,7 @@ def add_trips_to_feed(self, feed, data): TRAVEL_TIME = DEFAULT_TRAVEL_TIME for index_stop, a_stop in enumerate(a_route.stops): - stop_id = a_stop.split('/')[-1] + stop_id = a_stop departure_time = datetime(2008, 11, 22, 6, 0, 0) if index_stop == 0: From 68164d4c5d87ff44541093d6ac69ebbf1213bd0c Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 8 Dec 2019 20:33:49 +0100 Subject: [PATCH 03/18] Agency Info; extend bounds of Addis Ababa a bit --- osm2gtfs/creators/et_addisababa/config.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osm2gtfs/creators/et_addisababa/config.json b/osm2gtfs/creators/et_addisababa/config.json index d7df2813..9e9edb8a 100644 --- a/osm2gtfs/creators/et_addisababa/config.json +++ b/osm2gtfs/creators/et_addisababa/config.json @@ -1,23 +1,23 @@ { "query": { "bbox": { - "n": "9.098851", - "s": "8.829752", - "e": "38.905763", - "w": "38.653249" + "n": "9.13", + "s": "8.8", + "e": "38.96", + "w": "38.61" }, "tags": { "route": "bus" } }, "stops": { - "name_without": "Add a name to the stop with JungleBus app", + "name_without": "no name", "name_auto": "yes" }, "agency": { "agency_id": "AM3", - "agency_name": "Accra Tro tro", - "agency_url": "https://ama.gov.gh/welcome/transport/", + "agency_name": "Addis Ababa Public Transport", + "agency_url": "https://www.addismap.com", "agency_timezone": "Africa/Addis_Ababa", "agency_lang": "en", "agency_phone": "", @@ -25,7 +25,7 @@ }, "feed_info": { "publisher_name": "AddisMap", - "publisher_url": "http://junglebus.io", + "publisher_url": "https://addismap.com", "version": "0.1", "start_date": "20190101", "end_date": "20201231" From 8ad2de68d8727afe01fb43b37ca75c895bb5b4fa Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 8 Dec 2019 20:34:17 +0100 Subject: [PATCH 04/18] Mostly copy stop generator from Ghana --- .../stops_creator_et_addisababa.py | 58 ++++++++++++++++++- .../trips_creator_et_addisababa.py | 2 +- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py index f272a408..d4c4c7d5 100644 --- a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py @@ -1,7 +1,33 @@ # coding=utf-8 +from osm2gtfs.core.helper import Helper from osm2gtfs.creators.stops_creator import StopsCreator +def get_stop_id(stop): + return stop.osm_type + "/" + str(stop.osm_id) + +def create_stop_area(stop_data, feed): + stop_id = get_stop_id(stop_data) + gtfs_stop_area = feed.AddStop( + lat=float(stop_data.lat), + lng=float(stop_data.lon), + name=stop_data.name, + stop_id="SA" + str(stop_id) + ) + gtfs_stop_area.location_type = 1 + return gtfs_stop_area + +def create_stop_point(stop_data, feed): + stop_id = get_stop_id(stop_data) + gtfs_stop_point = feed.AddStop( + lat=float(stop_data.lat), + lng=float(stop_data.lon), + name=stop_data.name, + stop_id=str(stop_id) + ) + return gtfs_stop_point + + class StopsCreatorEtAddisababa(StopsCreator): @@ -9,5 +35,33 @@ class StopsCreatorEtAddisababa(StopsCreator): def _define_stop_id(self, stop): # Simply returns osm_id regardless of the osm_type as only map # objects of type nodes are assumed. - stop_id = stop.osm_type + "/" + str(stop.osm_id) - return stop_id \ No newline at end of file + return get_stop_id(stop) + + def add_stops_to_feed(self, feed, data): + stops = data.get_stops() + stops_by_name = {} + + for internal_stop_id, a_stop in stops['regular'].items(): + if a_stop.name not in stops_by_name: + stops_by_name[a_stop.name] = [] + stops_by_name[a_stop.name].append(a_stop) + + for a_stop_name in stops_by_name: + stop_areas = [] + + for a_stop_point in sorted(stops_by_name[a_stop_name], key=get_stop_id): + gtfs_stop_point = create_stop_point(a_stop_point, feed) + stop_point_has_parent_location = False + for a_stop_area in stop_areas: + distance_to_parent_station = Helper.get_crow_fly_distance( + (a_stop_area.stop_lat, a_stop_area.stop_lon), + (a_stop_point.lat, a_stop_point.lon) + ) + if distance_to_parent_station < 500: + gtfs_stop_point.parent_station = a_stop_area.stop_id + stop_point_has_parent_location = True + break + if not stop_point_has_parent_location: + new_sa = create_stop_area(a_stop_point, feed) + gtfs_stop_point.parent_station = new_sa.stop_id + stop_areas.append(new_sa) diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index 97f2ebaf..12493843 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -88,7 +88,7 @@ def add_trips_to_feed(self, feed, data): for index_stop, a_stop in enumerate(a_route.stops): stop_id = a_stop - departure_time = datetime(2008, 11, 22, 6, 0, 0) + departure_time = datetime(2019, 11, 22, 6, 0, 0) if index_stop == 0: trip_gtfs.AddStopTime(feed.GetStop( From 223fb80a52a6254e15903941a9fa875a8cbfa0d6 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 15 Dec 2019 11:32:50 +0100 Subject: [PATCH 05/18] Reset Addis Ababa Scripts (copy from gh_accra) --- osm2gtfs/creators/et_addisababa/config.json | 10 +++++----- .../routes_creator_et_addisababa.py | 10 ++++++---- .../et_addisababa/stops_creator_et_addisababa.py | 16 ++++++---------- .../et_addisababa/trips_creator_et_addisababa.py | 13 +++++-------- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/osm2gtfs/creators/et_addisababa/config.json b/osm2gtfs/creators/et_addisababa/config.json index 9e9edb8a..7f4658a9 100644 --- a/osm2gtfs/creators/et_addisababa/config.json +++ b/osm2gtfs/creators/et_addisababa/config.json @@ -15,9 +15,9 @@ "name_auto": "yes" }, "agency": { - "agency_id": "AM3", - "agency_name": "Addis Ababa Public Transport", - "agency_url": "https://www.addismap.com", + "agency_id": "AA", + "agency_name": "Addis Ababa Transport (all)", + "agency_url": "https://example.com", "agency_timezone": "Africa/Addis_Ababa", "agency_lang": "en", "agency_phone": "", @@ -25,10 +25,10 @@ }, "feed_info": { "publisher_name": "AddisMap", - "publisher_url": "https://addismap.com", + "publisher_url": "http://addismap.com", "version": "0.1", "start_date": "20190101", - "end_date": "20201231" + "end_date": "20991231" }, "output_file": "data/et-addisababa.zip", "selector": "et_addisababa" diff --git a/osm2gtfs/creators/et_addisababa/routes_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/routes_creator_et_addisababa.py index e8c4cc3f..83c88f85 100644 --- a/osm2gtfs/creators/et_addisababa/routes_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/routes_creator_et_addisababa.py @@ -1,11 +1,13 @@ # coding=utf-8 -import logging - -from osm2gtfs.core.elements import Line, Itinerary, Station, Stop from osm2gtfs.creators.routes_creator import RoutesCreator class RoutesCreatorEtAddisababa(RoutesCreator): - """Empty extend""" + def add_routes_to_feed(self, feed, data): + # Get routes information + data.get_routes() + + # GTFS routes are created in TripsCreator + return diff --git a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py index d4c4c7d5..ea12018d 100644 --- a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py @@ -3,11 +3,9 @@ from osm2gtfs.core.helper import Helper from osm2gtfs.creators.stops_creator import StopsCreator -def get_stop_id(stop): - return stop.osm_type + "/" + str(stop.osm_id) def create_stop_area(stop_data, feed): - stop_id = get_stop_id(stop_data) + stop_id = stop_data.osm_id gtfs_stop_area = feed.AddStop( lat=float(stop_data.lat), lng=float(stop_data.lon), @@ -17,8 +15,9 @@ def create_stop_area(stop_data, feed): gtfs_stop_area.location_type = 1 return gtfs_stop_area + def create_stop_point(stop_data, feed): - stop_id = get_stop_id(stop_data) + stop_id = stop_data.osm_id gtfs_stop_point = feed.AddStop( lat=float(stop_data.lat), lng=float(stop_data.lon), @@ -28,14 +27,11 @@ def create_stop_point(stop_data, feed): return gtfs_stop_point +def get_stop_id(stop): + return stop.osm_id -class StopsCreatorEtAddisababa(StopsCreator): - # Override construction of stop_id - def _define_stop_id(self, stop): - # Simply returns osm_id regardless of the osm_type as only map - # objects of type nodes are assumed. - return get_stop_id(stop) +class StopsCreatorEtAddisababa(StopsCreator): def add_stops_to_feed(self, feed, data): stops = data.get_stops() diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index 12493843..f03bee8a 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -1,13 +1,11 @@ # coding=utf-8 -import sys -import re -import logging from datetime import timedelta, datetime -import transitfeed + from osm2gtfs.creators.trips_creator import TripsCreator from osm2gtfs.core.helper import Helper -from osm2gtfs.core.elements import Line, Itinerary, Stop +from osm2gtfs.core.elements import Line + class TripsCreatorEtAddisababa(TripsCreator): service_weekday = None @@ -87,8 +85,8 @@ def add_trips_to_feed(self, feed, data): TRAVEL_TIME = DEFAULT_TRAVEL_TIME for index_stop, a_stop in enumerate(a_route.stops): - stop_id = a_stop - departure_time = datetime(2019, 11, 22, 6, 0, 0) + stop_id = a_stop.split('/')[-1] + departure_time = datetime(2008, 11, 22, 6, 0, 0) if index_stop == 0: trip_gtfs.AddStopTime(feed.GetStop( @@ -109,4 +107,3 @@ def add_trips_to_feed(self, feed, data): trip_gtfs.ReplaceStopTimeObject(stop_time) Helper.interpolate_stop_times(trip_gtfs) - From 7caa13531e21eda4cde538bc0f6e6b021122584a Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 15 Dec 2019 11:44:36 +0100 Subject: [PATCH 06/18] Add different config files for Anbessa / Sheger --- .../creators/et_addisababa/config_AB.json | 36 +++++++++++++++++++ .../creators/et_addisababa/config_SH.json | 36 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 osm2gtfs/creators/et_addisababa/config_AB.json create mode 100644 osm2gtfs/creators/et_addisababa/config_SH.json diff --git a/osm2gtfs/creators/et_addisababa/config_AB.json b/osm2gtfs/creators/et_addisababa/config_AB.json new file mode 100644 index 00000000..a1f7142a --- /dev/null +++ b/osm2gtfs/creators/et_addisababa/config_AB.json @@ -0,0 +1,36 @@ +{ + "query": { + "bbox": { + "n": "9.13", + "s": "8.8", + "e": "38.96", + "w": "38.61" + }, + "tags": { + "route": "bus", + "operator:short": "AB" + } + }, + "stops": { + "name_without": "no name", + "name_auto": "yes" + }, + "agency": { + "agency_id": "AB", + "agency_name": "Anbessa City Bus", + "agency_url": "https://example.com", + "agency_timezone": "Africa/Addis_Ababa", + "agency_lang": "en", + "agency_phone": "", + "agency_fare_url": "" + }, + "feed_info": { + "publisher_name": "AddisMap", + "publisher_url": "http://addismap.com", + "version": "0.1", + "start_date": "20190101", + "end_date": "20991231" + }, + "output_file": "data/et-addisababa-ab.zip", + "selector": "et_addisababa" +} diff --git a/osm2gtfs/creators/et_addisababa/config_SH.json b/osm2gtfs/creators/et_addisababa/config_SH.json new file mode 100644 index 00000000..b0e430a7 --- /dev/null +++ b/osm2gtfs/creators/et_addisababa/config_SH.json @@ -0,0 +1,36 @@ +{ + "query": { + "bbox": { + "n": "9.13", + "s": "8.8", + "e": "38.96", + "w": "38.61" + }, + "tags": { + "route": "bus", + "operator:short": "SH" + } + }, + "stops": { + "name_without": "no name", + "name_auto": "yes" + }, + "agency": { + "agency_id": "SH", + "agency_name": "Sheger City Bus", + "agency_url": "https://example.com", + "agency_timezone": "Africa/Addis_Ababa", + "agency_lang": "en", + "agency_phone": "", + "agency_fare_url": "" + }, + "feed_info": { + "publisher_name": "AddisMap", + "publisher_url": "http://addismap.com", + "version": "0.1", + "start_date": "20190101", + "end_date": "20991231" + }, + "output_file": "data/et-addisababa-sh.zip", + "selector": "et_addisababa" +} From e923038a83cb354a37bd1e60a3df0a8949228cfe Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Fri, 20 Dec 2019 11:44:49 +0100 Subject: [PATCH 07/18] Use "duration" tag for interpolation trip timings --- .vscode/launch.json | 16 ++++++++++++++++ .../et_addisababa/trips_creator_et_addisababa.py | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..4d227290 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: OSM2GTFS", + "type": "python", + "request": "launch", + "program": "/usr/local/bin/osm2gtfs", + "console": "integratedTerminal", + "args": ["-c", "osm2gtfs/creators/et_addisababa/config.json"] + } + ] +} \ No newline at end of file diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index f03bee8a..e9886be5 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -70,9 +70,10 @@ def add_trips_to_feed(self, feed, data): ROUTE_FREQUENCY = DEFAULT_ROUTE_FREQUENCY trip_gtfs.AddFrequency( "05:00:00", "22:00:00", ROUTE_FREQUENCY * 60) - if 'travel_time' in a_route.tags: + if 'duration' in a_route.tags: try: - TRAVEL_TIME = int(a_route.tags['travel_time']) + (hours, minutes, seconds) = a_route.tags['duration'].split(':') + TRAVEL_TIME = int(hours) * 60 + int(minutes); if not TRAVEL_TIME > 0: print("travel_time is invalid for route " + str( a_route.osm_id)) From f92bfc921a650df20b4732c7914f2cc859ad2fa2 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sat, 21 Dec 2019 13:41:44 +0100 Subject: [PATCH 08/18] Show warning if default travel time (duration) is used --- osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index e9886be5..2b53eea0 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -79,11 +79,12 @@ def add_trips_to_feed(self, feed, data): a_route.osm_id)) TRAVEL_TIME = DEFAULT_TRAVEL_TIME except (ValueError, TypeError) as e: - print("travel_time not a number for route " + str( + print("travel_time not a number / exception thrown for route with OSM ID " + str( a_route.osm_id)) TRAVEL_TIME = DEFAULT_TRAVEL_TIME else: TRAVEL_TIME = DEFAULT_TRAVEL_TIME + print("WARNING: No duration set --- Using default travel time for route with OSM ID " +str(a_route.osm_id)); for index_stop, a_stop in enumerate(a_route.stops): stop_id = a_stop.split('/')[-1] From 090e03db968d24e8c2a4472e0864da7234f3a4e2 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sat, 21 Dec 2019 13:56:33 +0100 Subject: [PATCH 09/18] ignore .vscode --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f602a4b6..33d9990c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ Pipfile.lock __pycache__ .cache +.vscode +.vscode From 5909a8aa704c321fb24e65b7c0fd40358422e385 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sat, 21 Dec 2019 13:57:11 +0100 Subject: [PATCH 10/18] Generate frequencies.txt from inverval tags in routes --- .../trips_creator_et_addisababa.py | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index 2b53eea0..b76bb180 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -7,6 +7,10 @@ from osm2gtfs.core.elements import Line +def time_string_to_minutes(time_string): + (hours, minutes, seconds) = time_string.split(':') + return int(hours) * 60 + int(minutes) + class TripsCreatorEtAddisababa(TripsCreator): service_weekday = None @@ -56,24 +60,26 @@ def add_trips_to_feed(self, feed, data): DEFAULT_TRAVEL_TIME = 120 frequency = None - if "frequency" in line.tags: - frequency = line.tags['frequency'] - try: - ROUTE_FREQUENCY = int(frequency) - if not ROUTE_FREQUENCY > 0: - print("frequency is invalid for route_master " + str( - line.osm_id)) - ROUTE_FREQUENCY = DEFAULT_ROUTE_FREQUENCY - except (ValueError, TypeError) as e: - print("frequency not a number for route_master " + str( - line.osm_id)) - ROUTE_FREQUENCY = DEFAULT_ROUTE_FREQUENCY + + ROUTE_FREQUENCY = DEFAULT_ROUTE_FREQUENCY + + if "interval" in a_route.tags: + frequency = a_route.tags['interval'] + try: + ROUTE_FREQUENCY = time_string_to_minutes(frequency) + if not ROUTE_FREQUENCY > 0: + print("frequency is invalid for route_master " + str( + line.osm_id)) + except (ValueError, TypeError) as e: + print("frequency not a number for route_master " + str( + line.osm_id)) + trip_gtfs.AddFrequency( "05:00:00", "22:00:00", ROUTE_FREQUENCY * 60) + if 'duration' in a_route.tags: try: - (hours, minutes, seconds) = a_route.tags['duration'].split(':') - TRAVEL_TIME = int(hours) * 60 + int(minutes); + TRAVEL_TIME = time_string_to_minutes(a_route.tags['duration']); if not TRAVEL_TIME > 0: print("travel_time is invalid for route " + str( a_route.osm_id)) From 1dd9d17e3215e830049b3ff13412d0e81bcc21ac Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 29 Dec 2019 15:12:42 +0100 Subject: [PATCH 11/18] Add light rail to the feed --- osm2gtfs/creators/et_addisababa/config.json | 6 +++--- .../et_addisababa/trips_creator_et_addisababa.py | 13 ++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/osm2gtfs/creators/et_addisababa/config.json b/osm2gtfs/creators/et_addisababa/config.json index 7f4658a9..02f9a947 100644 --- a/osm2gtfs/creators/et_addisababa/config.json +++ b/osm2gtfs/creators/et_addisababa/config.json @@ -7,7 +7,7 @@ "w": "38.61" }, "tags": { - "route": "bus" + "route": ["bus", "light_rail"] } }, "stops": { @@ -27,8 +27,8 @@ "publisher_name": "AddisMap", "publisher_url": "http://addismap.com", "version": "0.1", - "start_date": "20190101", - "end_date": "20991231" + "start_date": "20191201", + "end_date": "20201101" }, "output_file": "data/et-addisababa.zip", "selector": "et_addisababa" diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index b76bb180..c71bc9c9 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -28,13 +28,20 @@ def add_trips_to_feed(self, feed, data): continue print("Generating schedule for line: " + route_ref) + if 'route_master' in line.tags and line.tags['route_master'] == "light_rail": + route_type = "Tram" + route_suffix = " (Light Rail)" + else: + route_type = "Bus" + route_suffix = "" + line_gtfs = feed.AddRoute( short_name=str(line.route_id), - long_name=line.name, + long_name=line.name + route_suffix, # we change the route_long_name with the 'from' and 'to' tags # of the last route as the route_master name tag contains # the line code (route_short_name) - route_type="Bus", + route_type=route_type, route_id=line.osm_id) line_gtfs.agency_id = feed.GetDefaultAgency().agency_id line_gtfs.route_desc = "" @@ -54,7 +61,7 @@ def add_trips_to_feed(self, feed, data): trip_gtfs.trip_headsign = a_route.to line_gtfs.route_long_name = a_route.fr.decode( 'utf8') + " ↔ ".decode( - 'utf8') + a_route.to.decode('utf8') + 'utf8') + a_route.to.decode('utf8') + route_suffix DEFAULT_ROUTE_FREQUENCY = 30 DEFAULT_TRAVEL_TIME = 120 From 54b5806bf4a784bcc9eecbb6c61d49c1c050333a Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 29 Dec 2019 15:43:53 +0100 Subject: [PATCH 12/18] Remove custom stops creator / use standard stop IDs * stop IDs of the format OSM_TYPE/ID are used now * Stations are no longer grouper together (stop hierachies are only to be used for big bus terminals and trams, see https://support.google.com/transitpartners/answer/6377423?hl=en) --- .../stops_creator_et_addisababa.py | 61 +------------------ .../trips_creator_et_addisababa.py | 2 +- 2 files changed, 4 insertions(+), 59 deletions(-) diff --git a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py index ea12018d..05aa6630 100644 --- a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py @@ -1,63 +1,8 @@ # coding=utf-8 -from osm2gtfs.core.helper import Helper from osm2gtfs.creators.stops_creator import StopsCreator - -def create_stop_area(stop_data, feed): - stop_id = stop_data.osm_id - gtfs_stop_area = feed.AddStop( - lat=float(stop_data.lat), - lng=float(stop_data.lon), - name=stop_data.name, - stop_id="SA" + str(stop_id) - ) - gtfs_stop_area.location_type = 1 - return gtfs_stop_area - - -def create_stop_point(stop_data, feed): - stop_id = stop_data.osm_id - gtfs_stop_point = feed.AddStop( - lat=float(stop_data.lat), - lng=float(stop_data.lon), - name=stop_data.name, - stop_id=str(stop_id) - ) - return gtfs_stop_point - - -def get_stop_id(stop): - return stop.osm_id - - class StopsCreatorEtAddisababa(StopsCreator): - - def add_stops_to_feed(self, feed, data): - stops = data.get_stops() - stops_by_name = {} - - for internal_stop_id, a_stop in stops['regular'].items(): - if a_stop.name not in stops_by_name: - stops_by_name[a_stop.name] = [] - stops_by_name[a_stop.name].append(a_stop) - - for a_stop_name in stops_by_name: - stop_areas = [] - - for a_stop_point in sorted(stops_by_name[a_stop_name], key=get_stop_id): - gtfs_stop_point = create_stop_point(a_stop_point, feed) - stop_point_has_parent_location = False - for a_stop_area in stop_areas: - distance_to_parent_station = Helper.get_crow_fly_distance( - (a_stop_area.stop_lat, a_stop_area.stop_lon), - (a_stop_point.lat, a_stop_point.lon) - ) - if distance_to_parent_station < 500: - gtfs_stop_point.parent_station = a_stop_area.stop_id - stop_point_has_parent_location = True - break - if not stop_point_has_parent_location: - new_sa = create_stop_area(a_stop_point, feed) - gtfs_stop_point.parent_station = new_sa.stop_id - stop_areas.append(new_sa) + """ + nothing overwritten + """ diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index c71bc9c9..c3eae379 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -100,7 +100,7 @@ def add_trips_to_feed(self, feed, data): print("WARNING: No duration set --- Using default travel time for route with OSM ID " +str(a_route.osm_id)); for index_stop, a_stop in enumerate(a_route.stops): - stop_id = a_stop.split('/')[-1] + stop_id = a_stop departure_time = datetime(2008, 11, 22, 6, 0, 0) if index_stop == 0: From 3eefdcd8b53e7064e0e99e57d08a1db705e2af53 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 12 Jan 2020 13:37:18 +0100 Subject: [PATCH 13/18] Add share taxi (minibus) to known route types --- osm2gtfs/core/elements.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osm2gtfs/core/elements.py b/osm2gtfs/core/elements.py index 3c220e57..d938e763 100644 --- a/osm2gtfs/core/elements.py +++ b/osm2gtfs/core/elements.py @@ -71,7 +71,8 @@ def __attrs_post_init__(self): 'train': 'Rail', 'bus': 'Bus', 'trolleybus': 'Bus', - 'ferry': 'Ferry' + 'ferry': 'Ferry', + 'share_taxi': 'Share Taxi' } if self.route_type not in known_route_types: From 8f86d8211fe470727a7c52ad9f2caee4b5e92770 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 12 Jan 2020 13:37:45 +0100 Subject: [PATCH 14/18] Rename Light rail from Tram in known types --- osm2gtfs/core/elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osm2gtfs/core/elements.py b/osm2gtfs/core/elements.py index d938e763..ab324180 100644 --- a/osm2gtfs/core/elements.py +++ b/osm2gtfs/core/elements.py @@ -66,7 +66,7 @@ def __attrs_post_init__(self): known_route_types = { 'tram': 'Tram', - 'light_rail': 'Tram', + 'light_rail': 'Light Rail', 'subway': 'Subway', 'train': 'Rail', 'bus': 'Bus', From 82818708c9f43fbb2ab6cd98c87819f105333892 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sat, 1 Feb 2020 12:22:23 +0100 Subject: [PATCH 15/18] WIP: Flex support --- osm2gtfs/creators/et_addisababa/config.json | 2 +- .../trips_creator_et_addisababa.py | 15 ++++++++-- osm2gtfs/osm2gtfs.py | 17 +++++++++-- transitfeedflex/README | 30 +++++++++++++++++++ transitfeedflex/__init__.py | 6 ++++ transitfeedflex/flexschedule.py | 11 +++++++ transitfeedflex/flexstoptime.py | 27 +++++++++++++++++ transitfeedflex/setup_extension.py | 14 +++++++++ 8 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 transitfeedflex/README create mode 100644 transitfeedflex/__init__.py create mode 100644 transitfeedflex/flexschedule.py create mode 100644 transitfeedflex/flexstoptime.py create mode 100644 transitfeedflex/setup_extension.py diff --git a/osm2gtfs/creators/et_addisababa/config.json b/osm2gtfs/creators/et_addisababa/config.json index 02f9a947..1e7d993c 100644 --- a/osm2gtfs/creators/et_addisababa/config.json +++ b/osm2gtfs/creators/et_addisababa/config.json @@ -7,7 +7,7 @@ "w": "38.61" }, "tags": { - "route": ["bus", "light_rail"] + "route": ["bus", "light_rail", "share_taxi"] } }, "stops": { diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index c3eae379..39d98b38 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -6,7 +6,9 @@ from osm2gtfs.core.helper import Helper from osm2gtfs.core.elements import Line +from transitfeed.trip import Trip + def time_string_to_minutes(time_string): (hours, minutes, seconds) = time_string.split(':') return int(hours) * 60 + int(minutes) @@ -28,9 +30,14 @@ def add_trips_to_feed(self, feed, data): continue print("Generating schedule for line: " + route_ref) + is_share_taxi = True if 'route_master' in line.tags and line.tags['route_master'] == "light_rail": route_type = "Tram" route_suffix = " (Light Rail)" + elif 'route_master' in line.tags and line.tags['route_master'] == "share_taxi": + route_type = "Bus" + route_suffix = " (Minibus)" + is_share_taxi = True else: route_type = "Bus" route_suffix = "" @@ -106,16 +113,18 @@ def add_trips_to_feed(self, feed, data): if index_stop == 0: trip_gtfs.AddStopTime(feed.GetStop( str(stop_id)), stop_time=departure_time.strftime( - "%H:%M:%S")) + "%H:%M:%S"), continuous_pickup = 0, continuous_drop_off = 0) elif index_stop == len(a_route.stops) - 1: departure_time += timedelta(minutes=TRAVEL_TIME) trip_gtfs.AddStopTime(feed.GetStop( str(stop_id)), stop_time=departure_time.strftime( - "%H:%M:%S")) + "%H:%M:%S"), continuous_pickup = 0, continuous_drop_off = 0) else: - trip_gtfs.AddStopTime(feed.GetStop(str(stop_id))) + trip_gtfs.AddStopTime(feed.GetStop(str(stop_id)), continuous_pickup = 0, continuous_drop_off = 0) for secs, stop_time, is_timepoint in trip_gtfs.GetTimeInterpolatedStops(): + stop_time.continuous_pickup_flag = 0 + stop_time.continuous_drop_off_flag = 0 if not is_timepoint: stop_time.arrival_secs = secs stop_time.departure_secs = secs diff --git a/osm2gtfs/osm2gtfs.py b/osm2gtfs/osm2gtfs.py index c92a2f8f..e01e57cb 100644 --- a/osm2gtfs/osm2gtfs.py +++ b/osm2gtfs/osm2gtfs.py @@ -5,11 +5,15 @@ import sys import logging import argparse +import transitfeedflex import transitfeed from core.configuration import Configuration from core.osm_connector import OsmConnector from core.creator_factory import CreatorFactory +from transitfeed.trip import Trip +from transitfeed.gtfsfactoryuser import GtfsFactoryUser + # Define logging level logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) @@ -40,7 +44,6 @@ def main(): - # Load, prepare and validate configuration config = Configuration(args) @@ -62,8 +65,18 @@ def main(): data.get_stops(refresh=True) config.get_schedule_source(refresh=True) + gtfs_factory = transitfeedflex.GetGtfsFactory() + + def GetGtfsFactory(self): + return gtfs_factory; + + # Monkey patch all the base classes + GtfsFactoryUser.GetGtfsFactory = GetGtfsFactory + # Define (transitfeed) object for GTFS creation - feed = transitfeed.Schedule() + feed = transitfeedflex.FlexSchedule(gtfs_factory=gtfs_factory) + + # Initiate creators for GTFS components through an object factory factory = CreatorFactory(config) diff --git a/transitfeedflex/README b/transitfeedflex/README new file mode 100644 index 00000000..2074d4be --- /dev/null +++ b/transitfeedflex/README @@ -0,0 +1,30 @@ +README file for the googletransit extension + + +This extension modifies the transitfeed package to adapt it to the requirements +and features specific to Google Transit. + +List of changes to the default behavior: +* extensions.googletransit.Agency: + - extension of transitfeed.Agency + - adding pybcp47 module for supporting BCP-47 language code validation +* extensions.googletransit.FareAttribute: + - extension of transitfeed.FareAttribute as in the open proposal + "add agency_id column to fare_attributes.txt" at + http://groups.google.com/group/gtfs-changes/browse_frm/thread/4e74c23bb1f80480 + - adds new field 'agency_id' and function ValidateAgencyId() +* extensions.googletransit.Route + - extension of transitfeed.Route as proposed in + https://sites.google.com/site/gtfschanges/spec-changes-summary#routes and + https://sites.google.com/site/gtfschanges/open-proposals + - adding field 'co2_per_km' and ValidateCo2PerKm() function + - adding HVT (Hierarchical Vehicle Type) route types, also used in + extensions.googletransit.Stop for validating field vehicle_type. + The HVT values are derived from the European TPEG standard, see discussion + at http://groups.google.com/group/gtfs-changes/msg/ed917a69cf8c5bef +* extensions.googletransit.Stop + - extension of transitfeed.Stop as proposed in + https://sites.google.com/site/gtfschanges/spec-changes-summary#stops + - adding and validating fields 'stop_timezone', 'vehicle_type' and + 'wheelchair_boarding' + - overriding ValidateStopLocationType(), adding location_type 2 (entrance) diff --git a/transitfeedflex/__init__.py b/transitfeedflex/__init__.py new file mode 100644 index 00000000..7f5ba959 --- /dev/null +++ b/transitfeedflex/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/python2.5 + +from __future__ import absolute_import +from .flexstoptime import * +from .flexschedule import * +from .setup_extension import * diff --git a/transitfeedflex/flexschedule.py b/transitfeedflex/flexschedule.py new file mode 100644 index 00000000..1b0097b5 --- /dev/null +++ b/transitfeedflex/flexschedule.py @@ -0,0 +1,11 @@ +from transitfeed.schedule import Schedule + +class FlexSchedule(Schedule): + + def ConnectDb(self, memory_db): + super(FlexSchedule, self).ConnectDb(memory_db) + cursor = self._connection.cursor() + cursor.execute("""ALTER TABLE stop_times ADD continuous_pickup INTEGER;""") + cursor.execute("""ALTER TABLE stop_times ADD continuous_drop_off INTEGER;""") + + diff --git a/transitfeedflex/flexstoptime.py b/transitfeedflex/flexstoptime.py new file mode 100644 index 00000000..aecabc22 --- /dev/null +++ b/transitfeedflex/flexstoptime.py @@ -0,0 +1,27 @@ +from transitfeed.stoptime import StopTime + +class FlexStopTime(StopTime): + + _OPTIONAL_FIELD_NAMES = StopTime._OPTIONAL_FIELD_NAMES + [ 'continuous_pickup', 'continuous_drop_off'] + + _FIELD_NAMES = StopTime._REQUIRED_FIELD_NAMES + _OPTIONAL_FIELD_NAMES + + _SQL_FIELD_NAMES = StopTime._SQL_FIELD_NAMES + [ 'continuous_pickup', 'continuous_drop_off'] + + __slots__ = StopTime.__slots__ + ('continuous_pickup_flag', 'continuous_drop_off_flag') + + def __init__(self, problems, stop, arrival_time=None, departure_time=None, stop_headsign=None, pickup_type=None, drop_off_type=None, shape_dist_traveled=None, arrival_secs=None, departure_secs=None, stop_time=None, stop_sequence=None, timepoint=None, continuous_pickup=None, continuous_drop_off=None): + super(FlexStopTime, self).__init__(problems, stop, arrival_time=arrival_time, departure_time=departure_time, stop_headsign=stop_headsign, pickup_type=pickup_type, drop_off_type=drop_off_type, shape_dist_traveled=shape_dist_traveled, arrival_secs=arrival_secs, departure_secs=departure_secs, stop_time=stop_time, stop_sequence=stop_sequence, timepoint=timepoint) + self.continuous_pickup_flag = continuous_pickup + self.continuous_drop_off_flag = continuous_drop_off + + def __getattr__(self, name): + if name == 'continuous_pickup': + if self.continuous_drop_off_flag == None: + return '' + return str(self.continuous_pickup_flag) # force 0 to be exported, because default = 1 = blank + elif name == 'continuous_drop_off': + if self.continuous_drop_off_flag == None: + return '' + return str(self.continuous_drop_off_flag) + return super(FlexStopTime, self).__getattr__(name) diff --git a/transitfeedflex/setup_extension.py b/transitfeedflex/setup_extension.py new file mode 100644 index 00000000..431f58ad --- /dev/null +++ b/transitfeedflex/setup_extension.py @@ -0,0 +1,14 @@ +#!/usr/bin/python2 + +from __future__ import absolute_import +import transitfeed + +from . import flexstoptime + +def GetGtfsFactory(factory = None): + if not factory: + factory = transitfeed.GetGtfsFactory() + + factory.UpdateClass('StopTime', flexstoptime.FlexStopTime) + + return factory From b4bf0b960aa3667b4af9689e42dbf04371b99e74 Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Sun, 2 Feb 2020 15:51:06 +0100 Subject: [PATCH 16/18] Finalize Flex-Routes (for Minibus -> set flex_flag for continous drop of and pick up) --- .../trips_creator_et_addisababa.py | 14 +++---- transitfeedflex/README | 31 +--------------- transitfeedflex/flextrip.py | 37 +++++++++++++++++++ transitfeedflex/setup_extension.py | 2 + 4 files changed, 47 insertions(+), 37 deletions(-) create mode 100644 transitfeedflex/flextrip.py diff --git a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py index 39d98b38..82b920c6 100644 --- a/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/trips_creator_et_addisababa.py @@ -30,14 +30,14 @@ def add_trips_to_feed(self, feed, data): continue print("Generating schedule for line: " + route_ref) - is_share_taxi = True + flex_flag = None if 'route_master' in line.tags and line.tags['route_master'] == "light_rail": route_type = "Tram" route_suffix = " (Light Rail)" elif 'route_master' in line.tags and line.tags['route_master'] == "share_taxi": route_type = "Bus" route_suffix = " (Minibus)" - is_share_taxi = True + flex_flag = 0 else: route_type = "Bus" route_suffix = "" @@ -113,18 +113,18 @@ def add_trips_to_feed(self, feed, data): if index_stop == 0: trip_gtfs.AddStopTime(feed.GetStop( str(stop_id)), stop_time=departure_time.strftime( - "%H:%M:%S"), continuous_pickup = 0, continuous_drop_off = 0) + "%H:%M:%S"), continuous_pickup = flex_flag, continuous_drop_off = flex_flag) elif index_stop == len(a_route.stops) - 1: departure_time += timedelta(minutes=TRAVEL_TIME) trip_gtfs.AddStopTime(feed.GetStop( str(stop_id)), stop_time=departure_time.strftime( - "%H:%M:%S"), continuous_pickup = 0, continuous_drop_off = 0) + "%H:%M:%S"), continuous_pickup = flex_flag, continuous_drop_off = flex_flag) else: - trip_gtfs.AddStopTime(feed.GetStop(str(stop_id)), continuous_pickup = 0, continuous_drop_off = 0) + trip_gtfs.AddStopTime(feed.GetStop(str(stop_id)), continuous_pickup = flex_flag, continuous_drop_off = flex_flag) for secs, stop_time, is_timepoint in trip_gtfs.GetTimeInterpolatedStops(): - stop_time.continuous_pickup_flag = 0 - stop_time.continuous_drop_off_flag = 0 + stop_time.continuous_pickup_flag = flex_flag + stop_time.continuous_drop_off_flag = flex_flag if not is_timepoint: stop_time.arrival_secs = secs stop_time.departure_secs = secs diff --git a/transitfeedflex/README b/transitfeedflex/README index 2074d4be..f3fcc69f 100644 --- a/transitfeedflex/README +++ b/transitfeedflex/README @@ -1,30 +1 @@ -README file for the googletransit extension - - -This extension modifies the transitfeed package to adapt it to the requirements -and features specific to Google Transit. - -List of changes to the default behavior: -* extensions.googletransit.Agency: - - extension of transitfeed.Agency - - adding pybcp47 module for supporting BCP-47 language code validation -* extensions.googletransit.FareAttribute: - - extension of transitfeed.FareAttribute as in the open proposal - "add agency_id column to fare_attributes.txt" at - http://groups.google.com/group/gtfs-changes/browse_frm/thread/4e74c23bb1f80480 - - adds new field 'agency_id' and function ValidateAgencyId() -* extensions.googletransit.Route - - extension of transitfeed.Route as proposed in - https://sites.google.com/site/gtfschanges/spec-changes-summary#routes and - https://sites.google.com/site/gtfschanges/open-proposals - - adding field 'co2_per_km' and ValidateCo2PerKm() function - - adding HVT (Hierarchical Vehicle Type) route types, also used in - extensions.googletransit.Stop for validating field vehicle_type. - The HVT values are derived from the European TPEG standard, see discussion - at http://groups.google.com/group/gtfs-changes/msg/ed917a69cf8c5bef -* extensions.googletransit.Stop - - extension of transitfeed.Stop as proposed in - https://sites.google.com/site/gtfschanges/spec-changes-summary#stops - - adding and validating fields 'stop_timezone', 'vehicle_type' and - 'wheelchair_boarding' - - overriding ValidateStopLocationType(), adding location_type 2 (entrance) +Adds Flexible Pick Off / Drop Off (Part of the GTFSFlex specification) \ No newline at end of file diff --git a/transitfeedflex/flextrip.py b/transitfeedflex/flextrip.py new file mode 100644 index 00000000..f4cbdc54 --- /dev/null +++ b/transitfeedflex/flextrip.py @@ -0,0 +1,37 @@ +from transitfeed.trip import Trip + +import transitfeed.problems as problems_module + +class FlexTrip(Trip): + def GetStopTimes(self, problems=None): + """Adds continuous_pickup + continuous_drop_off to fetching script""" + # In theory problems=None should be safe because data from database has been + # validated. See comment in _LoadStopTimes for why this isn't always true. + cursor = self._schedule._connection.cursor() + cursor.execute( + 'SELECT arrival_secs,departure_secs,stop_headsign,pickup_type,' + 'drop_off_type,shape_dist_traveled,stop_id,stop_sequence,timepoint, continuous_pickup, continuous_drop_off ' + 'FROM stop_times ' + 'WHERE trip_id=? ' + 'ORDER BY stop_sequence', (self.trip_id,)) + stop_times = [] + stoptime_class = self.GetGtfsFactory().StopTime + if problems is None: + # TODO: delete this branch when StopTime.__init__ doesn't need a + # ProblemReporter + problems = problems_module.default_problem_reporter + for row in cursor.fetchall(): + stop = self._schedule.GetStop(row[6]) + stop_times.append(stoptime_class(problems=problems, + stop=stop, + arrival_secs=row[0], + departure_secs=row[1], + stop_headsign=row[2], + pickup_type=row[3], + drop_off_type=row[4], + shape_dist_traveled=row[5], + stop_sequence=row[7], + timepoint=row[8], + continuous_pickup=row[9], + continuous_drop_off=row[10])) + return stop_times diff --git a/transitfeedflex/setup_extension.py b/transitfeedflex/setup_extension.py index 431f58ad..ba8d068a 100644 --- a/transitfeedflex/setup_extension.py +++ b/transitfeedflex/setup_extension.py @@ -4,11 +4,13 @@ import transitfeed from . import flexstoptime +from . import flextrip def GetGtfsFactory(factory = None): if not factory: factory = transitfeed.GetGtfsFactory() factory.UpdateClass('StopTime', flexstoptime.FlexStopTime) + factory.UpdateClass('Trip', flextrip.FlexTrip) return factory From 56a071ca7a795337a634cc67b69b373b1a910c8a Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Mon, 3 Apr 2023 12:50:13 +0200 Subject: [PATCH 17/18] Add minibus data, change expiry data --- .vscode/launch.json | 16 ---------------- osm2gtfs/creators/et_addisababa/config.json | 4 ++-- 2 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 4d227290..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Python: OSM2GTFS", - "type": "python", - "request": "launch", - "program": "/usr/local/bin/osm2gtfs", - "console": "integratedTerminal", - "args": ["-c", "osm2gtfs/creators/et_addisababa/config.json"] - } - ] -} \ No newline at end of file diff --git a/osm2gtfs/creators/et_addisababa/config.json b/osm2gtfs/creators/et_addisababa/config.json index 02f9a947..374460e8 100644 --- a/osm2gtfs/creators/et_addisababa/config.json +++ b/osm2gtfs/creators/et_addisababa/config.json @@ -7,7 +7,7 @@ "w": "38.61" }, "tags": { - "route": ["bus", "light_rail"] + "route": ["bus", "light_rail", "share_taxi"] } }, "stops": { @@ -28,7 +28,7 @@ "publisher_url": "http://addismap.com", "version": "0.1", "start_date": "20191201", - "end_date": "20201101" + "end_date": "20991231" }, "output_file": "data/et-addisababa.zip", "selector": "et_addisababa" From c092176909add22e8260b01391f6e58356dff73d Mon Sep 17 00:00:00 2001 From: Alexander Menk Date: Tue, 27 Jun 2023 22:43:10 +0200 Subject: [PATCH 18/18] Always use nodes as ID in Addis Ababa --- .../et_addisababa/stops_creator_et_addisababa.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py index 05aa6630..3021905d 100644 --- a/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py +++ b/osm2gtfs/creators/et_addisababa/stops_creator_et_addisababa.py @@ -3,6 +3,11 @@ from osm2gtfs.creators.stops_creator import StopsCreator class StopsCreatorEtAddisababa(StopsCreator): - """ - nothing overwritten - """ + def _define_stop_id(self, stop): + """ + We always use the node ID in Addis Ababa because refs currently might contain duplicates + """ + + stop_id = stop.osm_type + "/" + str(stop.osm_id) + + return stop_id \ No newline at end of file