Пример #1
0
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')
Пример #2
0
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')
Пример #3
0
class FareRule(Base):
    """Associate a Fare with a Route and/or Zones"""
    fare = models.ForeignKey('Fare', on_delete=models.CASCADE)
    route = models.ForeignKey('Route',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              help_text="Fare class is valid for this route.")
    origin = models.ForeignKey(
        'Zone',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='fare_origins',
        help_text="Fare class is valid for travel originating in this zone.")
    destination = models.ForeignKey(
        'Zone',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='fare_destinations',
        help_text="Fare class is valid for travel ending in this zone.")
    contains = models.ForeignKey(
        'Zone',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='fare_contains',
        help_text="Fare class is valid for travel withing this zone.")
    extra_data = JSONField(default={}, blank=True, null=True)

    def __str__(self):
        u = "%d-%s" % (self.fare.feed.id, self.fare.fare_id)
        if self.route:
            u += '-%s' % self.route.route_id
        return u

    class Meta:
        db_table = 'fare_rules'
        app_label = 'multigtfs'

    # For Base import/export
    _column_map = (('fare_id', 'fare__fare_id'), ('route_id',
                                                  'route__route_id'),
                   ('origin_id', 'origin__zone_id'), ('destination_id',
                                                      'destination__zone_id'),
                   ('contains_id', 'contains__zone_id'))
    _filename = 'fare_rules.txt'
    _rel_to_feed = 'fare__feed'
    _sort_order = ('route__route_id', 'fare__fare_id')
    _unique_fields = ('fare_id', 'route_id', 'origin_id', 'destination_id',
                      'contains_id')
Пример #4
0
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')
Пример #5
0
class NewStop(Base):
    """A stop captured during a ride

    To reference new stops data in the GTFS feed.
    """
    ride = models.ForeignKey('Ride')
    latitude = models.CharField(
        max_length=255, blank=True, null=True,
        help_text='WGS 84 latitude of stop or station')
    longitude = models.CharField(
        max_length=255, blank=True, null=True,
        help_text='WGS 84 latitude of stop or station')
    arrival_time = models.CharField(
        max_length=255, blank=True, null=True,
        help_text='What time did the ride arrive?'
    )
    departure_time = models.CharField(
        max_length=255, blank=True, null=True,
        help_text='What time did the ride arrive?'
    )
    board = models.CharField(
        max_length=255, blank=True, null=True,
    )
    alight = models.CharField(
        max_length=255, blank=True, null=True,
    )
    extra_data = JSONField(default={}, blank=True, null=True)

    def __str__(self):
        return "%s %s" % (self.latitude, self.longitude)
Пример #6
0
class Shape(Base):
    """The path the vehicle takes along the route.

    Implements shapes.txt."""
    feed = models.ForeignKey('Feed', on_delete=models.CASCADE)
    shape_id = models.CharField(
        max_length=255, db_index=True,
        help_text="Unique identifier for a shape.")
    geometry = models.LineStringField(
        null=True, blank=True,
        help_text='Geometry cache of ShapePoints')

    def __str__(self):
        return "%d-%s" % (self.feed.id, self.shape_id)

    def update_geometry(self, update_parent=True):
        """Update the geometry from the related ShapePoints"""
        original = self.geometry
        points = self.points.order_by(
            'sequence').values_list('point', flat=True)
        if len(points) > 1:
            self.geometry = LineString([pt.coords for pt in points])
            if self.geometry != original:
                self.save()
                if update_parent:
                    for trip in self.trip_set.all():
                        trip.update_geometry()

    class Meta:
        db_table = 'shape'
        app_label = 'multigtfs'

    _rel_to_feed = 'feed'
Пример #7
0
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')
Пример #8
0
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'))
Пример #9
0
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')
Пример #10
0
class Service(Base):
    """Dates that a route is active.

    Implements calendar.txt
    """

    feed = models.ForeignKey('Feed', on_delete=models.CASCADE)
    service_id = models.CharField(
        max_length=255,
        db_index=True,
        help_text="Unique identifier for service dates.")
    monday = models.BooleanField(default=True,
                                 help_text="Is the route active on Monday?")
    tuesday = models.BooleanField(default=True,
                                  help_text="Is the route active on Tuesday?")
    wednesday = models.BooleanField(
        default=True, help_text="Is the route active on Wednesday?")
    thursday = models.BooleanField(
        default=True, help_text="Is the route active on Thursday?")
    friday = models.BooleanField(default=True,
                                 help_text="Is the route active on Friday?")
    saturday = models.BooleanField(
        default=True, help_text="Is the route active on Saturday?")
    sunday = models.BooleanField(default=True,
                                 help_text="Is the route active on Sunday?")
    start_date = models.DateField(null=True, blank=True)
    end_date = models.DateField(null=True, blank=True)
    extra_data = JSONField(default={}, blank=True, null=True)

    def __str__(self):
        return "%d-%s" % (self.feed.id, self.service_id)

    class Meta:
        db_table = 'gtfs_service'
        app_label = 'multigtfs'

    # For Base import/export
    _column_map = (('service_id', 'service_id'), ('monday', 'monday'),
                   ('tuesday', 'tuesday'), ('wednesday', 'wednesday'),
                   ('thursday', 'thursday'), ('friday', 'friday'),
                   ('saturday', 'saturday'), ('sunday', 'sunday'),
                   ('start_date', 'start_date'), ('end_date', 'end_date'))
    _filename = 'calendar.txt'
    _sort_order = ('start_date', 'end_date')
    _unique_fields = ('service_id', )

    @classmethod
    def export_txt(cls, feed):
        '''Export records as calendar.txt'''

        # If no records with start/end dates, skip calendar.txt
        objects = cls.objects.in_feed(feed)

        if not objects.exclude(start_date__isnull=True,
                               end_date__isnull=True).exists():
            return None

        return super(Service, cls).export_txt(feed)
