org.onebusaway.gtfs_realtime.trip_updates_producer_demo.GtfsRealtimeProviderImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.onebusaway.gtfs_realtime.trip_updates_producer_demo.GtfsRealtimeProviderImpl.java

Source

/**
 * Copyright (C) 2012 Google, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onebusaway.gtfs_realtime.trip_updates_producer_demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.onebusway.gtfs_realtime.exporter.GtfsRealtimeExporterModule;
import org.onebusway.gtfs_realtime.exporter.GtfsRealtimeLibrary;
import org.onebusway.gtfs_realtime.exporter.GtfsRealtimeMutableProvider;
import org.onebusway.gtfs_realtime.exporter.GtfsRealtimeProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.transit.realtime.GtfsRealtime.FeedEntity;
import com.google.transit.realtime.GtfsRealtime.FeedMessage;
import com.google.transit.realtime.GtfsRealtime.Position;
import com.google.transit.realtime.GtfsRealtime.TripDescriptor;
import com.google.transit.realtime.GtfsRealtime.TripUpdate;
import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent;
import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate;
import com.google.transit.realtime.GtfsRealtime.VehicleDescriptor;
import com.google.transit.realtime.GtfsRealtime.VehiclePosition;

/**
 * This class produces GTFS-realtime trip updates and vehicle positions by
 * periodically polling the custom SEPTA vehicle data API and converting the
 * resulting vehicle data into the GTFS-realtime format.
 * 
 * Since this class implements {@link GtfsRealtimeProvider}, it will
 * automatically be queried by the {@link GtfsRealtimeExporterModule} to export
 * the GTFS-realtime feeds to file or to host them using a simple web-server, as
 * configured by the client.
 * 
 * @author bdferris
 * 
 */
@Singleton
public class GtfsRealtimeProviderImpl {

    private static final Logger _log = LoggerFactory.getLogger(GtfsRealtimeProviderImpl.class);

    private ScheduledExecutorService _executor;

    private GtfsRealtimeMutableProvider _gtfsRealtimeProvider;

    private URL _url;

    /**
     * How often vehicle data will be downloaded, in seconds.
     */
    private int _refreshInterval = 30;

    @Inject
    public void setGtfsRealtimeProvider(GtfsRealtimeMutableProvider gtfsRealtimeProvider) {
        _gtfsRealtimeProvider = gtfsRealtimeProvider;
    }

    /**
     * @param url the URL for the SEPTA vehicle data API.
     */
    public void setUrl(URL url) {
        _url = url;
    }

    /**
     * @param refreshInterval how often vehicle data will be downloaded, in
     *          seconds.
     */
    public void setRefreshInterval(int refreshInterval) {
        _refreshInterval = refreshInterval;
    }

    /**
     * The start method automatically starts up a recurring task that periodically
     * downloads the latest vehicle data from the SEPTA vehicle stream and
     * processes them.
     */
    @PostConstruct
    public void start() {
        _log.info("starting GTFS-realtime service");
        _executor = Executors.newSingleThreadScheduledExecutor();
        _executor.scheduleAtFixedRate(new VehiclesRefreshTask(), 0, _refreshInterval, TimeUnit.SECONDS);
    }

    /**
     * The stop method cancels the recurring vehicle data downloader task.
     */
    @PreDestroy
    public void stop() {
        _log.info("stopping GTFS-realtime service");
        _executor.shutdownNow();
    }

    /****
     * Private Methods - Here is where the real work happens
     ****/

