aboutsummaryrefslogtreecommitdiff
path: root/.idea/gtfs-realtime-book/ch-07-consuming-vehicle-positions.md
diff options
context:
space:
mode:
Diffstat (limited to '.idea/gtfs-realtime-book/ch-07-consuming-vehicle-positions.md')
-rw-r--r--.idea/gtfs-realtime-book/ch-07-consuming-vehicle-positions.md509
1 files changed, 0 insertions, 509 deletions
diff --git a/.idea/gtfs-realtime-book/ch-07-consuming-vehicle-positions.md b/.idea/gtfs-realtime-book/ch-07-consuming-vehicle-positions.md
deleted file mode 100644
index 1b745d0..0000000
--- a/.idea/gtfs-realtime-book/ch-07-consuming-vehicle-positions.md
+++ /dev/null
@@ -1,509 +0,0 @@
-## 7. Consuming Vehicle Positions
-
-Just like when consuming service alerts, you can loop over the
-`FeedEntity` objects returned from `getEntityList()` to process
-vehicle positions. If an entity contains a vehicle position, you can
-retrieve it using the `getVehicle()` method.
-
-```java
-for (FeedEntity entity : fm.getEntityList()) {
- if (entity.hasAlert()) {
- VehiclePosition vp = entity.getVehicle();
- processVehiclePosition(vp);
- }
-}
-```
-
-You can then process the returned `VehiclePosition` object to extract
-the details of the vehicle position.
-
-### Timestamp
-
-One of the provided values is a timestamp reading of when the vehicle
-position reading was taken.
-
-```java
-if (vp.hasTimestamp()) {
- Date timestamp = new Date(vp.getTimestamp() * 1000);
-
-}
-```
-
-***Note:** The value is multiplied by 1,000 because the java.util.Date
-class accepts milliseconds, whereas GTFS-realtime uses whole seconds.*
-
-This value is useful because the age of a reading can dictate how the
-data is interpreted. For example, if your latest reading was only thirty
-seconds earlier, your users would realize it is very recent and
-therefore is probably quite accurate. On the other hand, if the latest
-reading was ten minutes earlier, they would see it had not updated
-recently and may therefore not be completely accurate.
-
-The other way this value is useful is for determining whether to store
-this new vehicle position. If your previous reading for the same vehicle
-has the same timestamp, you can ignore this update, as nothing has
-changed.
-
-### Geographic Location
-
-A `VehiclePosition` object contains a `Position` object, which
-contains a vehicle's latitude and longitude, and may also include other
-useful information such as its bearing and speed.
-
-```java
-public static void processVehiclePosition(VehiclePosition vp) {
- if (vp.hasPosition()) {
- Position position = vp.getPosition();
-
- if (position.hasLatitude() && position.hasLongitude()) {
- float latitude = position.getLatitude();
- float longitude = position.getLongitude();
-
- // ...
- }
-
- // ...
- }
-}
-```
-
-Even though the `Position` element of a `VehiclePosition` is
-required (according to the specification), checking for it explicitly
-means you can handle its omission gracefully. Remember: when consuming
-GTFS-realtime data you are likely to be relying on a third-party data
-provider who may or may not follow the specification correctly.
-
-Likewise, the latitude and longitude are also required, but it is still
-prudent to ensure they are included. These values are treated as any
-floating-point numbers, so technically they may not be valid geographic
-coordinates.
-
-A basic check to ensure the coordinates are valid is to ensure the
-latitude is between -90 and 90 and the longitude is between -180 and
-180.
-
-```java
-float latitude = position.getLatitude();
-float longitude = position.getLongitude();
-
-if (Math.abs(latitude) <= 90 && Math.abs(longitude) <= 180) {
- // Valid coordinate
-
-}
-else {
- // Invalid coordinate
-
-}
-```
-
-A more advanced check would be to determine a bounding box of the data
-provider's entire public transportation network from the corresponding
-GTFS feed's `stops.txt`. You would then check that all received
-coordinates are within or near the bounding box.
-
-***Note:** The important lesson to take from this is that a GTFS-realtime
-feed may appear to adhere to the specification, but you should still
-perform your own sanity checks on received data.*
-
-In addition to the latitude and longitude, you can also retrieve a
-vehicle's speed, bearing and odometer reading.
-
-```java
-if (position.hasBearing()) {
- float bearing = position.getBearing();
-
- // Degrees from 0-359. 0 is North, 90 is East, 180 is South, 270 is West
-
-}
-
-if (position.hasOdometer()) {
- double odometer = position.getOdometer();
-
- // Meters
-}
-
-if (position.hasSpeed()) {
- float speed = position.getSpeed();
-
- // Meters per second
-}
-```
-
-### Trip Information
-
-In order to associate a vehicle position with a particular trip from the
-corresponding GTFS feed, vehicle positions may include a trip
-descriptor.
-
-***Note:** The trip descriptor is declared as optional in the
-GTFS-realtime specification. Realistically, it will be hard to provide
-value to end-users without knowing which trip the position corresponds
-to. At the very least, you would need to know the route (which can be
-specified via the trip descriptor).*
-
-Just like with service alerts (covered in the previous chapter), there
-are a number of values you can retrieve from a trip descriptor to
-determine which trip a vehicle position belongs to. The following
-listing demonstrates how to access this data:
-
-```java
-if (vp.hasTrip()) {
- TripDescriptor trip = vp.getTrip();
-
- if (trip.hasTripId()) {
- String tripId = trip.getTripId();
-
- }
-
- if (trip.hasRouteId()) {
- String routeId = trip.getRouteId();
-
- }
-
- if (trip.hasStartDate()) {
- String startDate = trip.getStartDate();
-
- }
-
- if (trip.hasStartTime()) {
- String startTime = trip.getStartTime();
-
- }
-
- if (trip.hasScheduleRelationship()) {
- ScheduleRelationship sr = trip.getScheduleRelationship();
-
- }
-}
-```
-
-### Vehicle Identifiers
-
-There are a number of values available in a vehicle position entity by
-which to identify a vehicle. You can access an internal identifier (not
-for public display), a label (such as a vehicle number painted on to a
-vehicle), or a license plate, as shown in the following listing:
-
-```java
-if (vp.hasVehicle()) {
- VehicleDescriptor vehicle = vp.getVehicle();
-
- if (vehicle.hasId()) {
- String id = vehicle.getId();
-
- }
-
- if (vehicle.hasLabel()) {
- String label = vehicle.getLabel();
-
- }
-
- if (vehicle.hasLicensePlate()) {
- String licensePlate = vehicle.getLicensePlate();
-
- }
-}
-```
-
-The vehicle descriptor and the values contained within are all optional.
-In the case where this information is not available, you can use the
-trip descriptor provided with each vehicle position to match up vehicle
-positions across multiple updates.
-
-Being able to match up the trip and/or vehicle reliably over subsequent
-updates allows you reliably track the ongoing position changes for a
-particular vehicle. For instance, if you wanted to animate the vehicle
-moving on a map as new positions were received, you would need to know
-that each update corresponds to a particular vehicle.
-
-### Current Stop
-
-Each vehicle position record can be associated with a single stop. If
-specified, this stop must appear in the corresponding GTFS feed.
-
-The stop can be identified either by the `stop_id` value, or by using
-the `current_stop_sequence` value. If you use the stop sequence, the
-stop can be determined by finding the corresponding record in the GTFS
-feed's `stop_times.txt` file.
-
-```java
-if (vp.hasStopId()) {
- String stopId = vp.getStopId();
-
-}
-
-if (vp.hasCurrentStopSequence()) {
- int sequence = vp.getCurrentStopSequence();
-
-}
-```
-
-***Note:** It is possible for the same stop to be visited multiple times
-in a single trip (consider a loop service, although there are other
-instances when this may also happen). The stop sequence value can be
-useful to disambiguate this case.*
-
-On its own, knowing the stop has no meaning without context. The
-`current_status` field provides this context, indicating that the
-vehicle is either:
-
-* In transit to the stop (it is the next stop but the vehicle is not yet nearby)
-* About to arrive at the stop
-* Currently stopped at the stop.
-
-According to the GTFS-realtime specification, you can only make use of
-`current_status` if the stop sequence is specified.
-
-The following code shows how you can retrieve and check the value of the
-stop status.
-
-```java
-if (vp.hasCurrentStopSequence()) {
- int sequence = vp.getCurrentStopSequence();
-
- if (vp.hasCurrentStatus()) {
- VehicleStopStatus status = vp.getCurrentStatus();
-
- switch (status.getNumber()) {
- case VehicleStopStatus.IN_TRANSIT_TO_VALUE:
- // ...
-
- case VehicleStopStatus.INCOMING_AT_VALUE:
- // ...
-
- case VehicleStopStatus.STOPPED_AT_VALUE:
- // ...
-
- }
- }
-}
-```
-
-### Congestion Levels
-
-The other two values that may be included with a vehicle position relate
-to the congestion inside and outside of the vehicle.
-
-The `congestion_level` value indicates the flow of traffic. This value does
-not indicate whether or not the vehicle is running to schedule, since congestion
-levels are typically accounted for in scheduling.
-
-The following code shows how you can check the congestion level value:
-
-```java
-if (vp.hasCongestionLevel()) {
- CongestionLevel congestion = vp.getCongestionLevel();
-
- switch (congestion.getNumber()) {
- case CongestionLevel.UNKNOWN_CONGESTION_LEVEL_VALUE:
- // ...
-
- case CongestionLevel.RUNNING_SMOOTHLY_VALUE:
- // ...
-
- case CongestionLevel.STOP_AND_GO_VALUE:
- // ...
-
- case CongestionLevel.SEVERE_CONGESTION_VALUE:
- // ...
-
- case CongestionLevel.CONGESTION_VALUE:
- // ...
-
- }
-}
-```
-
-The `occupancy_status` value indicates how full the vehicle currently
-is. This can be useful to present to your users so they know what to
-expect before the vehicle arrives. For instance:
-
-* A person with a broken leg may not want to travel on a standing
- room-only bus
-* Someone traveling late at night might prefer a taxi over an empty
- train for safety reasons
-* If a bus is full and not accepting passengers, someone may stay at
- home for longer until a bus with seats is coming by.
-
-You can check the occupancy status of a vehicle as follows:
-
-```java
-if (vp.hasOccupancyStatus()) {
- OccupancyStatus status = vp.getOccupancyStatus();
-
- switch (status.getNumber()) {
- case OccupancyStatus.EMPTY_VALUE:
- // ...
-
- case OccupancyStatus.MANY_SEATS_AVAILABLE_VALUE:
- // ...
-
- case OccupancyStatus.FEW_SEATS_AVAILABLE_VALUE:
- // ...
-
- case OccupancyStatus.STANDING_ROOM_ONLY_VALUE:
- // ...
-
- case OccupancyStatus.CRUSHED_STANDING_ROOM_ONLY_VALUE:
- // ...
-
- case OccupancyStatus.FULL_VALUE:
- // ...
-
- case OccupancyStatus.NOT_ACCEPTING_PASSENGERS_VALUE:
- // ...
-
- }
-}
-```
-
-### Determining a Vehicle's Bearing
-
-One of the values that can be specified in a GTFS-realtime vehicle
-position update is the bearing of the vehicle being reported. This value
-indicates either the direction the vehicle is facing, or the direction
-towards the next stop.
-
-Ideally, the bearing contains the actual direction that the vehicle is
-facing, not the direction to the next stop, since it is possible for
-this value to be inaccurate. For example, if a Northbound vehicle is
-stopped at a stop (that is, directly beside it), then the calculated
-bearing would indicate the vehicle was facing East, not North.
-
-***Note:** This example assumes that the vehicle is in a country that
-drives on the right-hand side of the road.*
-
-There are several ways to calculate a vehicle's bearing if it is not
-specified in a GTFS-realtime feed:
-
-* Determine the direction towards the next stop
-* Determine the direction using a previous vehicle position reading
-* A combination of the above.
-
-***Note:** The GTFS-realtime specification states that feed providers
-should not include the bearing if it is calculated using previous
-positions. This is because consumers of the feed can calculate this, as
-shown in the remainder of this chapter.*
-
-### Bearing to Next Stop
-
-In order to determine the bearing from the current location to the next
-stop, there are two values you need:
-
-* **Vehicle position.** This is provided in the `latitude` and
- `longitude` fields of the vehicle position.
-* **Position of the next stop.** This is provided by the `stop_id`
- or `current_stop_sequence` fields of the vehicle position.
-
-***Note:** Many GTFS-realtime vehicle position feeds do not include
-information about the next stop, and consequently this technique will
-not work in those instances. If this is the case, using the previous
-reading to determine the bearing would be used, as shown later in
-*Bearing From Previous Position*.*
-
-The following formula is used to determine the bearing between the
-starting location and the next stop:
-
-```
-θ = atan2( sin Δλ ⋅ cos φ2 , cos φ1 ⋅ sin φ2 − sin φ1 ⋅ cos φ2 ⋅ cos Δλ )
-```
-
-In this equation, the starting point is indicated by *1*, while the next
-stop is represented by *2*. Latitude is represented by *φ*, while
-longitude is represented by *λ*. For example, *φ2* means the latitude of
-the next stop.
-
-The resultant value *θ* is the bearing between the two points in
-*radians*, and must then be converted to degrees (0-360).
-
-This equation can be represented in Java as follows. It accepts that
-latitude and longitude of two points in degrees, and returns the bearing
-in degrees.
-
-```java
-public static double calculateBearing(double lat1Deg, double lon1Deg, double lat2Deg, double lon2Deg) {
-
- // Convert all degrees to radians
- double lat1 = Math.toRadians(lat1Deg);
- double lon1 = Math.toRadians(lon2Deg);
- double lat2 = Math.toRadians(lat2Deg);
- double lon2 = Math.toRadians(lon2Deg);
-
- // sin Δλ ⋅ cos φ2
- double y = Math.sin(lon2 - lon1) * Math.cos(lat2);
-
- // cos φ1 ⋅ sin φ2 − sin φ1 ⋅ cos φ2 ⋅ cos Δλ
- double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
-
- // Calculate the bearing in radians
- double bearingRad = Math.atan2(y, x);
-
- // Convert radians to degrees
- double bearingDeg = Math.toDegrees(bearingRad);
-
- // Ensure x is positive, in the range of 0 <= x < 360
- if (bearingDeg < 0) {
- bearingDeg += 360;
- }
-
- return bearingDeg;
-}
-```
-
-One thing to be aware of when using the next stop to determine bearing
-is the `current_status` value of the vehicle position (if specified).
-If the value is `STOPPED_AT`, then the calculated angle might be
-significantly wrong, since the vehicle is likely directly next to the
-stop. In this instance, you should either use the next stop, or
-determine the bearing from the previous position reading.
-
-### Bearing From Previous Position
-
-Similar to calculating a vehicle's bearing using the direction to the
-next stop, you can also use a previous reading to calculate the
-vehicle's direction.
-
-The following diagram shows two readings for the same vehicle, as well
-as the calculated bearing from the first point to the second.
-
-![Bearing From Previous Position](images/bearing-from-position.png)
-
-To calculate the bearing in this way, you need the following data:
-
-* **Current vehicle position.** This is provided in the `latitude`
- and `longitude` fields of the vehicle position.
-* **Previous vehicle position.** This should be the most recent
- vehicle position recorded prior to receiving the current vehicle
- position. Additionally, this position must represent a different
- location to the current position. If a vehicle is stationary for
- several minutes, you may receive several locations for the vehicle
- with the same coordinates.
-
-***Note:** In the case of multiple readings at the same location, you
-should use a minimum distance threshold to decide whether or not the
-location is the same. For instance, you may decide that the two previous
-locations must be more than, say, 10 meters to use it for comparison.*
-
-It is also necessary to check the age of the previous reading. If the
-previous reading is more than a minute or two old, it is likely that the
-vehicle has travelled far enough to render the calculated bearing
-meaningless.
-
-### Combination
-
-These two strategies are useful to approximate a vehicle's bearing if
-it is not specified in a vehicle position message, but they are still
-reliant on certain data being available.
-
-The first technique needs to know the next stop, while the second needs
-a previous vehicle position reading.
-
-Your algorithm to determine a vehicle's position could be as follows:
-
-* Use the provided bearing value in the vehicle position if this
- available.
-* Otherwise, if you have a recent previous reading, calculate the
- direction using that and the current reading.
-* Otherwise, if the next stop is known, show the bearing of the
- vehicle towards the stop.
-