Пример #11
0
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')
Пример #12
0
class Ride(Base):
    """A ride along a given route

    To reference new stops data in the GTFS feed.
    """
    route = models.ForeignKey('Route')
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return "%s" % self.route.route_id
Пример #13
0
class FareRule(Base):
    """Associate a Fare with a Route and/or Zones"""
    fare = models.ForeignKey('Fare')
    route = models.ForeignKey(
        'Route', null=True, blank=True,
        help_text="Fare class is valid for this route.")
    origin = models.ForeignKey(
        'Zone', null=True, blank=True,
        related_name='fare_origins',
        help_text="Fare class is valid for travel originating in this zone.")
    destination = models.ForeignKey(
        'Zone', null=True, blank=True,
        related_name='fare_destinations',
        help_text="Fare class is valid for travel ending in this zone.")
    contains = models.ForeignKey(
        'Zone', null=True, blank=True,
        related_name='fare_contains',
        help_text="Fare class is valid for travel withing this zone.")

    def __unicode__(self):
        u = u"%d-%s" % (self.fare.feed.id, self.fare.fare_id)
        if self.route:
            u += '-%s' % self.route.route_id
        return u

    class Meta:
        db_table = 'multigtfs_farerule'
        app_label = 'multigtfs'

    # For Base import/export
    _column_map = (
        ('fare_id', 'fare__fare_id'),
        ('route_id', 'route__route_id'),
        ('origin_id', 'origin__zone_id'),
        ('destination_id', 'destination__zone_id'),
        ('contains_id', 'contains__zone_id')
    )
    _rel_to_feed = 'fare__feed'
    _sort_order = ('route__route_id', 'fare__fare_id')
Пример #14
0
class Zone(Base):
    """Represents a fare zone.

    This data is not represented as a file in the GTFS.  It appears as an
    identifier in the fare_rules and the stop tables.
    """
    feed = models.ForeignKey('Feed')
    zone_id = models.CharField(max_length=10,
                               db_index=True,
                               help_text="Unique identifier for a zone.")

    class Meta:
        db_table = 'zone'
        app_label = 'multigtfs'
Пример #15
0
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', )
Пример #16
0
class Shape(Base):
    """The path the vehicle takes along the route"""
    feed = models.ForeignKey('Feed')
    shape_id = models.CharField(
        max_length=255, db_index=True,
        help_text="Unique identifier for a shape.")

    def __unicode__(self):
        return u"%d-%s" % (self.feed.id, self.shape_id)

    class Meta:
        db_table = 'shape'
        app_label = 'multigtfs'

    _rel_to_feed = 'feed'
Пример #17
0
class Service(Base):
    """Dates that a route is active."""

    feed = models.ForeignKey('Feed')
    service_id = models.CharField(
        max_length=255,
        db_index=True,
        help_text="Unique identifier for service dates.")
    monday = models.BooleanField(default=True,
                                 help_text="Is the route active on Monday?")
    tuesday = models.BooleanField(default=True,
                                  help_text="Is the route active on Tuesday?")
    wednesday = models.BooleanField(
        default=True, help_text="Is the route active on Wednesday?")
    thursday = models.BooleanField(
        default=True, help_text="Is the route active on Thursday?")
    friday = models.BooleanField(default=True,
                                 help_text="Is the route active on Friday?")
    saturday = models.BooleanField(
        default=True, help_text="Is the route active on Saturday?")
    sunday = models.BooleanField(default=True,
                                 help_text="Is the route active on Sunday?")
    start_date = models.DateField(
        default=datetime.strptime('18991231', '%Y%m%d'))
    end_date = models.DateField(
        default=datetime.strptime('21000101', '%Y%m%d'))

    def __unicode__(self):
        return u"%d-%s" % (self.feed.id, self.service_id)

    class Meta:
        db_table = 'multigtfs_service'
        app_label = 'multigtfs'

    # For Base import/export
    _column_map = (('service_id', 'service_id'), ('monday', 'monday'),
                   ('tuesday', 'tuesday'), ('wednesday', 'wednesday'),
                   ('thursday', 'thursday'), ('friday', 'friday'),
                   ('saturday', 'saturday'), ('sunday', 'sunday'),
                   ('start_date', 'start_date'), ('end_date', 'end_date'))
    _sort_order = ('start_date', 'end_date')

    # support commonly out-of-date GTFS feed data
    # {'old csv name': 'django field name'}
    _legacy_format = {
        'service_name': 'service_id',
    }
