gtfs-realtime 0.3.0 → 0.4.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 +4 -2
- data/bin/console +2 -1
- data/gtfs-realtime.gemspec +2 -1
- data/lib/gtfs/realtime.rb +180 -1
- data/lib/gtfs/realtime/calendar_date.rb +1 -1
- data/lib/gtfs/realtime/configuration.rb +8 -9
- data/lib/gtfs/realtime/migrations/001_create_gtfs_tables.rb +81 -0
- data/lib/gtfs/realtime/model.rb +8 -0
- data/lib/gtfs/realtime/service_alert.rb +1 -1
- data/lib/gtfs/realtime/shape.rb +1 -8
- data/lib/gtfs/realtime/stop.rb +8 -16
- data/lib/gtfs/realtime/stop_time.rb +4 -4
- data/lib/gtfs/realtime/stop_time_update.rb +12 -4
- data/lib/gtfs/realtime/trip.rb +6 -6
- data/lib/gtfs/realtime/trip_update.rb +2 -2
- data/lib/gtfs/realtime/vehicle_position.rb +2 -2
- data/lib/gtfs/realtime/version.rb +1 -1
- metadata +19 -5
- data/lib/gtfs/realtime/bootstrap.rb +0 -178
- data/lib/gtfs/realtime/database.rb +0 -140
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4fb01e9aa3b3d57def85c0ebdbda36acbc4c9ab
|
4
|
+
data.tar.gz: d3bbf13907cd3b09a0e55cada1aa16dae4ec09c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfc85354de0492acac1c60bf85a1d9085b170f502f57b3f32364b06eaa8fca59237bfee4f51b00e8ea8cac52a6d49ccce18ac90f1724aa1cbad80e106276934c
|
7
|
+
data.tar.gz: ff2b51ad9471a0f11ca788363f8968446504625b6f5a0538302dbe362d37e7954705014c5f8373d4c366f9dbb683d14a68418bc03f9fd2755a308e9d95288003
|
data/README.md
CHANGED
@@ -30,14 +30,16 @@ GTFS::Realtime.configure do |config|
|
|
30
30
|
config.trip_updates_feed = "http://realtime.ripta.com:81/api/tripupdates"
|
31
31
|
config.vehicle_positions_feed = "http://realtime.ripta.com:81/api/vehiclepositions"
|
32
32
|
config.service_alerts_feed = "http://realtime.ripta.com:81/api/servicealerts"
|
33
|
-
config.
|
33
|
+
config.database_url = "sqlite3:////Users/rofreg/database.db"
|
34
|
+
# leave database_url unset to use your existing ActiveRecord DB, or
|
35
|
+
# set it to `nil` to use an in-memory SQLite database
|
34
36
|
end
|
35
37
|
|
36
38
|
# After calling 'configure', the gem loads all relevant GTFS info into a database.
|
37
39
|
# This may take some time (up to a minute) depending on the size of the input data.
|
38
40
|
# By default, gtfs-realtime uses an in-memory database, which requires reloading all
|
39
41
|
# data from scratch on each launch. If you'd like to use a persistent database instead,
|
40
|
-
# set 'config.
|
42
|
+
# set 'config.database_url' above, and include a scheme/protocol path for the DB type
|
41
43
|
# that you would like to use. gtfs-realtime will generate the relevant tables.
|
42
44
|
|
43
45
|
@nearby = GTFS::Realtime::Stop.nearby(41.834521, -71.396906)
|
data/bin/console
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "bundler/setup"
|
4
4
|
require "gtfs/realtime"
|
5
5
|
require "sqlite3"
|
6
|
+
require "uri"
|
6
7
|
|
7
8
|
# Load the RIPTA feed as an example, with a sqlite3 database
|
8
9
|
GTFS::Realtime.configure do |config|
|
@@ -10,7 +11,7 @@ GTFS::Realtime.configure do |config|
|
|
10
11
|
config.trip_updates_feed = "http://realtime.ripta.com:81/api/tripupdates"
|
11
12
|
config.vehicle_positions_feed = "http://realtime.ripta.com:81/api/vehiclepositions"
|
12
13
|
config.service_alerts_feed = "http://realtime.ripta.com:81/api/servicealerts"
|
13
|
-
config.
|
14
|
+
config.database_url = "sqlite3:///#{URI.escape(File.expand_path("../../database.db", __FILE__))}"
|
14
15
|
end
|
15
16
|
|
16
17
|
def reload!
|
data/gtfs-realtime.gemspec
CHANGED
data/lib/gtfs/realtime.rb
CHANGED
@@ -1,8 +1,22 @@
|
|
1
1
|
require "google/transit/gtfs-realtime.pb"
|
2
2
|
require "gtfs"
|
3
|
-
require "
|
3
|
+
require "active_record"
|
4
|
+
require "bulk_insert"
|
5
|
+
require "gtfs/gtfs_gem_patch"
|
4
6
|
|
5
7
|
require "gtfs/realtime/configuration"
|
8
|
+
require "gtfs/realtime/model"
|
9
|
+
require "gtfs/realtime/calendar_date"
|
10
|
+
require "gtfs/realtime/route"
|
11
|
+
require "gtfs/realtime/service_alert"
|
12
|
+
require "gtfs/realtime/shape"
|
13
|
+
require "gtfs/realtime/stop"
|
14
|
+
require "gtfs/realtime/stop_time"
|
15
|
+
require "gtfs/realtime/stop_time_update"
|
16
|
+
require "gtfs/realtime/trip"
|
17
|
+
require "gtfs/realtime/trip_update"
|
18
|
+
require "gtfs/realtime/vehicle_position"
|
19
|
+
require "gtfs/realtime/version"
|
6
20
|
|
7
21
|
module GTFS
|
8
22
|
class Realtime
|
@@ -17,9 +31,174 @@ module GTFS
|
|
17
31
|
def configure
|
18
32
|
yield(configuration)
|
19
33
|
|
34
|
+
run_migrations
|
20
35
|
load_static_feed!
|
21
36
|
refresh_realtime_feed!
|
22
37
|
end
|
38
|
+
|
39
|
+
def load_static_feed!(force: false)
|
40
|
+
return if !force && GTFS::Realtime::Route.count > 0
|
41
|
+
|
42
|
+
static_data = GTFS::Source.build(@configuration.static_feed)
|
43
|
+
return unless static_data
|
44
|
+
|
45
|
+
GTFS::Realtime::Model.transaction do
|
46
|
+
GTFS::Realtime::CalendarDate.delete_all
|
47
|
+
GTFS::Realtime::CalendarDate.bulk_insert(values:
|
48
|
+
static_data.calendar_dates.collect do |calendar_date|
|
49
|
+
{
|
50
|
+
service_id: calendar_date.service_id.strip,
|
51
|
+
date: Date.strptime(calendar_date.date, "%Y%m%d"),
|
52
|
+
exception_type: calendar_date.exception_type
|
53
|
+
}
|
54
|
+
end
|
55
|
+
)
|
56
|
+
|
57
|
+
GTFS::Realtime::Route.delete_all
|
58
|
+
GTFS::Realtime::Route.bulk_insert(:id, :short_name, :long_name, :url, values:
|
59
|
+
static_data.routes.collect do |route|
|
60
|
+
{
|
61
|
+
id: route.id.strip,
|
62
|
+
short_name: route.short_name,
|
63
|
+
long_name: route.long_name,
|
64
|
+
url: route.url
|
65
|
+
}
|
66
|
+
end
|
67
|
+
)
|
68
|
+
|
69
|
+
GTFS::Realtime::Shape.delete_all
|
70
|
+
GTFS::Realtime::Shape.bulk_insert(:id, :sequence, :latitude, :longitude, values:
|
71
|
+
static_data.shapes.collect do |shape|
|
72
|
+
{
|
73
|
+
id: shape.id.strip,
|
74
|
+
sequence: shape.pt_sequence,
|
75
|
+
latitude: shape.pt_lat.to_f,
|
76
|
+
longitude: shape.pt_lon.to_f
|
77
|
+
}
|
78
|
+
end
|
79
|
+
)
|
80
|
+
|
81
|
+
GTFS::Realtime::Stop.delete_all
|
82
|
+
GTFS::Realtime::Stop.bulk_insert(:id, :name, :latitude, :longitude, values:
|
83
|
+
static_data.stops.collect do |stop|
|
84
|
+
{
|
85
|
+
id: stop.id.strip,
|
86
|
+
name: stop.name.strip,
|
87
|
+
latitude: stop.lat.to_f,
|
88
|
+
longitude: stop.lon.to_f
|
89
|
+
}
|
90
|
+
end
|
91
|
+
)
|
92
|
+
|
93
|
+
GTFS::Realtime::StopTime.delete_all
|
94
|
+
GTFS::Realtime::StopTime.bulk_insert(values:
|
95
|
+
static_data.stop_times.collect do |stop_time|
|
96
|
+
{
|
97
|
+
stop_id: stop_time.stop_id.strip,
|
98
|
+
trip_id: stop_time.trip_id.strip,
|
99
|
+
arrival_time: stop_time.arrival_time,
|
100
|
+
departure_time: stop_time.departure_time,
|
101
|
+
stop_sequence: stop_time.stop_sequence.to_i
|
102
|
+
}
|
103
|
+
end
|
104
|
+
)
|
105
|
+
|
106
|
+
GTFS::Realtime::Trip.delete_all
|
107
|
+
GTFS::Realtime::Trip.bulk_insert(:id, :headsign, :route_id, :service_id, :shape_id, :direction_id, values:
|
108
|
+
static_data.trips.collect do |trip|
|
109
|
+
{
|
110
|
+
id: trip.id.strip,
|
111
|
+
headsign: trip.headsign.strip,
|
112
|
+
route_id: trip.route_id.strip,
|
113
|
+
service_id: trip.service_id.strip,
|
114
|
+
shape_id: trip.shape_id.strip,
|
115
|
+
direction_id: trip.direction_id
|
116
|
+
}
|
117
|
+
end
|
118
|
+
)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def refresh_realtime_feed!
|
123
|
+
trip_updates = get_entities(@configuration.trip_updates_feed)
|
124
|
+
vehicle_positions = get_entities(@configuration.vehicle_positions_feed)
|
125
|
+
service_alerts = get_entities(@configuration.service_alerts_feed)
|
126
|
+
|
127
|
+
GTFS::Realtime::Model.transaction do
|
128
|
+
GTFS::Realtime::TripUpdate.delete_all
|
129
|
+
GTFS::Realtime::TripUpdate.bulk_insert(:id, :trip_id, :route_id, values:
|
130
|
+
trip_updates.collect do |trip_update|
|
131
|
+
{
|
132
|
+
id: trip_update.id.strip,
|
133
|
+
trip_id: trip_update.trip_update.trip.trip_id.strip,
|
134
|
+
route_id: trip_update.trip_update.trip.route_id.strip
|
135
|
+
}
|
136
|
+
end
|
137
|
+
)
|
138
|
+
|
139
|
+
GTFS::Realtime::StopTimeUpdate.delete_all
|
140
|
+
GTFS::Realtime::StopTimeUpdate.bulk_insert(values:
|
141
|
+
trip_updates.collect do |trip_update|
|
142
|
+
trip_update.trip_update.stop_time_update.collect do |stop_time_update|
|
143
|
+
{
|
144
|
+
trip_update_id: trip_update.id.strip,
|
145
|
+
stop_id: stop_time_update.stop_id.strip,
|
146
|
+
arrival_delay: stop_time_update.arrival ? stop_time_update.arrival.delay : nil,
|
147
|
+
arrival_time: stop_time_update.arrival ? Time.at(stop_time_update.arrival.time) : nil,
|
148
|
+
departure_delay: stop_time_update.departure ? stop_time_update.departure.delay : nil,
|
149
|
+
departure_time: stop_time_update.departure ? Time.at(stop_time_update.departure.time) : nil,
|
150
|
+
}
|
151
|
+
end
|
152
|
+
end.flatten
|
153
|
+
)
|
154
|
+
|
155
|
+
GTFS::Realtime::VehiclePosition.delete_all
|
156
|
+
GTFS::Realtime::VehiclePosition.bulk_insert(values:
|
157
|
+
vehicle_positions.collect do |vehicle|
|
158
|
+
{
|
159
|
+
trip_id: vehicle.vehicle.trip.trip_id.strip,
|
160
|
+
stop_id: vehicle.vehicle.stop_id.strip,
|
161
|
+
latitude: vehicle.vehicle.position.latitude.to_f,
|
162
|
+
longitude: vehicle.vehicle.position.longitude.to_f,
|
163
|
+
bearing: vehicle.vehicle.position.bearing.to_f,
|
164
|
+
timestamp: Time.at(vehicle.vehicle.timestamp)
|
165
|
+
}
|
166
|
+
end
|
167
|
+
)
|
168
|
+
|
169
|
+
GTFS::Realtime::ServiceAlert.delete_all
|
170
|
+
GTFS::Realtime::ServiceAlert.bulk_insert(values:
|
171
|
+
service_alerts.collect do |service_alert|
|
172
|
+
{
|
173
|
+
stop_id: service_alert.alert.informed_entity.first.stop_id.strip,
|
174
|
+
header_text: service_alert.alert.header_text.translation.first.text,
|
175
|
+
description_text: service_alert.alert.description_text.translation.first.text,
|
176
|
+
start_time: Time.at(service_alert.alert.active_period.first.start),
|
177
|
+
end_time: Time.at(service_alert.alert.active_period.first.end)
|
178
|
+
}
|
179
|
+
end
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def get_entities(path)
|
187
|
+
return [] if path.nil?
|
188
|
+
|
189
|
+
if File.exists?(path)
|
190
|
+
data = File.open(path, 'r'){|f| f.read}
|
191
|
+
else
|
192
|
+
data = Net::HTTP.get(URI.parse(path))
|
193
|
+
end
|
194
|
+
feed = Transit_realtime::FeedMessage.decode(data)
|
195
|
+
feed.entity # array of entities
|
196
|
+
end
|
197
|
+
|
198
|
+
def run_migrations
|
199
|
+
ActiveRecord::Migration.verbose = false
|
200
|
+
ActiveRecord::Migrator.migrate(File.expand_path("../realtime/migrations", __FILE__))
|
201
|
+
end
|
23
202
|
end
|
24
203
|
end
|
25
204
|
end
|
@@ -1,17 +1,16 @@
|
|
1
1
|
module GTFS
|
2
2
|
class Realtime
|
3
3
|
class Configuration
|
4
|
-
attr_accessor :static_feed, :trip_updates_feed, :vehicle_positions_feed, :service_alerts_feed, :
|
4
|
+
attr_accessor :static_feed, :trip_updates_feed, :vehicle_positions_feed, :service_alerts_feed, :database_url
|
5
5
|
|
6
|
-
def
|
7
|
-
@
|
6
|
+
def database_url=(new_path)
|
7
|
+
@database_url = new_path
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
require 'gtfs/realtime/bootstrap'
|
9
|
+
if @database_url
|
10
|
+
ActiveRecord::Base.establish_connection(@database_url)
|
11
|
+
else
|
12
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
13
|
+
end
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class CreateGtfsTables < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :gtfs_realtime_calendar_dates, id: false do |t|
|
4
|
+
t.string :service_id, index: true
|
5
|
+
t.date :date
|
6
|
+
t.integer :exception_type
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table :gtfs_realtime_routes do |t|
|
10
|
+
t.string :short_name
|
11
|
+
t.string :long_name
|
12
|
+
t.string :url
|
13
|
+
end
|
14
|
+
change_column :gtfs_realtime_routes, :id, :string
|
15
|
+
|
16
|
+
create_table :gtfs_realtime_shapes, id: false do |t|
|
17
|
+
t.string :id # NOT unique
|
18
|
+
t.integer :sequence
|
19
|
+
t.float :latitude
|
20
|
+
t.float :longitude
|
21
|
+
|
22
|
+
t.index [:id, :sequence]
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table :gtfs_realtime_stops do |t|
|
26
|
+
t.string :name
|
27
|
+
t.float :latitude
|
28
|
+
t.float :longitude
|
29
|
+
end
|
30
|
+
change_column :gtfs_realtime_stops, :id, :string
|
31
|
+
|
32
|
+
create_table :gtfs_realtime_stop_times, id: false do |t|
|
33
|
+
t.string :trip_id, index: true
|
34
|
+
t.string :stop_id, index: true
|
35
|
+
t.string :arrival_time
|
36
|
+
t.string :departure_time
|
37
|
+
t.integer :stop_sequence
|
38
|
+
end
|
39
|
+
|
40
|
+
create_table :gtfs_realtime_trips do |t|
|
41
|
+
t.string :headsign
|
42
|
+
t.string :route_id, index: true
|
43
|
+
t.string :service_id
|
44
|
+
t.string :shape_id
|
45
|
+
t.integer :direction_id
|
46
|
+
end
|
47
|
+
change_column :gtfs_realtime_trips, :id, :string
|
48
|
+
|
49
|
+
create_table :gtfs_realtime_trip_updates do |t|
|
50
|
+
t.string :trip_id
|
51
|
+
t.string :route_id
|
52
|
+
end
|
53
|
+
change_column :gtfs_realtime_trip_updates, :id, :string
|
54
|
+
|
55
|
+
create_table :gtfs_realtime_stop_time_updates, id: false do |t|
|
56
|
+
t.string :trip_update_id, index: true
|
57
|
+
t.string :stop_id, index: true
|
58
|
+
t.integer :arrival_delay
|
59
|
+
t.timestamp :arrival_time
|
60
|
+
t.integer :departure_delay
|
61
|
+
t.timestamp :departure_time
|
62
|
+
end
|
63
|
+
|
64
|
+
create_table :gtfs_realtime_vehicle_positions, id: false do |t|
|
65
|
+
t.string :trip_id, index: true
|
66
|
+
t.string :stop_id, index: true
|
67
|
+
t.float :latitude
|
68
|
+
t.float :longitude
|
69
|
+
t.float :bearing
|
70
|
+
t.timestamp :timestamp
|
71
|
+
end
|
72
|
+
|
73
|
+
create_table :gtfs_realtime_service_alerts, id: false do |t|
|
74
|
+
t.string :stop_id, index: true
|
75
|
+
t.string :header_text
|
76
|
+
t.text :description_text
|
77
|
+
t.timestamp :start_time
|
78
|
+
t.timestamp :end_time
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/gtfs/realtime/shape.rb
CHANGED
@@ -1,14 +1,7 @@
|
|
1
1
|
module GTFS
|
2
2
|
class Realtime
|
3
3
|
class Shape < GTFS::Realtime::Model
|
4
|
-
|
5
|
-
def ordered_by_sequence
|
6
|
-
order(:sequence)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
# order results by sequence by default
|
11
|
-
set_dataset(self.ordered_by_sequence)
|
4
|
+
scope :ordered, -> { order(sequence: :ASC) }
|
12
5
|
end
|
13
6
|
end
|
14
7
|
end
|
data/lib/gtfs/realtime/stop.rb
CHANGED
@@ -5,24 +5,16 @@ module GTFS
|
|
5
5
|
class Stop < GTFS::Realtime::Model
|
6
6
|
include GTFS::Realtime::Nearby
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
[:trips, :id, :route_id]
|
16
|
-
]
|
17
|
-
many_through_many :active_routes, class: GTFS::Realtime::Route, through: [
|
18
|
-
[:stop_time_updates, :stop_id, :trip_update_id],
|
19
|
-
[:trip_updates, :id, :route_id]
|
20
|
-
]
|
8
|
+
has_many :service_alerts
|
9
|
+
has_many :stop_times
|
10
|
+
has_many :stop_time_updates
|
11
|
+
has_many :trips, through: :stop_times
|
12
|
+
has_many :trip_updates, through: :stop_times
|
13
|
+
has_many :routes, through: :trips
|
14
|
+
has_many :active_routes, through: :trip_updates, source: :route
|
21
15
|
|
22
16
|
def stop_times_schedule_for(date)
|
23
|
-
|
24
|
-
self_with_eager_loads = GTFS::Realtime::Stop.where(id: id).eager(stop_times: {trip: [:calendar_dates, :route, :shapes]}).all.first
|
25
|
-
self_with_eager_loads.stop_times.select{|st| st.trip.active?(Date.today)}.sort_by{|st| st.departure_time}
|
17
|
+
stop_times.includes(trip: [:calendar_dates, :route, :shapes]).select{|st| st.trip.active?(date)}.sort_by{|st| st.departure_time}
|
26
18
|
end
|
27
19
|
|
28
20
|
def stop_times_for_today
|
@@ -1,8 +1,9 @@
|
|
1
1
|
module GTFS
|
2
2
|
class Realtime
|
3
3
|
class StopTime < GTFS::Realtime::Model
|
4
|
-
|
5
|
-
|
4
|
+
belongs_to :trip
|
5
|
+
belongs_to :trip_update, primary_key: :trip_id, foreign_key: :trip_id
|
6
|
+
belongs_to :stop
|
6
7
|
|
7
8
|
attr_accessor :actual_arrival_time, :actual_arrival_delay, :actual_departure_time, :actual_departure_delay
|
8
9
|
|
@@ -38,7 +39,6 @@ module GTFS
|
|
38
39
|
private
|
39
40
|
|
40
41
|
def self.parse_time(time, date = Date.today)
|
41
|
-
# TODO: handle case where date != Date.today
|
42
42
|
day_adjustment = 0
|
43
43
|
hour = time[0...2].to_i
|
44
44
|
|
@@ -48,7 +48,7 @@ module GTFS
|
|
48
48
|
time[0...2] = (hour % 24).to_s.rjust(2, '0')
|
49
49
|
end
|
50
50
|
|
51
|
-
Time.parse(time) + day_adjustment * 60 * 60 * 24
|
51
|
+
Time.parse("#{date} #{time}").in_time_zone(Time.zone) + day_adjustment * 60 * 60 * 24
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -1,10 +1,18 @@
|
|
1
1
|
module GTFS
|
2
2
|
class Realtime
|
3
3
|
class StopTimeUpdate < GTFS::Realtime::Model
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
belongs_to :stop
|
5
|
+
belongs_to :trip_update
|
6
|
+
has_one :trip, through: :trip_update
|
7
|
+
has_one :route, through: :trip_update
|
8
|
+
|
9
|
+
def arrival_time
|
10
|
+
super ? super.in_time_zone(Time.zone) : nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def departure_time
|
14
|
+
super ? super.in_time_zone(Time.zone) : nil
|
15
|
+
end
|
8
16
|
end
|
9
17
|
end
|
10
18
|
end
|
data/lib/gtfs/realtime/trip.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
module GTFS
|
2
2
|
class Realtime
|
3
3
|
class Trip < GTFS::Realtime::Model
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
belongs_to :route
|
5
|
+
has_many :stop_times
|
6
|
+
has_many :stops, through: :stop_times
|
7
|
+
has_many :calendar_dates, primary_key: :service_id, foreign_key: :service_id
|
8
|
+
has_many :shapes, primary_key: :shape_id, foreign_key: :id
|
8
9
|
|
9
10
|
def active?(date)
|
10
|
-
|
11
|
-
calendar_dates.find{|cd| cd.exception_type == GTFS::Realtime::CalendarDate::ADDED && cd.date == date}
|
11
|
+
calendar_dates.where(exception_type: GTFS::Realtime::CalendarDate::ADDED, date: date).any?
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gtfs-realtime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Laughlin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -109,7 +109,21 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: activerecord
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: bulk_insert
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
129
|
- - ">="
|
@@ -143,10 +157,10 @@ files:
|
|
143
157
|
- gtfs-realtime.gemspec
|
144
158
|
- lib/gtfs/gtfs_gem_patch.rb
|
145
159
|
- lib/gtfs/realtime.rb
|
146
|
-
- lib/gtfs/realtime/bootstrap.rb
|
147
160
|
- lib/gtfs/realtime/calendar_date.rb
|
148
161
|
- lib/gtfs/realtime/configuration.rb
|
149
|
-
- lib/gtfs/realtime/
|
162
|
+
- lib/gtfs/realtime/migrations/001_create_gtfs_tables.rb
|
163
|
+
- lib/gtfs/realtime/model.rb
|
150
164
|
- lib/gtfs/realtime/nearby.rb
|
151
165
|
- lib/gtfs/realtime/route.rb
|
152
166
|
- lib/gtfs/realtime/service_alert.rb
|
@@ -1,178 +0,0 @@
|
|
1
|
-
require "gtfs/gtfs_gem_patch"
|
2
|
-
require "gtfs/realtime/calendar_date"
|
3
|
-
require "gtfs/realtime/route"
|
4
|
-
require "gtfs/realtime/service_alert"
|
5
|
-
require "gtfs/realtime/shape"
|
6
|
-
require "gtfs/realtime/stop"
|
7
|
-
require "gtfs/realtime/stop_time"
|
8
|
-
require "gtfs/realtime/stop_time_update"
|
9
|
-
require "gtfs/realtime/trip"
|
10
|
-
require "gtfs/realtime/trip_update"
|
11
|
-
require "gtfs/realtime/vehicle_position"
|
12
|
-
require "gtfs/realtime/version"
|
13
|
-
|
14
|
-
module GTFS
|
15
|
-
class Realtime
|
16
|
-
# This is a singleton object, so everything will be on the class level
|
17
|
-
class << self
|
18
|
-
def load_static_feed!(force: false)
|
19
|
-
return if !force && GTFS::Realtime::Route.count > 0
|
20
|
-
|
21
|
-
static_data = GTFS::Source.build(@configuration.static_feed)
|
22
|
-
return unless static_data
|
23
|
-
|
24
|
-
GTFS::Realtime::Model.db.transaction do
|
25
|
-
GTFS::Realtime::CalendarDate.dataset.delete
|
26
|
-
GTFS::Realtime::CalendarDate.multi_insert(
|
27
|
-
static_data.calendar_dates.collect do |calendar_date|
|
28
|
-
{
|
29
|
-
service_id: calendar_date.service_id.strip,
|
30
|
-
date: Date.strptime(calendar_date.date, "%Y%m%d"),
|
31
|
-
exception_type: calendar_date.exception_type
|
32
|
-
}
|
33
|
-
end
|
34
|
-
)
|
35
|
-
|
36
|
-
GTFS::Realtime::Route.dataset.delete
|
37
|
-
GTFS::Realtime::Route.multi_insert(
|
38
|
-
static_data.routes.collect do |route|
|
39
|
-
{
|
40
|
-
id: route.id.strip,
|
41
|
-
short_name: route.short_name,
|
42
|
-
long_name: route.long_name,
|
43
|
-
url: route.url
|
44
|
-
}
|
45
|
-
end
|
46
|
-
)
|
47
|
-
|
48
|
-
GTFS::Realtime::Shape.dataset.delete
|
49
|
-
GTFS::Realtime::Shape.multi_insert(
|
50
|
-
static_data.shapes.collect do |shape|
|
51
|
-
{
|
52
|
-
id: shape.id.strip,
|
53
|
-
sequence: shape.pt_sequence,
|
54
|
-
latitude: shape.pt_lat.to_f,
|
55
|
-
longitude: shape.pt_lon.to_f
|
56
|
-
}
|
57
|
-
end
|
58
|
-
)
|
59
|
-
|
60
|
-
GTFS::Realtime::Stop.dataset.delete
|
61
|
-
GTFS::Realtime::Stop.multi_insert(
|
62
|
-
static_data.stops.collect do |stop|
|
63
|
-
{
|
64
|
-
id: stop.id.strip,
|
65
|
-
name: stop.name.strip,
|
66
|
-
latitude: stop.lat.to_f,
|
67
|
-
longitude: stop.lon.to_f
|
68
|
-
}
|
69
|
-
end
|
70
|
-
)
|
71
|
-
|
72
|
-
GTFS::Realtime::StopTime.dataset.delete
|
73
|
-
GTFS::Realtime::StopTime.multi_insert(
|
74
|
-
static_data.stop_times.collect do |stop_time|
|
75
|
-
{
|
76
|
-
stop_id: stop_time.stop_id.strip,
|
77
|
-
trip_id: stop_time.trip_id.strip,
|
78
|
-
arrival_time: stop_time.arrival_time,
|
79
|
-
departure_time: stop_time.departure_time,
|
80
|
-
stop_sequence: stop_time.stop_sequence.to_i
|
81
|
-
}
|
82
|
-
end
|
83
|
-
)
|
84
|
-
|
85
|
-
GTFS::Realtime::Trip.dataset.delete
|
86
|
-
GTFS::Realtime::Trip.multi_insert(
|
87
|
-
static_data.trips.collect do |trip|
|
88
|
-
{
|
89
|
-
id: trip.id.strip,
|
90
|
-
headsign: trip.headsign.strip,
|
91
|
-
route_id: trip.route_id.strip,
|
92
|
-
service_id: trip.service_id.strip,
|
93
|
-
shape_id: trip.shape_id.strip,
|
94
|
-
direction_id: trip.direction_id
|
95
|
-
}
|
96
|
-
end
|
97
|
-
)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def refresh_realtime_feed!
|
102
|
-
trip_updates = get_entities(@configuration.trip_updates_feed)
|
103
|
-
vehicle_positions = get_entities(@configuration.vehicle_positions_feed)
|
104
|
-
service_alerts = get_entities(@configuration.service_alerts_feed)
|
105
|
-
|
106
|
-
GTFS::Realtime::Model.db.transaction do
|
107
|
-
GTFS::Realtime::TripUpdate.dataset.delete
|
108
|
-
GTFS::Realtime::TripUpdate.multi_insert(
|
109
|
-
trip_updates.collect do |trip_update|
|
110
|
-
{
|
111
|
-
id: trip_update.id.strip,
|
112
|
-
trip_id: trip_update.trip_update.trip.trip_id.strip,
|
113
|
-
route_id: trip_update.trip_update.trip.route_id.strip
|
114
|
-
}
|
115
|
-
end
|
116
|
-
)
|
117
|
-
|
118
|
-
GTFS::Realtime::StopTimeUpdate.dataset.delete
|
119
|
-
GTFS::Realtime::StopTimeUpdate.multi_insert(
|
120
|
-
trip_updates.collect do |trip_update|
|
121
|
-
trip_update.trip_update.stop_time_update.collect do |stop_time_update|
|
122
|
-
{
|
123
|
-
trip_update_id: trip_update.id.strip,
|
124
|
-
stop_id: stop_time_update.stop_id.strip,
|
125
|
-
arrival_delay: stop_time_update.arrival ? stop_time_update.arrival.delay : nil,
|
126
|
-
arrival_time: stop_time_update.arrival ? Time.at(stop_time_update.arrival.time) : nil,
|
127
|
-
departure_delay: stop_time_update.departure ? stop_time_update.departure.delay : nil,
|
128
|
-
departure_time: stop_time_update.departure ? Time.at(stop_time_update.departure.time) : nil,
|
129
|
-
}
|
130
|
-
end
|
131
|
-
end.flatten
|
132
|
-
)
|
133
|
-
|
134
|
-
GTFS::Realtime::VehiclePosition.dataset.delete
|
135
|
-
GTFS::Realtime::VehiclePosition.multi_insert(
|
136
|
-
vehicle_positions.collect do |vehicle|
|
137
|
-
{
|
138
|
-
trip_id: vehicle.vehicle.trip.trip_id.strip,
|
139
|
-
stop_id: vehicle.vehicle.stop_id.strip,
|
140
|
-
latitude: vehicle.vehicle.position.latitude.to_f,
|
141
|
-
longitude: vehicle.vehicle.position.longitude.to_f,
|
142
|
-
bearing: vehicle.vehicle.position.bearing.to_f,
|
143
|
-
timestamp: Time.at(vehicle.vehicle.timestamp)
|
144
|
-
}
|
145
|
-
end
|
146
|
-
)
|
147
|
-
|
148
|
-
GTFS::Realtime::ServiceAlert.dataset.delete
|
149
|
-
GTFS::Realtime::ServiceAlert.multi_insert(
|
150
|
-
service_alerts.collect do |service_alert|
|
151
|
-
{
|
152
|
-
stop_id: service_alert.alert.informed_entity.first.stop_id.strip,
|
153
|
-
header_text: service_alert.alert.header_text.translation.first.text,
|
154
|
-
description_text: service_alert.alert.description_text.translation.first.text,
|
155
|
-
start_time: Time.at(service_alert.alert.active_period.first.start),
|
156
|
-
end_time: Time.at(service_alert.alert.active_period.first.end)
|
157
|
-
}
|
158
|
-
end
|
159
|
-
)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
private
|
164
|
-
|
165
|
-
def get_entities(path)
|
166
|
-
return [] if path.nil?
|
167
|
-
|
168
|
-
if File.exists?(path)
|
169
|
-
data = File.open(path, 'r'){|f| f.read}
|
170
|
-
else
|
171
|
-
data = Net::HTTP.get(URI.parse(path))
|
172
|
-
end
|
173
|
-
feed = Transit_realtime::FeedMessage.decode(data)
|
174
|
-
feed.entity # array of entities
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
@@ -1,140 +0,0 @@
|
|
1
|
-
module GTFS
|
2
|
-
class Realtime
|
3
|
-
class Database
|
4
|
-
class << self
|
5
|
-
attr_writer :database_path
|
6
|
-
|
7
|
-
def path=(new_path)
|
8
|
-
@path = new_path
|
9
|
-
|
10
|
-
# This script sets up an in-memory DB so that it can be used by this gem.
|
11
|
-
# It also extends Sequel::Model so that Sequel may be used independently by
|
12
|
-
# the parent project if desired.
|
13
|
-
db = Sequel.connect(new_path || "sqlite://")
|
14
|
-
|
15
|
-
# Set up all database tables
|
16
|
-
db.create_table? :gtfs_realtime_calendar_dates do
|
17
|
-
String :service_id
|
18
|
-
Date :date
|
19
|
-
Integer :exception_type
|
20
|
-
|
21
|
-
index :service_id
|
22
|
-
end
|
23
|
-
|
24
|
-
db.create_table? :gtfs_realtime_routes do
|
25
|
-
String :id, primary_key: true
|
26
|
-
String :short_name
|
27
|
-
String :long_name
|
28
|
-
String :url
|
29
|
-
|
30
|
-
index :id
|
31
|
-
end
|
32
|
-
|
33
|
-
db.create_table? :gtfs_realtime_shapes do
|
34
|
-
String :id
|
35
|
-
Integer :sequence
|
36
|
-
Float :latitude
|
37
|
-
Float :longitude
|
38
|
-
|
39
|
-
index :id
|
40
|
-
end
|
41
|
-
|
42
|
-
db.create_table? :gtfs_realtime_stops do
|
43
|
-
String :id, primary_key: true
|
44
|
-
String :name
|
45
|
-
Float :latitude
|
46
|
-
Float :longitude
|
47
|
-
|
48
|
-
index :id
|
49
|
-
end
|
50
|
-
|
51
|
-
db.create_table? :gtfs_realtime_stop_times do
|
52
|
-
String :trip_id
|
53
|
-
String :stop_id
|
54
|
-
String :arrival_time
|
55
|
-
String :departure_time
|
56
|
-
Integer :stop_sequence
|
57
|
-
|
58
|
-
index :trip_id
|
59
|
-
index :stop_id
|
60
|
-
end
|
61
|
-
|
62
|
-
db.create_table? :gtfs_realtime_trips do
|
63
|
-
String :id, primary_key: true
|
64
|
-
String :headsign
|
65
|
-
String :route_id
|
66
|
-
String :service_id
|
67
|
-
String :shape_id
|
68
|
-
Integer :direction_id
|
69
|
-
|
70
|
-
index :id
|
71
|
-
index :route_id
|
72
|
-
end
|
73
|
-
|
74
|
-
db.create_table? :gtfs_realtime_trip_updates do
|
75
|
-
String :id, primary_key: true
|
76
|
-
String :trip_id
|
77
|
-
String :route_id
|
78
|
-
|
79
|
-
index :id
|
80
|
-
end
|
81
|
-
|
82
|
-
db.create_table? :gtfs_realtime_stop_time_updates do
|
83
|
-
String :trip_update_id
|
84
|
-
String :stop_id
|
85
|
-
Integer :arrival_delay
|
86
|
-
Time :arrival_time
|
87
|
-
Integer :departure_delay
|
88
|
-
Time :departure_time
|
89
|
-
|
90
|
-
index :trip_update_id
|
91
|
-
index :stop_id
|
92
|
-
end
|
93
|
-
|
94
|
-
db.create_table? :gtfs_realtime_vehicle_positions do
|
95
|
-
String :trip_id
|
96
|
-
String :stop_id
|
97
|
-
Float :latitude
|
98
|
-
Float :longitude
|
99
|
-
Float :bearing
|
100
|
-
Time :timestamp
|
101
|
-
|
102
|
-
index :trip_id
|
103
|
-
index :stop_id
|
104
|
-
end
|
105
|
-
|
106
|
-
db.create_table? :gtfs_realtime_service_alerts do
|
107
|
-
String :stop_id
|
108
|
-
String :header_text
|
109
|
-
Text :description_text
|
110
|
-
Time :start_time
|
111
|
-
Time :end_time
|
112
|
-
|
113
|
-
index :stop_id
|
114
|
-
end
|
115
|
-
|
116
|
-
# Set up all gtfs-realtime models to use this database
|
117
|
-
model_classes.each do |model_class|
|
118
|
-
model_class.db = db
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def model_classes
|
123
|
-
ObjectSpace.each_object(::Class).select{|klass| klass <= GTFS::Realtime::Model}
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
# If we have not defined our model parent class yet, initialize it.
|
131
|
-
if !defined?(GTFS::Realtime::Model)
|
132
|
-
GTFS::Realtime::Model = Class.new(Sequel::Model)
|
133
|
-
GTFS::Realtime::Model.plugin :many_through_many
|
134
|
-
|
135
|
-
class GTFS::Realtime::Model
|
136
|
-
def self.implicit_table_name
|
137
|
-
"gtfs_realtime_#{super}".to_sym
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|