    /**
     * This method downloads the latest vehicle data, processes each vehicle in
     * turn, and create a GTFS-realtime feed of trip updates and vehicle positions
     * as a result.
     */
    private void refreshVehicles() throws IOException, JSONException {

        /**
         * We download the vehicle details as an array of JSON objects.
         */
        JSONArray vehicleArray = downloadVehicleDetails();

        /**
         * The FeedMessage.Builder is what we will use to build up our GTFS-realtime
         * feeds. We create a feed for both trip updates and vehicle positions.
         */
        FeedMessage.Builder tripUpdates = GtfsRealtimeLibrary.createFeedMessageBuilder();
        FeedMessage.Builder vehiclePositions = GtfsRealtimeLibrary.createFeedMessageBuilder();

        /**
         * We iterate over every JSON vehicle object.
         */
        for (int i = 0; i < vehicleArray.length(); ++i) {

            JSONObject obj = vehicleArray.getJSONObject(i);
            String vehicleId = obj.getString("VEHICLE");
            String tripId = obj.getString("TRIPID");
            // String blockId = obj.getString("BLOCKID");
            // String blockAbbr = obj.getString("BLOCK_ABBR");
            // String timepoint = obj.getString("TIMEPOINT");
            // String msgTime = obj.getString("MSGTIME");
            String route = obj.getString("ROUTE");
            String stopId = obj.getString("STOPID");
            double lat = obj.getDouble("LATITUDE");
            double lon = obj.getDouble("LONGITUDE");
            int delay = -1 * obj.getInt("ADHERENCE");

            /**
             * We construct a TripDescriptor and VehicleDescriptor, which will be used
             * in both trip updates and vehicle positions to identify the trip and
             * vehicle. Ideally, we would have a trip id to use for the trip
             * descriptor, but the SEPTA api doesn't include it, so we settle for a
             * route id instead.
             */
            TripDescriptor.Builder tripDescriptor = TripDescriptor.newBuilder();
            tripDescriptor.setRouteId(route);
            tripDescriptor.setTripId(tripId);
            VehicleDescriptor.Builder vehicleDescriptor = VehicleDescriptor.newBuilder();
            vehicleDescriptor.setId(vehicleId);

            /**
             * To construct our TripUpdate, we create a stop-time arrival event for
             * the next stop for the vehicle, with the specified arrival delay. We add
             * the stop-time update to a TripUpdate builder, along with the trip and
             * vehicle descriptors.
             */
            StopTimeEvent.Builder arrival = StopTimeEvent.newBuilder();
            arrival.setDelay(delay * 60);

            StopTimeUpdate.Builder stopTimeUpdate = StopTimeUpdate.newBuilder();
            stopTimeUpdate.setArrival(arrival);
            stopTimeUpdate.setStopId(stopId);

            TripUpdate.Builder tripUpdate = TripUpdate.newBuilder();
            tripUpdate.addStopTimeUpdate(stopTimeUpdate);
            tripUpdate.setTrip(tripDescriptor);
            tripUpdate.setVehicle(vehicleDescriptor);

            /**
             * Create a new feed entity to wrap the trip update and add it to the
             * GTFS-realtime trip updates feed.
             */
            FeedEntity.Builder tripUpdateEntity = FeedEntity.newBuilder();
            tripUpdateEntity.setId(vehicleId);
            tripUpdateEntity.setTripUpdate(tripUpdate);
            tripUpdates.addEntity(tripUpdateEntity);

            /**
             * To construct our VehiclePosition, we create a position for the vehicle.
             * We add the position to a VehiclePosition builder, along with the trip
             * and vehicle descriptors.
             */

            Position.Builder position = Position.newBuilder();
            position.setLatitude((float) lat);
            position.setLongitude((float) lon);

            VehiclePosition.Builder vehiclePosition = VehiclePosition.newBuilder();
            vehiclePosition.setPosition(position);
            vehiclePosition.setTrip(tripDescriptor);
            vehiclePosition.setVehicle(vehicleDescriptor);

            /**
             * Create a new feed entity to wrap the vehicle position and add it to the
             * GTFS-realtime vehicle positions feed.
             */
            FeedEntity.Builder vehiclePositionEntity = FeedEntity.newBuilder();
            vehiclePositionEntity.setId(vehicleId);
            vehiclePositionEntity.setVehicle(vehiclePosition);
            vehiclePositions.addEntity(vehiclePositionEntity);
        }

        /**
         * Build out the final GTFS-realtime feed messagse and save them.
         */
        _gtfsRealtimeProvider.setTripUpdates(tripUpdates.build());
        _gtfsRealtimeProvider.setVehiclePositions(vehiclePositions.build());

        _log.info("vehicles extracted: " + tripUpdates.getEntityCount());
    }

    /**
     * @return a JSON array parsed from the data pulled from the SEPTA vehicle
     *         data API.
     */
    private JSONArray downloadVehicleDetails() throws IOException, JSONException {
        URL testURL = new URL("http://developer.itsmarta.com/BRDRestService/BRDRestService.svc/GetAllBus");
        BufferedReader reader = new BufferedReader(new InputStreamReader(testURL.openStream()));
        JSONTokener tokener = new JSONTokener(reader);
        JSONArray vehiclesArray = new JSONArray(tokener);
        return vehiclesArray;
    }

    /**
     * Task that will download new vehicle data from the remote data source when
     * executed.
     */
    private class VehiclesRefreshTask implements Runnable {

        @Override
        public void run() {
            try {
                _log.info("refreshing vehicles");
                refreshVehicles();
            } catch (Exception ex) {
                _log.warn("Error in vehicle refresh task", ex);
            }
        }
    }

}