gtfs_reader 1.2.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|