Пример #18
0
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',)
Пример #19
0
class Block(Base):
    """Represents a fare zone.

    This data is not represented as a file in the GTFS.  It appears as an
    identifier in the trip table.
    """
    feed = models.ForeignKey('Feed')
    block_id = models.CharField(max_length=63,
                                db_index=True,
                                help_text="Unique identifier for a block.")

    def __str__(self):
        return u"%d-%s" % (self.feed.id, self.block_id)

    class Meta:
        db_table = 'block'
        app_label = 'multigtfs'
Пример #20
0
class Zone(Base):
    """Represents a fare zone.

    This data is not represented as a file in the GTFS.  It appears as an
    identifier in the fare_rules and the stop tables.
    """
    feed = models.ForeignKey('Feed', on_delete=models.CASCADE)
    zone_id = models.CharField(max_length=63,
                               db_index=True,
                               help_text="Unique identifier for a zone.")

    def __str__(self):
        return "%d-%s" % (self.feed_id, self.zone_id)

    class Meta:
        db_table = 'zone'
        app_label = 'multigtfs'
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', )
Пример #22
0
class Service(Base):
    """Dates that a route is active."""

    feed = models.ForeignKey('Feed')
    service_id = models.CharField(
        max_length=255,
        db_index=True,
        help_text="Unique identifier for service dates.")
    monday = models.BooleanField(default=True,
                                 help_text="Is the route active on Monday?")
    tuesday = models.BooleanField(default=True,
                                  help_text="Is the route active on Tuesday?")
    wednesday = models.BooleanField(
        default=True, help_text="Is the route active on Wednesday?")
    thursday = models.BooleanField(
        default=True, help_text="Is the route active on Thursday?")
    friday = models.BooleanField(default=True,
                                 help_text="Is the route active on Friday?")
    saturday = models.BooleanField(
        default=True, help_text="Is the route active on Saturday?")
    sunday = models.BooleanField(default=True,
                                 help_text="Is the route active on Sunday?")
    start_date = models.DateField()
    end_date = models.DateField()
    extra_data = JSONField(default={}, blank=True, null=True)

    def __str__(self):
        return "%d-%s" % (self.feed.id, self.service_id)

    class Meta:
        db_table = 'service'
        app_label = 'multigtfs'

    # For Base import/export
    _column_map = (('service_id', 'service_id'), ('monday', 'monday'),
                   ('tuesday', 'tuesday'), ('wednesday', 'wednesday'),
                   ('thursday', 'thursday'), ('friday', 'friday'),
                   ('saturday', 'saturday'), ('sunday', 'sunday'),
                   ('start_date', 'start_date'), ('end_date', 'end_date'))
    _filename = 'calendar.txt'
    _sort_order = ('start_date', 'end_date')
    _unique_fields = ('service_id', )
Пример #23
0
class NewRoute(Base):
    """A stop captured during a ride

    To reference new stops data in the GTFS feed.
    """
    ride = models.ForeignKey('Ride')
    latitude = models.CharField(max_length=255,
                                blank=True,
                                null=True,
                                help_text='WGS 84 latitude of stop or station')
    longitude = models.CharField(
        max_length=255,
        blank=True,
        null=True,
        help_text='WGS 84 latitude of stop or station')
    time = models.CharField(max_length=255,
                            blank=True,
                            null=True,
                            help_text='What time did the ride arrive?')

    def __str__(self):
        return "%s %s" % (self.latitude, self.longitude)
