class FeedInfo(Base): """Information about the feed""" feed = models.ForeignKey('Feed') publisher_name = models.CharField( max_length=255, help_text="Full name of organization that publishes the feed.") publisher_url = models.URLField( help_text="URL of the feed publisher's organization.") lang = models.CharField( max_length=20, help_text="IETF BCP 47 language code for text in field.") start_date = models.DateField( null=True, blank=True, help_text="Date that feed starts providing reliable data.") end_date = models.DateField( null=True, blank=True, help_text="Date that feed stops providing reliable data.") version = models.CharField(max_length=255, blank=True, help_text="Version of feed.") def __unicode__(self): return u'%s-%s' % (self.feed.id, self.publisher_name) class Meta: db_table = 'multigtfs_feedinfo' app_label = 'multigtfs' verbose_name_plural = "feed info" _column_map = (('feed_publisher_name', 'publisher_name'), ('feed_publisher_url', 'publisher_url'), ('feed_lang', 'lang'), ('feed_start_date', 'start_date'), ('feed_end_date', 'end_date'), ('feed_version', 'version'))
class Agency(Base): """One or more transit agencies that provide the data in this feed. Maps to agency.txt in the GTFS feed. """ feed = models.ForeignKey('Feed') agency_id = models.CharField( max_length=255, blank=True, db_index=True, help_text="Unique identifier for transit agency") name = models.CharField( max_length=255, help_text="Full name of the transit agency") url = models.URLField( blank=True, help_text="URL of the transit agency") timezone = models.CharField( max_length=255, help_text="Timezone of the agency") lang = models.CharField( max_length=2, blank=True, help_text="ISO 639-1 code for the primary language") phone = models.CharField( max_length=255, blank=True, help_text="Voice telephone number") fare_url = models.URLField( blank=True, help_text="URL for purchasing tickets online") extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return u"%d-%s" % (self.feed.id, self.agency_id) class Meta: db_table = 'agency' app_label = 'multigtfs' verbose_name_plural = "agencies" # GTFS column names to fields, used by Base for import/export _column_map = ( ('agency_id', 'agency_id'), ('agency_name', 'name'), ('agency_url', 'url'), ('agency_timezone', 'timezone'), ('agency_lang', 'lang'), ('agency_phone', 'phone'), ('agency_fare_url', 'fare_url') ) _filename = 'agency.txt' _unique_fields = ('agency_id',)
class Route(Base): """A transit route""" feed = models.ForeignKey('Feed') route_id = models.CharField( max_length=255, db_index=True, help_text="Unique identifier for route.") agency = models.ForeignKey( 'Agency', null=True, blank=True, help_text="Agency for this route.") short_name = models.CharField( max_length=10, help_text="Short name of the route") long_name = models.CharField( max_length=255, help_text="Long name of the route") desc = models.TextField( blank=True, help_text="Long description of a route") rtype = models.IntegerField( choices=((0, 'Tram, Streetcar, or Light rail'), (1, 'Subway or Metro'), (2, 'Rail'), (3, 'Bus'), (4, 'Ferry'), (5, 'Cable car'), (6, 'Gondola or Suspended cable car'), (7, 'Funicular')), help_text='Type of transportation used on route') url = models.URLField( blank=True, help_text="Web page about for the route") color = models.CharField( max_length=6, blank=True, help_text="Color of route in hex") text_color = models.CharField( max_length=6, blank=True, help_text="Color of route text in hex") def __unicode__(self): return u"%d-%s" % (self.feed.id, self.route_id) class Meta: db_table = 'route' app_label = 'multigtfs' _column_map = ( ('route_id', 'route_id'), ('agency_id', 'agency__agency_id'), ('route_short_name', 'short_name'), ('route_long_name', 'long_name'), ('route_desc', 'desc'), ('route_type', 'rtype'), ('route_url', 'url'), ('route_color', 'color'), ('route_text_color', 'text_color') ) _sort_order = ('route_id', 'short_name')
class FeedInfo(Base): """Information about the feed Implements feed_info.txt in the GTFS feed. """ feed = models.ForeignKey('Feed', on_delete=models.CASCADE) publisher_name = models.CharField( max_length=255, help_text="Full name of organization that publishes the feed.") publisher_url = models.URLField( help_text="URL of the feed publisher's organization.") lang = models.CharField( "language", max_length=20, help_text="IETF BCP 47 language code for text in field.") start_date = models.DateField( null=True, blank=True, help_text="Date that feed starts providing reliable data.") end_date = models.DateField( null=True, blank=True, help_text="Date that feed stops providing reliable data.") version = models.CharField(max_length=255, blank=True, help_text="Version of feed.") extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return '%s-%s' % (self.feed.id, self.publisher_name) class Meta: db_table = 'feed_info' app_label = 'multigtfs' verbose_name_plural = "feed info" _column_map = (('feed_publisher_name', 'publisher_name'), ('feed_publisher_url', 'publisher_url'), ('feed_lang', 'lang'), ('feed_start_date', 'start_date'), ('feed_end_date', 'end_date'), ('feed_version', 'version')) _filename = 'feed_info.txt' _unique_fields = ('feed_publisher_name', )
class Route(Base): """A transit route""" feed = models.ForeignKey('Feed') route_id = models.CharField(max_length=255, db_index=True, help_text="Unique identifier for route.") agency = models.ForeignKey('Agency', null=True, blank=True, help_text="Agency for this route.") short_name = models.CharField(max_length=63, help_text="Short name of the route") long_name = models.CharField(max_length=255, help_text="Long name of the route") desc = models.TextField(blank=True, help_text="Long description of a route") rtype = models.IntegerField( choices=((0, 'Tram, Streetcar, or Light rail'), (1, 'Subway or Metro'), (2, 'Rail'), (3, 'Bus'), (4, 'Ferry'), (5, 'Cable car'), (6, 'Gondola or Suspended cable car'), (7, 'Funicular')), help_text='Type of transportation used on route') url = models.URLField(blank=True, help_text="Web page about for the route") color = models.CharField(max_length=6, blank=True, help_text="Color of route in hex") text_color = models.CharField(max_length=6, blank=True, help_text="Color of route text in hex") geometry = models.MultiLineStringField(null=True, blank=True, help_text='Geometry cache of Trips') def update_geometry(self): """Update the geometry from the Trips""" original = self.geometry trips = self.trip_set.exclude(geometry=None) unique_coords = set() unique_geom = list() for t in trips: coords = t.geometry.coords if coords not in unique_coords: unique_coords.add(coords) unique_geom.append(t.geometry) self.geometry = MultiLineString(unique_geom) if self.geometry != original: self.save() def __unicode__(self): return u"%d-%s" % (self.feed.id, self.route_id) def stop_set(self): stoptimes = itertools.chain(*[ trip.stoptime_set.all().distinct('stop') for trip in self.trip_set.all() ]) return set([stoptime.stop for stoptime in stoptimes]) class Meta: db_table = 'multigtfs_route' app_label = 'multigtfs' _column_map = (('route_id', 'route_id'), ('agency_id', 'agency__agency_id'), ('route_short_name', 'short_name'), ('route_long_name', 'long_name'), ('route_desc', 'desc'), ('route_type', 'rtype'), ('route_url', 'url'), ('route_color', 'color'), ('route_text_color', 'text_color')) _sort_order = ('route_id', 'short_name')
class Stop(Base): """A stop or station""" feed = models.ForeignKey('Feed') stop_id = models.CharField( max_length=255, db_index=True, help_text="Unique identifier for a stop or station.") code = models.CharField( max_length=255, blank=True, help_text="Uniquer identifier (short text or number) for passengers.") name = models.CharField(max_length=255, help_text="Name of stop in local vernacular.") desc = models.CharField("description", max_length=255, blank=True, help_text='Description of a stop.') point = models.PointField( help_text='WGS 84 latitude/longitude of stop or station') zone = models.ForeignKey('Zone', null=True, blank=True, help_text="Fare zone for a stop ID.") url = models.URLField(blank=True, help_text="URL for the stop") location_type = models.CharField(max_length=1, blank=True, choices=(('0', 'Stop'), ('1', 'Station')), help_text="Is this a stop or station?") parent_station = models.ForeignKey( 'Stop', null=True, blank=True, help_text="The station associated with the stop") timezone = models.CharField(max_length=255, blank=True, help_text="Timezone of the stop") wheelchair_boarding = models.CharField( max_length=1, blank=True, choices=(('0', 'No information'), ('1', 'Some wheelchair boarding'), ('2', 'No wheelchair boarding')), help_text='Is wheelchair boarding possible?') extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return "%d-%s" % (self.feed_id, self.stop_id) def getlon(self): return self.point[0] if self.point else 0.0 def setlon(self, value): if self.point: self.point[0] = value else: self.point = "POINT(%s 0)" % value lon = property(getlon, setlon, doc="WGS 84 longitude of stop or station") def getlat(self): return self.point[1] if self.point else 0.0 def setlat(self, value): if self.point: self.point[1] = value else: self.point = "POINT(0 %s)" % value lat = property(getlat, setlat, doc="WGS 84 latitude of stop or station") def __init__(self, *args, **kwargs): lat = kwargs.pop('lat', None) lon = kwargs.pop('lon', None) if lat is not None or lon is not None: assert kwargs.get('point') is None msg = "Setting Stop location with lat and lon is deprecated" warnings.warn(msg, DeprecationWarning) kwargs['point'] = "POINT(%s %s)" % (lon or 0.0, lat or 0.0) super(Stop, self).__init__(*args, **kwargs) class Meta: db_table = 'stop' app_label = 'multigtfs' _column_map = ( ('stop_id', 'stop_id'), ('stop_code', 'code'), ('stop_name', 'name'), ('stop_desc', 'desc'), ('stop_lat', 'point[1]'), ('stop_lon', 'point[0]'), ('zone_id', 'zone__zone_id'), ('stop_url', 'url'), ('location_type', 'location_type'), ('parent_station', 'parent_station__stop_id'), ('stop_timezone', 'timezone'), ('wheelchair_boarding', 'wheelchair_boarding'), ) _filename = 'stops.txt' _unique_fields = ('stop_id', ) @classmethod def import_txt(cls, txt_file, feed): '''Import from a stops.txt file Stations need to be imported before stops ''' txt = txt_file.read() def is_station(pairs): '''Does the row represent a station?''' for name, val in pairs: if name == 'location_type': return val == '1' return False logger.info("Importing station stops") stations = super(Stop, cls).import_txt(StringIO(txt), feed, is_station) logger.info("Imported %d station stops", stations) def is_stop(pairs): '''Does the row represent a stop?''' for name, val in pairs: if name == 'location_type': return val != '1' return True logger.info("Importing non-station stops") stops = super(Stop, cls).import_txt(StringIO(txt), feed, is_stop) logger.info("Imported %d non-station stops", stops) return stations + stops
class Route(Base): """A transit route Maps to route.txt in the GTFS feed. """ feed = models.ForeignKey('Feed', on_delete=models.CASCADE) route_id = models.CharField(max_length=255, db_index=True, help_text="Unique identifier for route.") agency = models.ForeignKey('Agency', null=True, blank=True, on_delete=models.SET_NULL, help_text="Agency for this route.") short_name = models.CharField(max_length=63, help_text="Short name of the route") long_name = models.CharField(max_length=255, help_text="Long name of the route") desc = models.TextField("description", blank=True, help_text="Long description of a route") rtype = models.IntegerField( "route type", choices=((0, 'Tram, Streetcar, or Light rail'), (1, 'Subway or Metro'), (2, 'Rail'), (3, 'Bus'), (4, 'Ferry'), (5, 'Cable car'), (6, 'Gondola or Suspended cable car'), (7, 'Funicular')), help_text='Type of transportation used on route') url = models.URLField(blank=True, help_text="Web page about for the route") color = models.CharField(max_length=6, blank=True, help_text="Color of route in hex") text_color = models.CharField(max_length=6, blank=True, help_text="Color of route text in hex") geometry = models.MultiLineStringField(null=True, blank=True, help_text='Geometry cache of Trips') extra_data = JSONField(default={}, blank=True, null=True) def update_geometry(self): """Update the geometry from the Trips""" original = self.geometry trips = self.trip_set.exclude(geometry__isnull=True) unique_coords = set() unique_geom = list() for t in trips: coords = t.geometry.coords if coords not in unique_coords: unique_coords.add(coords) unique_geom.append(t.geometry) self.geometry = MultiLineString(unique_geom) if self.geometry != original: self.save() def __str__(self): return "%d-%s" % (self.feed.id, self.route_id) class Meta: db_table = 'route' app_label = 'multigtfs' _column_map = (('route_id', 'route_id'), ('agency_id', 'agency__agency_id'), ('route_short_name', 'short_name'), ('route_long_name', 'long_name'), ('route_desc', 'desc'), ('route_type', 'rtype'), ('route_url', 'url'), ('route_color', 'color'), ('route_text_color', 'text_color')) _filename = 'routes.txt' _sort_order = ('route_id', 'short_name') _unique_fields = ('route_id', )
class Stop(Base): """A stop or station""" feed = models.ForeignKey('Feed') stop_id = models.CharField( max_length=255, db_index=True, help_text="Unique identifier for a stop or station.") code = models.CharField( max_length=255, blank=True, help_text="Uniquer identifier (short text or number) for passengers.") name = models.CharField(max_length=255, help_text="Name of stop in local vernacular.") desc = models.CharField(max_length=255, blank=True, help_text='Description of a stop.') point = models.PointField( geography=MULTIGTFS_USE_GEOGRAPHY, srid=MULTIGTFS_SRID, help_text='WGS 84 latitude/longitude of stop or station') zone = models.ForeignKey('Zone', null=True, blank=True, help_text="Fare zone for a stop ID.") url = models.URLField(blank=True, help_text="URL for the stop") location_type = models.CharField(max_length=1, blank=True, choices=(('0', 'Stop'), ('1', 'Station')), help_text="Is this a stop or station?") parent_station = models.ForeignKey( 'Stop', null=True, blank=True, help_text="The station associated with the stop") timezone = models.CharField(max_length=255, blank=True, help_text="Timezone of the stop") wheelchair_boarding = models.CharField( max_length=1, blank=True, choices=(('0', '0'), ('1', '1'), ('2', '2')), help_text= "Whether wheelchair boardings are possible from the specified stop or station" ) def __unicode__(self): return u"%d-%s" % (self.feed.id, self.stop_id) def getlon(self): return self.point[0] if self.point else 0.0 def setlon(self, value): if self.point: self.point[0] = value else: self.point = "POINT(%s 0)" % value lon = property(getlon, setlon, doc="WGS 84 longitude of stop or station") def getlat(self): return self.point[1] if self.point else 0.0 def setlat(self, value): if self.point: self.point[1] = value else: self.point = "POINT(0 %s)" % value lat = property(getlat, setlat, doc="WGS 84 latitude of stop or station") def __init__(self, *args, **kwargs): lat = kwargs.pop('lat', None) lon = kwargs.pop('lon', None) if lat is not None or lon is not None: assert kwargs.get('point') is None msg = "Setting Stop location with lat and lon is deprecated" warnings.warn(msg, DeprecationWarning) kwargs['point'] = "POINT(%s %s)" % (lon or 0.0, lat or 0.0) super(Stop, self).__init__(*args, **kwargs) class Meta: db_table = 'stop' app_label = 'multigtfs' _column_map = (('stop_id', 'stop_id'), ('stop_code', 'code'), ('stop_name', 'name'), ('stop_desc', 'desc'), ('stop_lat', 'point[1]'), ('stop_lon', 'point[0]'), ('zone_id', 'zone__zone_id'), ('stop_url', 'url'), ('location_type', 'location_type'), ('parent_station', 'parent_station__stop_id'), ('stop_timezone', 'timezone'), ('wheelchair_boarding', 'wheelchair_boarding')) @classmethod def import_txt(cls, txt_file, feed): '''Import from a stops.txt file Stations need to be imported before stops ''' def writeheader(writer): ''' Write the header row for a DictWriter CSV file This is a member function of DictWriter in Python 2.7 ''' writer.writerow(dict((fn, fn) for fn in writer.fieldnames)) txt = txt_file.read() fieldnames, _ = zip(*cls._column_map) has_stations = False stations_csv = StringIO.StringIO() stations = DictWriter(stations_csv, fieldnames) has_stops = False stops_csv = StringIO.StringIO() stops = DictWriter(stops_csv, fieldnames) for row in DictReader(StringIO.StringIO(txt)): if row.get('location_type') == '1': if not has_stations: writeheader(stations) has_stations = True stations.writerow(row) else: if not has_stops: writeheader(stops) has_stops = True stops.writerow(row) if has_stations: super(Stop, cls).import_txt(StringIO.StringIO(stations_csv.getvalue()), feed) if has_stops: super(Stop, cls).import_txt(StringIO.StringIO(stops_csv.getvalue()), feed)