class Fare(Base): """A fare class""" feed = models.ForeignKey('Feed') fare_id = models.CharField(max_length=255, db_index=True, help_text="Unique identifier for a fare class") price = models.DecimalField( max_digits=17, decimal_places=4, help_text="Fare price, in units specified by currency_type") currency_type = models.CharField( max_length=3, help_text="ISO 4217 alphabetical currency code") payment_method = models.IntegerField( default=1, choices=((0, 'Fare is paid on board.'), (1, 'Fare must be paid before boarding.')), help_text="When is the fare paid?") transfers = models.IntegerField( default=None, null=True, blank=True, choices=((0, 'No transfers permitted on this fare.'), (1, 'Passenger may transfer once.'), (2, 'Passenger may transfer twice.'), (None, 'Unlimited transfers are permitted.')), help_text="Are transfers permitted?") transfer_duration = models.IntegerField( null=True, blank=True, help_text="Time in seconds until a ticket or transfer expires") extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return u"%d-%s(%s %s)" % (self.feed.id, self.fare_id, self.price, self.currency_type) class Meta: db_table = 'fare' app_label = 'multigtfs' # For Base import/export _column_map = (('fare_id', 'fare_id'), ('price', 'price'), ('currency_type', 'currency_type'), ('payment_method', 'payment_method'), ('transfers', 'transfers'), ('transfer_duration', 'transfer_duration')) _filename = 'fare_attributes.txt' _unique_fields = ('fare_id', )
class Frequency(Base): """Description of a trip that repeats without fixed stop times""" trip = models.ForeignKey('Trip') start_time = SecondsField( help_text="Time that the service begins at the specified frequency") end_time = SecondsField( help_text="Time that the service ends at the specified frequency") headway_secs = models.IntegerField( help_text="Time in seconds before returning to same stop") exact_times = models.CharField( max_length=1, blank=True, choices=((0, 'Trips are not exactly scheduled'), (1, 'Trips are exactly scheduled from start time')), help_text="Should frequency-based trips be exactly scheduled?") extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return str(self.trip) class Meta: db_table = 'frequency' app_label = 'multigtfs' verbose_name_plural = "frequencies" # For Base import/export _column_map = (('trip_id', 'trip__trip_id'), ('start_time', 'start_time'), ('end_time', 'end_time'), ('headway_secs', 'headway_secs'), ('exact_times', 'exact_times')) _filename = 'frequencies.txt' _rel_to_feed = 'trip__route__feed' _unique_fields = ('trip_id', 'start_time')
class ServiceDate(Base): """Dates that a route is active.""" service = models.ForeignKey('Service') date = models.DateField( help_text="Date that the service differs from the norm.") exception_type = models.IntegerField( default=1, choices=((1, 'Added'), (2, 'Removed')), help_text="Is service added or removed on this date?") extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return ("%d-%s %s %s" % (self.service.feed.id, self.service.service_id, self.date, 'Added' if self.exception_type == 1 else 'Removed')) class Meta: db_table = 'service_date' app_label = 'multigtfs' # For Base import/export _column_map = (('service_id', 'service__service_id'), ('date', 'date'), ('exception_type', 'exception_type')) _filename = 'calendar_dates.txt' _rel_to_feed = 'service__feed' _sort_order = ('date', 'exception_type') _unique_fields = ('service_id', 'date')
class StopTime(Base): """A specific stop on a route on a trip. This implements stop_times.txt in the GTFS feed """ trip = models.ForeignKey(Trip, on_delete=models.CASCADE) stop = models.ForeignKey(Stop, on_delete=models.CASCADE) arrival_time = SecondsField( default=None, null=True, blank=True, help_text="Arrival time. Must be set for end stops of trip.") departure_time = SecondsField( default=None, null=True, blank=True, help_text='Departure time. Must be set for end stops of trip.') stop_sequence = models.IntegerField() stop_headsign = models.CharField( max_length=255, blank=True, help_text="Sign text that identifies the stop for passengers") pickup_type = models.CharField( max_length=1, blank=True, choices=(('0', 'Regularly scheduled pickup'), ('1', 'No pickup available'), ('2', 'Must phone agency to arrange pickup'), ('3', 'Must coordinate with driver to arrange pickup')), help_text="How passengers are picked up") drop_off_type = models.CharField( max_length=1, blank=True, choices=(('0', 'Regularly scheduled drop off'), ('1', 'No drop off available'), ('2', 'Must phone agency to arrange drop off'), ('3', 'Must coordinate with driver to arrange drop off')), help_text="How passengers are picked up") shape_dist_traveled = models.FloatField( "shape distance traveled", null=True, blank=True, help_text='Distance of stop from start of shape') extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return "%s-%s-%s" % (self.trip, self.stop.stop_id, self.stop_sequence) class Meta: db_table = 'stop_time' app_label = 'multigtfs' _column_map = ( ('trip_id', 'trip__trip_id'), ('arrival_time', 'arrival_time'), ('departure_time', 'departure_time'), ('stop_id', 'stop__stop_id'), ('stop_sequence', 'stop_sequence'), ('stop_headsign', 'stop_headsign'), ('pickup_type', 'pickup_type'), ('drop_off_type', 'drop_off_type'), ('shape_dist_traveled', 'shape_dist_traveled') ) _filename = 'stop_times.txt' _rel_to_feed = 'trip__route__feed' _sort_order = ('trip__trip_id', 'stop_sequence') _unique_fields = ('trip_id', 'stop_sequence')
class ShapePoint(Base): """A point along the shape""" shape = models.ForeignKey('Shape', related_name='points') point = models.PointField( geography=MULTIGTFS_USE_GEOGRAPHY, srid=MULTIGTFS_SRID, help_text='WGS 84 latitude/longitude of shape point') sequence = models.IntegerField() traveled = models.FloatField( null=True, blank=True, help_text='Distance of point from start of shape') def __unicode__(self): return u"%s-%d" % (self.shape, self.sequence) 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 shape point") 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 shape point") 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 ShapePoint 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(ShapePoint, self).__init__(*args, **kwargs) class Meta: db_table = 'shape_point' app_label = 'multigtfs' _column_map = ( ('shape_id', 'shape__shape_id'), ('shape_pt_lat', 'point[1]'), ('shape_pt_lon', 'point[0]'), ('shape_pt_sequence', 'sequence'), ('shape_dist_traveled', 'traveled') ) _rel_to_feed = 'shape__feed' _sort_order = ('shape__shape_id', 'sequence')
class Transfer(Base): """Create additional rules for transfers between ambiguous stops. Implements transfer.txt in the GTFS feed. """ from_stop = models.ForeignKey( 'Stop', on_delete=models.CASCADE, related_name='transfer_from_stop', help_text='Stop where a connection between routes begins.') to_stop = models.ForeignKey( 'Stop', on_delete=models.CASCADE, related_name='transfer_to_stop', help_text='Stop where a connection between routes ends.') transfer_type = models.IntegerField( default=0, blank=True, choices=((0, 'Recommended transfer point'), (1, 'Timed transfer point (vehicle will wait)'), (2, 'min_transfer_time needed to successfully transfer'), (3, 'No transfers possible')), help_text="What kind of transfer?") min_transfer_time = models.IntegerField( null=True, blank=True, help_text="How many seconds are required to transfer?") extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return "%s-%s" % (self.from_stop, self.to_stop.stop_id) class Meta: db_table = 'transfer' app_label = 'multigtfs' _column_map = (('from_stop_id', 'from_stop__stop_id'), ('to_stop_id', 'to_stop__stop_id'), ('transfer_type', 'transfer_type'), ('min_transfer_time', 'min_transfer_time')) _filename = 'transfers.txt' _rel_to_feed = 'from_stop__feed' _sort_order = ('from_stop__stop_id', 'to_stop__stop_id') _unique_fields = ('from_stop_id', 'to_stop_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 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 ShapePoint(Base): """A point along the shape""" shape = models.ForeignKey( 'Shape', on_delete=models.CASCADE, related_name='points') point = models.PointField( help_text='WGS 84 latitude/longitude of shape point') sequence = models.IntegerField() traveled = models.FloatField( null=True, blank=True, help_text='Distance of point from start of shape') extra_data = JSONField(default={}, blank=True, null=True) def __str__(self): return "%s-%d" % (self.shape, self.sequence) 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 shape point") 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 shape point") def __init__(self, *args, **kwargs): """Initialize a ShapePoint If the legacy lat and lon params are used, then warn and initialize the point from them. """ 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 ShapePoint 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(ShapePoint, self).__init__(*args, **kwargs) class Meta: db_table = 'shape_point' app_label = 'multigtfs' _column_map = ( ('shape_id', 'shape__shape_id'), ('shape_pt_lat', 'point[1]'), ('shape_pt_lon', 'point[0]'), ('shape_pt_sequence', 'sequence'), ('shape_dist_traveled', 'traveled') ) _filename = 'shapes.txt' _rel_to_feed = 'shape__feed' _sort_order = ('shape__shape_id', 'sequence') _unique_fields = ('shape_id', 'shape_pt_sequence')
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', )