gtfs_reader 1.2.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +18 -15
- data/Rakefile +6 -9
- data/lib/gtfs_reader/bulk_feed_handler.rb +28 -24
- data/lib/gtfs_reader/config/column.rb +16 -16
- data/lib/gtfs_reader/config/defaults/gtfs_feed_definition.rb +50 -46
- data/lib/gtfs_reader/config/feed_definition.rb +19 -19
- data/lib/gtfs_reader/config/file_definition.rb +34 -33
- data/lib/gtfs_reader/config/source.rb +17 -15
- data/lib/gtfs_reader/config/sources.rb +7 -3
- data/lib/gtfs_reader/configuration.rb +8 -9
- data/lib/gtfs_reader/core.rb +14 -15
- data/lib/gtfs_reader/exceptions.rb +0 -1
- data/lib/gtfs_reader/feed_handler.rb +12 -8
- data/lib/gtfs_reader/file_reader.rb +34 -36
- data/lib/gtfs_reader/file_row.rb +40 -32
- data/lib/gtfs_reader/log.rb +37 -23
- data/lib/gtfs_reader/source_updater.rb +40 -43
- data/lib/gtfs_reader/version.rb +29 -28
- metadata +39 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e25dd494cd1cfa6d1e2fef48744b1016644362c6
|
4
|
+
data.tar.gz: b147c7f108b84ac047b68e50a704bb1aa3d4f6ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9dc292265e217230dac89bf9f92ac5c7bcec17e19e77ef62c9dd34e79660ebf77f1732d96627925d01ced21bace26479b6c4dddff59fcbd43d43ff43a42ecfd
|
7
|
+
data.tar.gz: dfd918d96505e679e61ac204441df6c7d4976601d7ea5d413ceeb47f4dc7941dd56d04e1492e2afad935c8ebcf19c3b74ce44aff8119ca4af03595e26be0242e
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ Feed](https://developers.google.com/transit/gtfs)":
|
|
15
15
|
> developers to write applications that consume that data in an interoperable
|
16
16
|
> way.
|
17
17
|
|
18
|
-
Essentially, a GTFS feed is a ZIP file containing
|
18
|
+
Essentially, a GTFS feed is a ZIP file containing
|
19
19
|
[CSV-formatted](https://en.wikipedia.org/wiki/Comma-separated_values) .txt
|
20
20
|
files which following the specification.
|
21
21
|
|
@@ -27,26 +27,27 @@ files which following the specification.
|
|
27
27
|
require 'gtfs_reader'
|
28
28
|
|
29
29
|
GtfsReader.config do
|
30
|
+
# verbose true # TODO: uncomment for verbose output
|
30
31
|
return_hashes true
|
31
|
-
|
32
|
+
|
32
33
|
sources do
|
33
34
|
sample do
|
34
|
-
url 'http://localhost/sample-feed.zip' # you can also use a filepath here
|
35
|
+
url 'http://localhost/sample-feed.zip' # you can also use a filepath here
|
35
36
|
before { |etag| puts "Processing source with tag #{etag}..." }
|
36
37
|
handlers do
|
37
|
-
agency {|row| puts "Read Agency: #{row[:agency_name]}" }
|
38
|
-
routes {|row| puts "Read Route: #{row[:route_long_name]}" }
|
38
|
+
agency { |row| puts "Read Agency: #{row[:agency_name]}" }
|
39
|
+
routes { |row| puts "Read Route: #{row[:route_long_name]}" }
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
|
-
GtfsReader.update
|
45
|
+
GtfsReader.update(:sample) # or GtfsReader.update_all!
|
45
46
|
```
|
46
47
|
|
47
|
-
Assuming that `http://localhost/sample-feed.zip` returns the
|
48
|
-
(https://developers.google.com/transit/gtfs/examples/gtfs-feed),
|
49
|
-
will print the following:
|
48
|
+
Assuming that `http://localhost/sample-feed.zip` returns the
|
49
|
+
[Example Feed](https://developers.google.com/transit/gtfs/examples/gtfs-feed),
|
50
|
+
this script will print the following:
|
50
51
|
|
51
52
|
```
|
52
53
|
Processing source with tag 4d9d3040c284f0581cd5620d5c131109...
|
@@ -73,25 +74,27 @@ GtfsReader.config do
|
|
73
74
|
sources do
|
74
75
|
sample do
|
75
76
|
feed_definition do
|
76
|
-
file
|
77
|
-
col
|
77
|
+
file(:drivers, required: true) do # for my_file.txt
|
78
|
+
col(:licence_number, required: true, unique: true)
|
78
79
|
|
79
80
|
# If the sex column contains "1", the symbol :male will be returned,
|
80
81
|
# otherwise :female will be returned
|
81
|
-
col :sex, &output_map(
|
82
|
+
col :sex, &output_map({ female: '1', male: '2' }, :unspecified)
|
82
83
|
|
83
84
|
# This will allow you to create a custom parser. Within the given
|
84
85
|
# block you can reference other columns in the current row by name.
|
85
86
|
col :name do |name|
|
86
87
|
case sex
|
87
|
-
|
88
|
-
|
89
|
-
|
88
|
+
when :female then "Ms. #{name}"
|
89
|
+
when :male then "Mr. #{name}"
|
90
|
+
else name
|
90
91
|
end
|
91
92
|
end
|
92
93
|
end
|
93
94
|
end
|
95
|
+
|
94
96
|
# ...
|
97
|
+
|
95
98
|
end
|
96
99
|
end
|
97
100
|
end
|
data/Rakefile
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require 'rubygems'
|
4
2
|
require 'bundler'
|
5
3
|
require 'rake'
|
@@ -11,8 +9,8 @@ require_relative 'lib/gtfs_reader/version'
|
|
11
9
|
begin
|
12
10
|
Bundler.setup :default, :development
|
13
11
|
rescue Bundler::BundlerError => e
|
14
|
-
|
15
|
-
|
12
|
+
warn e.message
|
13
|
+
warn 'Run `bundle install` to install missing gems'
|
16
14
|
exit e.status_code
|
17
15
|
end
|
18
16
|
|
@@ -20,15 +18,15 @@ Jeweler::Tasks.new do |gem|
|
|
20
18
|
gem.name = 'gtfs_reader'
|
21
19
|
gem.version = GtfsReader::Version.to_s
|
22
20
|
gem.homepage = 'http://github.com/sangster/gtfs_reader'
|
23
|
-
gem.license = 'GPL
|
21
|
+
gem.license = 'GPL-3.0'
|
24
22
|
gem.summary = 'Read General Transit Feed Specification zip files'
|
25
|
-
gem.description = <<-
|
23
|
+
gem.description = <<-DESC.strip.gsub(/\s+/, ' ')
|
26
24
|
Reads and parses zip files conforming to Google's GTFS spec. Such files can
|
27
25
|
take up quite a bit of memory when inflated, so this gem prefers to read
|
28
26
|
them as a stream of rows.
|
29
27
|
|
30
28
|
GTFS Spec: https://developers.google.com/transit/gtfs
|
31
|
-
|
29
|
+
DESC
|
32
30
|
gem.email = 'jon@ertt.ca'
|
33
31
|
gem.authors = ['Jon Sangster']
|
34
32
|
|
@@ -52,6 +50,5 @@ end
|
|
52
50
|
|
53
51
|
# RSpec
|
54
52
|
require 'rspec/core/rake_task'
|
55
|
-
RSpec::Core::RakeTask.new
|
53
|
+
RSpec::Core::RakeTask.new(:spec)
|
56
54
|
task default: :spec
|
57
|
-
|
@@ -1,23 +1,23 @@
|
|
1
1
|
module GtfsReader
|
2
2
|
class BulkFeedHandler
|
3
|
-
def initialize(bulk_size, args=[], &block)
|
3
|
+
def initialize(bulk_size, args = [], &block)
|
4
4
|
@bulk_size = bulk_size
|
5
5
|
@callbacks = {}
|
6
|
-
BulkFeedHandlerDsl.new(self).instance_exec
|
6
|
+
BulkFeedHandlerDsl.new(self).instance_exec(*args, &block)
|
7
7
|
end
|
8
8
|
|
9
9
|
def handler?(filename)
|
10
|
-
@callbacks.key?
|
10
|
+
@callbacks.key?(filename)
|
11
11
|
end
|
12
12
|
|
13
13
|
def handle_file(filename, reader)
|
14
|
-
unless @callbacks.key?
|
14
|
+
unless @callbacks.key?(filename)
|
15
15
|
Log.warn { "No handler registered for #{filename.to_s.red}.txt" }
|
16
16
|
return
|
17
17
|
end
|
18
18
|
|
19
|
-
calls = callbacks
|
20
|
-
calls[:before].call if calls.key?
|
19
|
+
calls = callbacks(filename)
|
20
|
+
calls[:before].call if calls.key?(:before)
|
21
21
|
read_row = calls[:read]
|
22
22
|
|
23
23
|
values = []
|
@@ -27,16 +27,16 @@ module GtfsReader
|
|
27
27
|
values << (read_row ? read_row.call(row) : row)
|
28
28
|
bulk_count += 1
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
next unless bulk_count == @bulk_size
|
31
|
+
|
32
|
+
total += bulk_count
|
33
|
+
calls[:bulk].call values, bulk_count, total, cols
|
34
|
+
bulk_count = 0
|
35
|
+
values = []
|
36
36
|
end
|
37
37
|
|
38
|
-
unless bulk_count
|
39
|
-
calls[:bulk].call
|
38
|
+
unless bulk_count.zero?
|
39
|
+
calls[:bulk].call(values, bulk_count, (total + bulk_count), cols)
|
40
40
|
end
|
41
41
|
nil
|
42
42
|
end
|
@@ -50,7 +50,7 @@ module GtfsReader
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def callback?(kind, filename)
|
53
|
-
@callbacks.key?
|
53
|
+
@callbacks.key?(filename) && @callbacks[filename].key?(kind)
|
54
54
|
end
|
55
55
|
|
56
56
|
private
|
@@ -65,30 +65,34 @@ module GtfsReader
|
|
65
65
|
@feed_handler = feed_handler
|
66
66
|
end
|
67
67
|
|
68
|
-
def method_missing(filename, *
|
69
|
-
BulkDsl.new(@feed_handler, filename).instance_exec
|
68
|
+
def method_missing(filename, *_args, &block)
|
69
|
+
BulkDsl.new(@feed_handler, filename).instance_exec(&block)
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
return if @feed_handler.callback?(:bulk, filename)
|
72
|
+
raise HandlerMissingError, "No bulk block for #{filename}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def respond_to_missing?(_name, _include_private = false)
|
76
|
+
true
|
74
77
|
end
|
75
78
|
end
|
76
79
|
|
77
80
|
class BulkDsl
|
78
81
|
def initialize(feed_handler, filename)
|
79
|
-
@feed_handler
|
82
|
+
@feed_handler = feed_handler
|
83
|
+
@filename = filename
|
80
84
|
end
|
81
85
|
|
82
86
|
def before(&block)
|
83
|
-
@feed_handler.create_callback
|
87
|
+
@feed_handler.create_callback(:before, @filename, block)
|
84
88
|
end
|
85
89
|
|
86
90
|
def read(&block)
|
87
|
-
@feed_handler.create_callback
|
91
|
+
@feed_handler.create_callback(:read, @filename, block)
|
88
92
|
end
|
89
93
|
|
90
94
|
def bulk(&block)
|
91
|
-
@feed_handler.create_callback
|
95
|
+
@feed_handler.create_callback(:bulk, @filename, block)
|
92
96
|
end
|
93
97
|
end
|
94
98
|
end
|
@@ -7,16 +7,16 @@ module GtfsReader
|
|
7
7
|
|
8
8
|
attr_reader :name
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
#
|
15
|
-
def initialize(name, opts={}, &parser)
|
10
|
+
# @param name [String] the name of the column
|
11
|
+
# @option opts [Boolean] :required (false) If this column is required to
|
12
|
+
# appear in the given file
|
13
|
+
# @option opts [Boolean] :unique (false) if values in this column need to
|
14
|
+
# be unique among all rows in the file.
|
15
|
+
def initialize(name, opts = {}, &parser)
|
16
16
|
@name = name
|
17
17
|
@parser = block_given? ? parser : IDENTITY_PARSER
|
18
18
|
|
19
|
-
@opts = { required: false, unique: false }.merge
|
19
|
+
@opts = { required: false, unique: false }.merge(opts || {})
|
20
20
|
end
|
21
21
|
|
22
22
|
def parser(&block)
|
@@ -24,30 +24,30 @@ module GtfsReader
|
|
24
24
|
@parser
|
25
25
|
end
|
26
26
|
|
27
|
-
|
27
|
+
# @return [Boolean] if this column is required to appear in the file
|
28
28
|
def required?
|
29
29
|
@opts[:required]
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
#
|
32
|
+
# @return [Boolean] if values in this column need to be unique among all
|
33
|
+
# rows in the file.
|
34
34
|
def unique?
|
35
35
|
@opts[:unique]
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
# @return [Boolean]
|
39
39
|
def parser?
|
40
40
|
parser != IDENTITY_PARSER
|
41
41
|
end
|
42
42
|
|
43
43
|
def to_s
|
44
|
-
opts = @opts.map do |key,value|
|
44
|
+
opts = @opts.map do |key, value|
|
45
45
|
case value
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
when true then key
|
47
|
+
when false, nil then nil
|
48
|
+
else "#{key}=#{value}"
|
49
49
|
end
|
50
|
-
end.reject
|
50
|
+
end.reject(&:nil?)
|
51
51
|
|
52
52
|
opts << 'has_parser' if parser?
|
53
53
|
|
@@ -15,36 +15,36 @@ module GtfsReader
|
|
15
15
|
col :agency_name, required: true
|
16
16
|
col :agency_url, required: true
|
17
17
|
col :agency_timezone, required: true
|
18
|
-
col :agency_id,
|
18
|
+
col :agency_id, unique: true
|
19
19
|
col :agency_lang
|
20
20
|
col :agency_phone
|
21
21
|
col :agency_fare_url
|
22
22
|
end
|
23
23
|
|
24
24
|
file :stops, required: true do
|
25
|
-
col :stop_id,
|
25
|
+
col :stop_id, required: true, unique: true
|
26
26
|
col :stop_code
|
27
|
-
col :stop_name,
|
27
|
+
col :stop_name, required: true
|
28
28
|
col :stop_desc
|
29
29
|
col :stop_lat, required: true
|
30
30
|
col :stop_lon, required: true
|
31
31
|
col :stop_url
|
32
32
|
col :stop_timezone
|
33
33
|
col :zone_id
|
34
|
-
col :location_type, &output_map(
|
34
|
+
col :location_type, &output_map({ station: '1' }, :stop)
|
35
35
|
col :parent_station
|
36
36
|
|
37
37
|
col :wheelchair_boarding do |val|
|
38
38
|
if parent_station
|
39
39
|
case val
|
40
|
-
when
|
41
|
-
when
|
40
|
+
when '2' then :no
|
41
|
+
when '1' then :yes
|
42
42
|
else :inherit
|
43
43
|
end
|
44
44
|
else
|
45
45
|
case val
|
46
|
-
when
|
47
|
-
when
|
46
|
+
when '2' then :no_vehicles
|
47
|
+
when '1' then :some_vehicles
|
48
48
|
else :unknown
|
49
49
|
end
|
50
50
|
end
|
@@ -56,16 +56,17 @@ module GtfsReader
|
|
56
56
|
col :route_short_name, required: true
|
57
57
|
col :route_long_name, required: true
|
58
58
|
col :route_desc
|
59
|
-
col :route_type,
|
60
|
-
&output_map(
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
59
|
+
col :route_type, required: true,
|
60
|
+
&output_map({
|
61
|
+
tram: '0',
|
62
|
+
subway: '1',
|
63
|
+
rail: '2',
|
64
|
+
bus: '3',
|
65
|
+
ferry: '4',
|
66
|
+
cable_car: '5',
|
67
|
+
gondola: '6',
|
68
|
+
funicular: '7'
|
69
|
+
}, :unknown)
|
69
70
|
col :route_url
|
70
71
|
col :route_color
|
71
72
|
col :route_text_color
|
@@ -73,17 +74,17 @@ module GtfsReader
|
|
73
74
|
end
|
74
75
|
|
75
76
|
file :trips, required: true do
|
76
|
-
col :trip_id,
|
77
|
+
col :trip_id, required: true, unique: true
|
77
78
|
col :trip_headsign
|
78
79
|
col :trip_short_name
|
79
80
|
col :trip_long_name
|
80
81
|
col :route_id, required: true
|
81
82
|
col :service_id, required: true
|
82
|
-
col :direction_id, &output_map(
|
83
|
+
col :direction_id, &output_map(primary: '0', opposite: '1')
|
83
84
|
col :block_id
|
84
85
|
col :shape_id
|
85
|
-
col :wheelchair_accessible, &output_map(
|
86
|
-
col :bikes_allowed, &output_map(
|
86
|
+
col :wheelchair_accessible, &output_map({ yes: '1', no: '2' }, :unknown)
|
87
|
+
col :bikes_allowed, &output_map({ yes: '1', no: '2' }, :unknown)
|
87
88
|
end
|
88
89
|
|
89
90
|
file :stop_times, required: true do
|
@@ -95,29 +96,31 @@ module GtfsReader
|
|
95
96
|
col :stop_headsign
|
96
97
|
|
97
98
|
col :pickup_type,
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
99
|
+
&output_map({
|
100
|
+
none: '1',
|
101
|
+
phone_agency: '2',
|
102
|
+
coordinate_with_driver: '3'
|
103
|
+
}, :regular)
|
102
104
|
|
103
105
|
col :drop_off_type,
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
106
|
+
&output_map({
|
107
|
+
none: '1',
|
108
|
+
phone_agency: '2',
|
109
|
+
coordinate_with_driver: '3'
|
110
|
+
}, :regular)
|
108
111
|
|
109
112
|
col :shape_dist_traveled
|
110
113
|
end
|
111
114
|
|
112
115
|
file :calendar, required: true do
|
113
116
|
col :service_id, required: true, unique: true
|
114
|
-
col :monday, required: true, &output_map(
|
115
|
-
col :tuesday, required: true, &output_map(
|
116
|
-
col :wednesday, required: true, &output_map(
|
117
|
-
col :thursday, required: true, &output_map(
|
118
|
-
col :friday, required: true, &output_map(
|
119
|
-
col :saturday, required: true, &output_map(
|
120
|
-
col :sunday, required: true, &output_map(
|
117
|
+
col :monday, required: true, &output_map(yes: '1', no: '0')
|
118
|
+
col :tuesday, required: true, &output_map(yes: '1', no: '0')
|
119
|
+
col :wednesday, required: true, &output_map(yes: '1', no: '0')
|
120
|
+
col :thursday, required: true, &output_map(yes: '1', no: '0')
|
121
|
+
col :friday, required: true, &output_map(yes: '1', no: '0')
|
122
|
+
col :saturday, required: true, &output_map(yes: '1', no: '0')
|
123
|
+
col :sunday, required: true, &output_map(yes: '1', no: '0')
|
121
124
|
col :start_date
|
122
125
|
col :end_date
|
123
126
|
end
|
@@ -126,7 +129,7 @@ module GtfsReader
|
|
126
129
|
col :service_id, required: true
|
127
130
|
col :date, required: true
|
128
131
|
col :exception_type, required: true,
|
129
|
-
|
132
|
+
&output_map(added: '1', removed: '2')
|
130
133
|
end
|
131
134
|
|
132
135
|
file :fare_attributes do
|
@@ -134,14 +137,14 @@ module GtfsReader
|
|
134
137
|
col :price, required: true
|
135
138
|
col :currency_type, required: true
|
136
139
|
col :payment_method, required: true,
|
137
|
-
|
140
|
+
&output_map(on_board: 0, before: 1)
|
138
141
|
col :transfers, required: true,
|
139
|
-
|
142
|
+
&output_map({ none: 0, once: 1, twice: 2 }, :unlimited)
|
140
143
|
col :transfer_duration
|
141
144
|
end
|
142
145
|
|
143
146
|
file :fare_rules do
|
144
|
-
col :fare_id,
|
147
|
+
col :fare_id, required: true
|
145
148
|
col :route_id
|
146
149
|
col :origin_id
|
147
150
|
col :destination_id
|
@@ -161,17 +164,18 @@ module GtfsReader
|
|
161
164
|
col :start_time, required: true
|
162
165
|
col :end_time, required: true
|
163
166
|
col :headway_secs, required: true
|
164
|
-
col :exact_times, &output_map(
|
167
|
+
col :exact_times, &output_map({ exact: 1 }, :inexact)
|
165
168
|
end
|
166
169
|
|
167
170
|
file :transfers do
|
168
171
|
col :from_stop_id, required: true
|
169
172
|
col :to_stop_id, required: true
|
170
173
|
col :transfer_type, required: true,
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
174
|
+
&output_map({
|
175
|
+
timed_transfer: 1,
|
176
|
+
minimum_time_required: 2,
|
177
|
+
impossible: 3
|
178
|
+
}, :recommended)
|
175
179
|
col :min_transfer_time
|
176
180
|
end
|
177
181
|
|