Пример #24
0
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')
Пример #25
0
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)
Пример #26
0
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')
Пример #27
0
class Trip(Base):
    """A trip along a route

    This implements trips.txt in the GTFS feed
    """

    route = models.ForeignKey('Route', on_delete=models.CASCADE)
    service = models.ForeignKey('Service',
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL)
    trip_id = models.CharField(max_length=255,
                               db_index=True,
                               help_text="Unique identifier for a trip.")
    headsign = models.CharField(
        max_length=255,
        blank=True,
        help_text="Destination identification for passengers.")
    short_name = models.CharField(
        max_length=63,
        blank=True,
        help_text="Short name used in schedules and signboards.")
    direction = models.CharField(
        max_length=1,
        blank=True,
        choices=(('0', '0'), ('1', '1')),
        help_text="Direction for bi-directional routes.")
    block = models.ForeignKey(
        'Block',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        help_text="Block of sequential trips that this trip belongs to.")
    wheelchair_accessible = models.CharField(
        max_length=1,
        blank=True,
        choices=(('0', 'No information'), ('1',
                                           'Some wheelchair accommodation'),
                 ('2', 'No wheelchair accommodation')),
        help_text='Are there accommodations for riders with wheelchair?')
    bikes_allowed = models.CharField(max_length=1,
                                     blank=True,
                                     choices=(('0', 'No information'),
                                              ('1',
                                               'Some bicycle accommodation'),
                                              ('2', 'No bicycles allowed')),
                                     help_text='Are bicycles allowed?')
    extra_data = JSONField(default={}, blank=True, null=True)

    def __str__(self):
        return "%s-%s" % (self.route, self.trip_id)

    class Meta:
        db_table = 'gtfs_trip'
        app_label = 'multigtfs'

    _column_map = (
        ('route_id', 'route__route_id'),
        ('service_id', 'service__service_id'),
        ('trip_id', 'trip_id'),
        ('trip_headsign', 'headsign'),
        ('trip_short_name', 'short_name'),
        ('direction_id', 'direction'),
        ('block_id', 'block__block_id'),
        ('wheelchair_accessible', 'wheelchair_accessible'),
        ('bikes_allowed', 'bikes_allowed'),
    )
    _filename = 'trips.txt'
    _rel_to_feed = 'route__feed'
    _unique_fields = ('trip_id', )
Пример #28
0
class Trip(Base):
    """A trip along a route

    This implements trips.txt in the GTFS feed
    """

    route = models.ForeignKey('Route', on_delete=models.CASCADE)
    service = models.ForeignKey('Service',
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL)
    trip_id = models.CharField(max_length=255,
                               db_index=True,
                               help_text="Unique identifier for a trip.")
    headsign = models.CharField(
        max_length=255,
        blank=True,
        help_text="Destination identification for passengers.")
    short_name = models.CharField(
        max_length=63,
        blank=True,
        help_text="Short name used in schedules and signboards.")
    direction = models.CharField(
        max_length=1,
        blank=True,
        choices=(('0', '0'), ('1', '1')),
        help_text="Direction for bi-directional routes.")
    block = models.ForeignKey(
        'Block',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        help_text="Block of sequential trips that this trip belongs to.")
    shape = models.ForeignKey('Shape',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              help_text="Shape used for this trip")
    geometry = models.LineStringField(
        null=True, blank=True, help_text='Geometry cache of Shape or Stops')
    wheelchair_accessible = models.CharField(
        max_length=1,
        blank=True,
        choices=(('0', 'No information'), ('1',
                                           'Some wheelchair accommodation'),
                 ('2', 'No wheelchair accommodation')),
        help_text='Are there accommodations for riders with wheelchair?')
    bikes_allowed = models.CharField(max_length=1,
                                     blank=True,
                                     choices=(('0', 'No information'),
                                              ('1',
                                               'Some bicycle accommodation'),
                                              ('2', 'No bicycles allowed')),
                                     help_text='Are bicycles allowed?')
    extra_data = JSONField(default={}, blank=True, null=True)

    def update_geometry(self, update_parent=True):
        """Update the geometry from the Shape or Stops"""
        original = self.geometry
        if self.shape:
            self.geometry = self.shape.geometry
        else:
            stoptimes = self.stoptime_set.order_by('stop_sequence')
            if stoptimes.count() > 1:
                self.geometry = LineString(
                    [st.stop.point.coords for st in stoptimes])
        if self.geometry != original:
            self.save()
            if update_parent:
                self.route.update_geometry()

    def __str__(self):
        return "%s-%s" % (self.route, self.trip_id)

    class Meta:
        db_table = 'trip'
        app_label = 'multigtfs'

    _column_map = (
        ('route_id', 'route__route_id'),
        ('service_id', 'service__service_id'),
        ('trip_id', 'trip_id'),
        ('trip_headsign', 'headsign'),
        ('trip_short_name', 'short_name'),
        ('direction_id', 'direction'),
        ('block_id', 'block__block_id'),
        ('shape_id', 'shape__shape_id'),
        ('wheelchair_accessible', 'wheelchair_accessible'),
        ('bikes_allowed', 'bikes_allowed'),
    )
    _filename = 'trips.txt'
    _rel_to_feed = 'route__feed'
    _unique_fields = ('trip_id', )
Пример #29
0
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', )
Пример #30
0
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