The MARTA API

Real-Time Bus Tracker

The MARTA Bus Real-time Data RESTful Web Service provides information about the real-time location and schedule adherence for active buses on the MARTA system.

Opened to developers in October 2012, the feed works in conjunction with schedule data provided in MARTA's GTFS feed.

What Does That Mean For Us?

Through the API we are allowed access to data concerning all buses currently on the road wherever the MARTA (bus) system runs.

This is a sample of the data we are pulling in:

{"ADHERENCE":"-4", "BLOCKID":"348", "BLOCK_ABBR":"32-7", "DIRECTION":"Southbound", "LATITUDE":"33.6891136", "LONGITUDE":"-84.3370348", "MSGTIME":"12\/2\/2014 7:07:26 AM", "ROUTE":"32", "STOPID":"151060", "TIMEPOINT":"Metro Transitional Center", "TRIPID":"4347502","VEHICLE":"2403"}

We

Data

Look at all the data we get to work with:

ADHERENCE: how late/early the bus is (in minutes)?

DIRECTION: which cardinal direction is the bus headed?

LATITUDE/LONGITUDE: exact coordinates of the bus!

ROUTE: the route number, obvi.

TIMEPOINT: the bus' next stop.

VEHICLE: the ID number of that particular bus.

What We'll Do With the Data

We can create a Rails app that will take an address (or approximate) from the user, and let them know if there is a bus anywhere nearby.

We'll be using the Geocoder gem to pinpoint the User's longitude/latitude.

First Things First...

Let's create a new project, with one resource: Location

$ rails new marta_near_me

$ cd marta_near_me

$ rails g scaffold Location address:string city:string latitude:float longitude:float

And don't forget to...

Remember, for Geocoder to work we need the attributes of latitude and longitude, both as floats.

Views We'll Use

Let's make this app all about the New and Show pages.

 

Location/New will be our root page.

Rails.application.routes.draw do

  root 'locations#new'

  resources :locations

end

 new and _form Views

<h1>Is a Bus Nearby?</h1>
<p>
  Give us your address or 
  approximate location (<em>I'm at the 
  corner of...</em>), and we'll let you 
  know if there's a bus in the vicinty!
</p>

<%= render 'form' %>
<!-- lines 1-12 remain the same;
but in the fields area we'll
be more specific on our label
for the address field, and we'll
delete the fields for latitude
and longitude - Geocoder will
take care of those for us!
-->

  <div class="field">
    Address or Approximate Location:<br>
    <%= f.text_field :address %>
  </div>
  <div class="field">
    <%= f.label :city %><br>
    <%= f.text_field :city %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

location/new.html.erb

location/_form.html.erb

Showing the Buses

If there are buses nearby, we'll have them saved in an array and display their vital info for the User.

<p id="notice"><%= notice %></p>

<h2>You Are Currently Standing At...</h2>

<p>
	<!-- my_location will be defined
	in the model, as the combination
	of address and city -->
  <%= @location.my_location %>
</p>

<h2>The Closest Buses Are...</h2>
<!-- What if there are no buses nearby? -->
<% if @bus_count == 0 %>
	<p>
		...not really that close. It may be best to just use Uber.
	</p>
<% end %>
<!-- But if there are some buses in the vicinty... -->
<% @nearby_buses.each do |bus| %>
	<p>
		<!-- We'll show the Route #, Vehicle # and
		where the bus' next stop is. -->
		<strong>Route</strong>: <%= bus["ROUTE"] %><br />
		<strong>Vehicle #</strong>: <%= bus["VEHICLE"] %><br />
		<strong>Next Stop:</strong>: <%= bus["TIMEPOINT"] %><br />
	</p>
<% end %>

<!-- Leave a link to the edit page,
so the User can move about and try
again. -->
<%= link_to "Actually, I'm at...", edit_location_path(@location) %>

location/show.html.erb

Now to the Back End!

We've got the Front End complete

(maybe it could use some styling).

 

Let's power this app in the Back End.

Our next steps will be to visit these files:

1. Gemfile

2. location.rb

3. locations_controller.rb

4. locations_helper.rb

 

Geocoder Gem

We'll start by adding the gem to our Gemfile...

gem 'geocoder'

And running bundle in Terminal...

$ bundle install

Geocoder & The Model

Next, we'll set up Geocoder's lat/long magic in the Location model.

location.rb

class Location < ActiveRecord::Base

	geocoded_by :my_location
	after_validation :geocode

	# here, behind the scenes,
	# we will combine the address
	# and city given to use by the User,
	# while also adding the state
	# (because obviously, for MARTA, it's
	# only going to be GA), and Geocode that!
	def my_location
		"#{address}, #{city}, GA"
	end
end

Gain Control(ler)

Next we'll visit the Locations controller.

We're particularly interested in the show action.

locations_controller.rb

  def show
    # below is the URL or the MARTA API,
    # from this is where we pull the data
    # of all active MARTA buses
    source = 'http://developer.itsmarta.com/BRDRestService/BRDRestService.svc/GetAllBus'
    
    # we'll need to parse the data into an array of hashes,
    # following method will be defined in the Helper
    @buses = fetch_url_data(source)

    @bus_count = 0

    @nearby_buses = []

    @buses.each do |bus|
      # once again we'll use a method defined in the Helper
      if nearby(@location.longitude, @location.latitude, bus["LONGITUDE"].to_f, bus["LATITUDE"].to_f)
        @bus_count += 1
        @nearby_buses.push(bus)
      end
    end
  end

If You Need Some Help

Let's visit the Application Helper now (you could also do this in the Location Helper) and define the methods we used in the Controller.

application_helper.rb

module ApplicationHelper

  # In the following method, we parse the data
  # to store it in an array of hashes (each bus a hash)
  # I wouldn't expect you to come up with code all on 
  # your own it was given to me by a wiser coder, and
  # now I hand it down to you, young Rails padowans...
  def fetch_url_data(source)
    http = Net::HTTP.get_response(URI.parse(source))
    data = http.body
    response = JSON.parse(data)
    return response
  end

  # This method will compare the lat/longs of the User
  # and each bus, to see if they're close to each other
  # In this case, "nearby" means within .01 of a degree.
  def nearby(lng1, lat1, lng2, lat2)
    if (lng1 - lng2).abs <= 0.01 && (lat1 - lat2).abs <= 0.01
      return true
    else
      return false
    end
  end

end

One Last Step...

We'll need to make sure the Locations controller is even looking at the Application Helper.

locations_controller.rb

class LocationsController < ApplicationController
  before_action :set_location, only: [:show, :edit, :update, :destroy]

  include ApplicationHelper

# and then the rest of the file...

Let's Test It Out!

localhost:3000

(/locations/new)

localhost:3000/locations/show/1

How Can We Improve This App?

MARTA only runs in a select amount of cities - we could give the User a dropdown of cities to choose from.

Would a map showing where the User and the nearby Buses are located being helpful to the User?

Map It!

We just learned about the Google Maps API, why not use it in this app as well!

The Marta API

By argroch

The Marta API

Using MARTA's real-time API (plus Google Maps API) to power to Rails app.

  • 1,551