aboutsummaryrefslogtreecommitdiff
path: root/gtfs-realtime-book/ch-05-protocol-buffers.md
blob: 2059a630921b1cb721ec11a66decdc629d9c1e9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
## 5. Protocol Buffers

The previous chapters have included extracts from GTFS-realtime feeds in
a human-readable format. This data is actually represented using a data
format called *Protocol Buffers*.

Developed by Google and initially released in 2008, Protocol Buffers are
a way of serializing structured data into a format which is intended to
be smaller and faster than XML.

Note: Remember, if you are writing a transit-related mobile app,
GTFS-realtime feeds are not intended to be consumed directly by mobile
devices due to the large amount of data transferred. Rather, you will
need an intermediate server to read the feed from the provider then
serve only relevant data to the mobile devices running your app.

Even though it looks similar to JSON data, the human-readable version of
a protocol buffer is not intended to be manually parsed. Instead, data
is extracted from a protocol buffer using native language (such as Java,
C++ or Python).

Note: Although the Protocol Buffers application can generate code in
Java, C++ or Python, all code examples in this book will be in Java.

For example, assume you have written a Java program that reads and
parses a GTFS-realtime service alerts feed (shown later in this chapter,
and in the next chapter).

In order to consume a GTFS-realtime feed provided by a transit agency
such as TriMet or MBTA, your workflow would look similar to the
following diagram:

![GTFS-realtime Consumption](images/GTFS-realtime-consumption.png)

When a transit agency or data provider want to publish a GTFS-realtime
feed, their process would be similar, except instead of reading the feed
every 15 seconds, they would write a new protocol buffer data file every
15 seconds using data received from their vehicles.

***Note:** *Chapter 11. Publishing GTFS-realtime Feeds* will
show you how to create a GTFS-realtime feed using Protocol Buffers. In
order to do so, you will need to install Protocol Buffers as
demonstrated in this chapter.*

### Installing Protocol Buffers

In order to generate code to read or write GTFS-realtime feeds in your
native language, you first need to install Protocol Buffers. Once
installed, it is capable of generating code for Java, C++ or Python.

This section shows you how to download and build the `protoc`
command-line tool on a UNIX or Linux-based system. These instructions
were derived from installing Protocol Buffers on Mac OS X 10.10.

First, download and extract the Protocol Buffers source code. At the
time of writing, the current version is 2.6.1.

```
$ curl -L \
    https://github.com/google/protobuf/releases/download/v2.6.1/protobuf-2.6.1.tar.gz \
    -o protobuf-2.6.1.tar.gz

$ tar -zxf protobuf-2.6.1.tar.gz

$ cd protobuf-2.6.1
```

***Note:** Visit <https://developers.google.com/protocol-buffers/> and
click the "Download" link to find the latest version. The following
instructions should still work for subsequent versions.*

Next, compile the source files using `make`. First run the
`configure` script to build the `Makefile`, then run `make`.

```
$ ./configure && make
```

***Note:** Separating the commands by && means that make will only run if
`./configure` exits successfully.*

Once compilation is complete, you can verify the build by running **make
check**. You can then install it globally on your system using **make
install**. If you do not want to install it globally, you can run
`protoc` directly from the **./src** directory instead.

```
$ make check

$ make install
```

Next verify that it has been successfully built and installed by running
the `protoc` command. The output should be "Missing input file."

```
$ protoc

Missing input file.
```

The next section will show you how to generate Java files using
`protoc` and the `gtfs-realtime.proto` file.

### Introduction to gtfs-realtime.proto

In order to generate source code files that can read a protocol buffer,
you need a `.proto` input file. Typically you won't need to create or
modify `.proto` files yourself, it is useful to have a basic
understanding of how they work.

A `.proto` file contains a series of instructions that defines the
structure of the data. In the case of GTFS-realtime, there is a file
called `gtfs-realtime.proto` which contains the structure for each of
the messages available (service alerts, vehicle positions and trip
updates).

The following is an extract from `gtfs-realtime.proto` for the
`VehiclePosition` message.

**Note: **The `TripDescriptor` and `VehicleDescriptor` types
referenced in this extract also have declarations, which are not
included here.

```
message VehiclePosition {
    optional TripDescriptor trip = 1;
    optional Position position = 2;
    optional uint32 current_stop_sequence = 3;
    
    enum VehicleStopStatus {
        INCOMING_AT = 0;
        STOPPED_AT = 1;
        IN_TRANSIT_TO = 2;
    }
    
    optional VehicleStopStatus current_status = 4 [default = IN_TRANSIT_TO];
    optional uint64 timestamp = 5;
    
    enum CongestionLevel {
        UNKNOWN_CONGESTION_LEVEL = 0;
        RUNNING_SMOOTHLY = 1;
        STOP_AND_GO = 2;
        CONGESTION = 3;
        SEVERE_CONGESTION = 4;
    }
    
    optional CongestionLevel congestion_level = 6;
    optional string stop_id = 7;
    optional VehicleDescriptor vehicle = 8;
    extensions 1000 to 1999;
}
```

Ignoring the numerical values assigned to each field (they aren't
likely to be relevant because you never directly refer to them), you can
see how the structure is the same as the specification covered earlier
in this book for vehicle positions.

Each field in a protocol buffer has a unique value assigned to it. This
value is used internally when encoding or decoding each field in
GTFS-realtime feed. If there is additional data to be represented in a
feed, the values between 1000 and 1999 are reserved for extensions.
*Chapter 10. GTFS-realtime Extensions* shows how extensions in
GTFS-realtime work.

### Compiling gtfs-realtime.proto

The next step towards consuming a GTFS-realtime feed is to compile the
`gtfs-realtime.proto` file into Java code using `protoc`. In order
to do this, you must have already created a Java project ahead of time.
`protoc` will generate the files and incorporate them directly into
your project's source tree.

These instructions assume you have created your Java project in
**/path/to/gtfsrt** and that you will download the
`gtfs-realtime.proto` file to **/path/to/protobuf**.

First, download the `gtfs-realtime.proto` file.

```
$ cd /path/to/protobuf

$ curl \
    https://developers.google.com/transit/gtfs-realtime/gtfs-realtime.proto \
    -o gtfs-realtime.proto
```

***Note:** If this URL is no longer current when you read this, you can
find the updated location at
<https://developers.google.com/transit/gtfs-realtime/>.*

In order to use `protoc`, you must specify the **--proto_path**
argument as the directory in which `gtfs-realtime.proto` resides.
Additionally, you must specify the full path to the
`gtfs-realtime.proto` file.

Typically, in a Java project your source files will reside in a
directory called `src` within the project directory. This directory
must be specified in the **--java_out** argument.

The full command to run is as follows:

```
$ protoc \
    --proto_path=/path/to/protobuf \
    --java_out=/path/to/gtfsrt/src \
    /path/to/protobuf/gtfs-realtime.proto
```

If this command runs successfully there will be no output to screen, but
there will be newly-created files in your source tree. There should now
be a **./com/google** directory in your source tree, and a package
called `com.google.transit.realtime`.

### Adding the Protocol Buffers Library

Before you can use this new package, you must add the Protocol Buffers
library to your Java project. You can either compile these files into a
Java archive (using the instructions in
**./protobuf-2.6.**1**/java/README.txt**), or you can add the Java
source files directly to your project as follows:

```
$ cd protobuf-2.6.1

$ cp -R ./java/src/main/java/com/google/protobuf \
    /path/to/gtfsrt/src/com/google/
```

If you now try to build your project, an error will occur due to a
missing package called `DescriptorProtos`. You can add this to your
project using the following command:

```
$ cd protobuf-2.6.1

$ protoc --java_out=/path/to/gtfsrt/src \
    --proto_path=./src \
    ./src/google/protobuf/descriptor.proto
```

Your project should now build successfully, meaning you can use the
`com.google.transit.realtime` package to read data from a
GTFS-realtime feed.

### Reading Data From a GTFS-realtime Feed

To read the data from a GTFS-realtime feed, you need to build a
`FeedMessage` object. The simplest way to do this is by opening an
`InputStream` for the URL of the GTFS-realtime feed.

The following code builds a `FeedMessage` for the vehicle positions
feed of the MBTA in Boston.

***Note:** To simplify the code listings in this book, package imports
are not included. All classes used are either standard Java classes, or
classes generated by Protocol Buffers.*

```java
public class YourClass {
    public void loadFeed() throws IOException {

        URL url = new URL("http://developer.mbta.com/lib/gtrtfs/Vehicles.pb");

        InputStream is = url.openStream();

        FeedMessage fm = FeedMessage.parseFrom(is);

        is.close();

        // ...
    }
}
```

A `FeedMessage` object contains zero or more entities, each of which
is either a service alert, a vehicle position, or a trip update. You can
retrieve a list of entities using **getEntityList()**, then loop over
them as follows:

```java
public class YourClass {
    public void loadFeed() throws IOException {

    // ...

    for (FeedEntity entity : fm.getEntityList()) {
        // Process the entity here
    }

    // ...

    }
}
```

Since many of the fields in GTFS-realtime are optional, you need to
check for the presence of the field you want to use before trying to
retrieve it. This is achieved using `hasFieldName()`. You can
then retrieve it using `getFieldName()`.

In the case of `FeedEntity`, you need to check which of the
GTFS-realtime messages are available. For instance, to check if the
entity contains a service alert, you would call `entity.hasAlert()`.
If the call to `hasAlert()` returns `true`, you can retrieve it
using `entity.getAlert()`.

The following code shows how to access the various entities.

```java
public class YourClass {
    public void loadFeed() throws IOException {
        // ...

        for (FeedEntity entity : fm.getEntityList()) {

            if (entity.hasAlert()) {
                Alert alert = entity.getAlert();

                // Process the alert here
            }

            if (entity.hasVehicle()) {
                VehiclePosition vp = entity.getVehicle();

                // Process the vehicle position here
            }

            if (entity.hasTripUpdate()) {
                TripUpdate tu = entity.getTripUpdate();

                // Process the trip update here
            }
        }

        // ...

    }
}
```

The next three chapters will show you how to process the `Alert`, `VehiclePosition` and `TripUpdate` objects.

### Outputting Human-Readable GTFS-realtime Feeds

Earlier chapters included human-readable extracts from GTFS-realtime
feeds. Although plain-text GTFS-realtime feeds are not designed to be
parsed directly, they can be useful for quickly determining the kinds of
data available within a feed.

All objects in a GTFS-realtime feed can be output using the
`TextFormat` class in the `protobuf` package. Passing the object to
**printToString()** will generate a human-readable version of the
GTFS-realtime element.

For instance, you can output an entire feed as follows:

```java
FeedMessage fm = ...;

String output = TextFormat.printToString(fm);

System.out.println(output);
```

Or you can output individual entities:

```java
for (FeedEntity entity : fm.getEntityList()) {
    String output = TextFormat.printToString(entity);
    
    System.out.println(output);
}
```

Alternatively, you can call the `toString()` method on these objects
to generate the same output.