Panama's public transport API
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
app added shapes to routes Aug 5, 2016
bin
config
db
lib/tasks
public
test
.gitignore
.ruby-version
Gemfile
Gemfile.lock
Procfile
README.md
Rakefile
config.ru

README.md

Panatrans API

API for the public bus tranportation system of Ciudad de Panamá (Panamá).

Build Status

Code Climate

Test Coverage

About

Panatrans is a collaborative project to allow the users of the Panamanian public transport to create a dataset with the information of the stops and routes available in the City of Panama (which was inexistent at the time the project started).

This project is based on the premise that open software and open data are keys on innovation.

Panatrans is divided in four components:

  1. gtfs_api: The data model. It is an implementation of the GTFS Specification in ruby on rails as an engine.
  2. panatrans-api: It is the server side that populates a JSON API based on the data model of gtfs_api. Also written in ruby on rails.
  3. panatrans-web: A javascript web client that makes usage of panatrans-api. It is an angularjs application.
  4. panatrans-dataset: It is the GTFS feed of the Panamanian public transport.

API Specification V1.1 beta

Specification for developers that plan to make a service or a mobile application based on this API.

This is a JSON RESTful API.

Conceptual model

This API relies in a schema that is an extreme simplification of the General Transit Feed Specification (GTFS) defined by Google.

Conceptual model of a route

There are 4 types of resources:

  • Stop: Represents a bus stop. Includes a name and the (latitude, longitude) tuple.

  • Route: Represents a bus route, for example: the route from Albrook to Miraflores.

  • Trip: A route generally has one trip.

  • Stop_Sequence: Links a trip with a stop to create an ordered list of stops. In the example, the trip Albrook to Miraflores has 4 stops, and therefore it has 4 stop_sequences, each one will link one of the stops with that trip. The first stop is Albrook (sequence = 0), then Diablo (sequence = 1), Ciudad del Saber (sequence = 2) and the last one Miraflores (sequence = 3). In GTFS language, stop_times are equivalent to stop_sequences.

In any call to the API you will be requesting any of these resources.

Common stuff in API Responses

In every API response there is a "status". Possible values are:

  1. success. Response data will be set in data. HTTP response is always 200 (success). Example:

    {
    "status":"success",
    "data":[
    	{"id":1048002442,"name":"Albrook-Marañón"},
    	{"id":219448156,"name":"Vía Brasil-Federico Boyd"}]
    }
  2. fail, there was a problem performing the operation (ie: incorrect value while creating or updating a resource). The list of problems is passed throught the errors object. Example:

    #curl -X POST --data "stops[name]=2" http://test-panatrans.herokuapp.com/v1/stops/
    
    {
      "status":"fail",
      "errors": {
    	"lat":["can't be blank","is not a number"],
    	"lon":["can't be blank","is not a number"]
    	}
    }

Please see the configuration section below on this file for more info about this mode

Staging Environment

If you plan to develop a client or a mobile app we recommend you to setup a local instance of Panatrans API (you have the instructions below in the section "Setting up your own panatrans API server"). Also, we've setup a staging environment that you can use to test your app http://test-panatrans.herokuapp.com/.

To release an app that points to the production environment, please contact us by submitting an issue or send a github message to @merlos.

Examples of usage

Examples of usage of this API are available on the public/examples/ folder. Live demos:

ROUTES

GET /routes/[?with_trips=true]

Gets all the routes ordered by name (alphabetical order)

{
	"status" : "success"
	"data" : [{
		"id" : INT,              #route id
		"name" : STRING,         # "Route name"
    "url" : URL,             # url of the route @ mibus.com.pa. May be null

		"trips" : [{  # <-- Only sent if with_trips=true
			"id": INT,            # trip id
			"headsign": STRING,   # "hacia Albrook"
			"direction": INT,     # 0=ida, 1= retorno
		   }, ...
		]}, ...
	 ]
   }
}			

urlis a web address that should have more information about the route. Typically located at mibus.com.pa. Its value may be null.

Example: http://test-panatrans.herokuapp.com/v1/routes/?prettify=true

Including trips: http://test-panatrans.herokuapp.com/v1/routes/?with_trips=true&prettify=true


HINT! In any request to the api, if you add to the query string the param ?prettify=true, the output will be a human readable JSON with indentantion and that kind of stuff. Example: http://panantransserver.org/v1/routes/?prettify=true


GET /routes/with_trips

Gets all the routes ordered alphabetically by name and includes the trips linked to each route.

Example:

https://test-panatrans.herokuapp.com/v1/routes/?prettify=true

GET /routes/:id[?without_shapes=true]

Returns the detail of the route identified by :id.

{
  "status" : "success"
  "data" :  {
  "id" : INT                  # route id
  "name" : STRING             # "Albrook - Exclusas de Miraflores"
  "url" : URL,                # URL of the route at mibus.com.pa. May be null
  "trips" : [
    {
      "id" : INT              # 2, trip id
      "headsign" : STRING,    # "Hacia Miraflores"
      "direction" : INT,      # 0 =  ida, 1 = retorno
      "stop_sequences" : [{
        "id" : INT,           # stop_sequence id
        "sequence" : INT      # first is 0
        "stop" : {
        	"id" : INT,         # stop id
        	"name" : STRING,    # "Albrook"
        	"lat" : LATITUDE,   # "8.9740946"
        	"lon" : LONGITUDE   # "-79.5508536"
        	}
        },
        ...
      ],
			shape: [                # <--- Not sent if ?without_shapes=true
				{"id": INT,
				"pt_lat": LATITUDE,
				"pt_lon":LONGITUDE,
				"pt_sequence":INT}]
				},
    	...
  		]
		}, ...
	]
}
  • LATITUDE is a float within the interval (-90, 90).
  • LONGITUDE is a float within the interval (-180, 180).

Note: in the response latitude and longitude are enclosed in "".

Example:

http://test-panatrans.herokuapp.com/v1/routes/1048002442?prettify=true

If the request is successful, it returns the route detail of the updated resource (ie: same as GET /routes/:id).

STOPS

GET /stops/

Gets all stops

{
  "status": "success",
  "data": [
    {
      "id": INT,        # 1
      "name": STRING    # "Albrook",
      "lat": LATITUDE   #"8.974095",
      "lon": LONGITUDE  #"-79.550854"
    },...
  ]
 }


Example:

http://test-panatrans.herokuapp.com/v1/stops/?prettify=true

GET /stops/:id[?with_stop_sequences=true]

Returns the detail of a stop with id :id.

Adding the option with_stop_sequences=true the response will include the stop_sequences of each trip that has this stop.

{
  "status": "success",
  "data": {
    "id": INT,
    "name": STRING,             # "Albrook",
    "lat": LATITUDE,             # "8.974095",
    "lon": LONGITUDE,            # "-79.550854",
    "routes": [
      {
        "id": INT,
        "name": STRING,          # "Albrook-Marañón",
        "url": URL,              # http://www.mibus.com.pa/rutas
        "trips": [
          {
            "id": INT,
            "headsign": STRING,  # "hacia Marañón",
            "direction": INT,    # 0=> ida/circular, 1=> vuelta,
            "route_id": INT,     # 1048002442

            "stop_sequences": [{  # STOP SEQUENCES ARE ONLY SENT if with_stop_sequences=true
              "id": INT,         # stop_sequence id
              "sequence": INT,      
              "stop_id": INT,
              "trip_id": INT
              },
              ...
            ]
          },
          ...
         ]
      },
      ...
    ]
  }
}

Example:

http://test-panatrans.herokuapp.com/v1/stops/382818451?prettify=true

http://test-panatrans.herokuapp.com/v1/stops/382818451?with_stop_sequences=true&prettify=true

GET /v1/stops/nearby/?lat=LATITUDE&lon=LONGITUDE&radius=METERS

Gets the stops in the surroundings of the center (lat, lon) within the radius (in meters).

{
  "status": "success",
  "data": [
    {
      "id": INT,       # stop_id
      "name": STRING   # stop name "Policía Nacional",
      "lat": LONGITUDE # "8.965629",
      "lon": LATITUDE  # "-79.549224",
      "distance": INT  # meters from requested point.
    },
   ]
 }

The response returns the stops ordered by ascendent distance.

Example: Get stops close to the point (8.9656294,-79.5492239) and within a radius of 1000m (1km):

http://test-panatrans.herokuapp.com/v1/stops/nearby?lat=8.9656294&lon=-79.5492239&radius=1000&prettify=true

TRIPS

GET /trips/

Gets all trips.

{
 "status": "success",
  "data": [
    {
      "id": INT,          # trip id
      "headsign": STRING  # "hacia Albrook",
      "direction": INT,   # 0 => go, 1 => return trip
      "route": {          # route this trip belongs to.
        "id": INT,        # route id
        "name": STRING,   # route name: "Albrook-Panamá Viejo"
        "url": URL,       # route url: http://www.mibus.com.pa/rutas/
      }
    },
	...
  ]
}

direction is an integer that indicates if the trip is to go or return. 0 means go and 1 means return. For example, in the route "Albrook - Miraflores", the first trip from Albrook to Miraflores has direction = 0. The return trip, from Miraflores to Albrook, has direction = 1.

Example:

http://test-panatrans.herokuapp.com/v1/trips?prettify=true

GET /trips/:id

Gets the detail of the trip with id :id. The sequence of stops is returned ordered by sequence number.

A stop without sequence number means that the stop belongs to that trip but the order within the same is unknown.

{
  "status": "success",
  "data": {
    "id": INT,           # trip id
    "headsign": STRING   # trip headsign "hacia Marañón",
    "direction": 0,      # trip direction, 0=>go 1=> return
    "route": {
      "id": INT,      # route id
      "name": STRING, # route name "Albrook-Marañón"
      "url": URL,
    },
    "stop_sequences": [
      {
        "id": INT,        # Stops sequence id
        "sequence": 0,    # sequence Number
        "stop": {         
          "id": INT,        # stop id
          "name": STRING    # "Albrook",
          "lat": LATITUDE   # "8.9740946",
          "lon": LONGITUDE  #"-79.5508536"
        }
      },
      ...
      }
    ]
  }
}

Example: http://test-panatrans.herokuapp.com/v1/trips/1048002442?prettify=true

STOP_SEQUENCES

Stops sequences link stops to trips.

GET /stop_sequences/

Gets all stops_sequences

{
  "status": "success",
  "data": [
    {
      "id": 13110989,
      "sequence": 3,
      "stop_id": 382818451,
      "trip_id": 665778822
    },...
  ]
}

Example:

http://test-panatrans.herokuapp.com/v1/stop_sequences?prettify=true

GET /stop_sequences/:id

Gets the details of a stop_sequence

The first stop in a trip has sequence = 0.

stop_sequence.sequence = nil, means that the order of this stop within the trip is unknown. It may happen that the stop was added to the trip, but it wasn't known the position.

{
  "status": "success",
  "data": {
    "id": INT,        # stop_sequence id
    "sequence": INT,  # position in the trip
    "stop": {
      "id": INT,         # stop id
      "name": STRING,    # "Albrook",
      "lat": LATITUDE,   # "8.974095",
      "lon": LONGITUDE   # "-79.550854"
    },
    "trip": {
      "id": INT,         # trip_id
      "headsign": STRING # "hacia Miraflores",
      "direction": INT,  #
      "route": {         
        "id": INT,       # route id
        "name": STRING,  # "Albrook-Miraflores"
        "url": URL       # http://www.mibus.com.pa/rutas/
      }
    }
  }
}

Example:

http://test-panatrans.herokuapp.com/v1/stop_sequences/396371388?prettify=true

Export Calls

There is a set of calls to get the resources that have been created or updated since a particular date.

These calls are useful either to get a full database dump or to keep track of the hanges changes in the server database (incremental upates, monitoring).

TODO: Please note that, right now these calls don't include information about deletes (ie: only returns new items that have been created or updated). This is a caveat that is expected to be solved after mergin the features/auditable branch.

The format is the following:

GET /v1/:resource/since/:seconds_since_epoc(.csv)

Where

  • :resource can be stops,routes, trips or stop_sequences

  • :seconds_since_epoc is the number of seconds since 1970-01-01 00:00:00 UTC. You can run the command date +%sto get the current number of seconds.

  • .csv: optional parameter to get the dump in CSV format.

Example:

# make a dump of the database in CSV
GET /v1/stops/since/0.csv
GET /v1/routes/since/0.csv
GET /v1/trips/since/0.csv
GET /v1/stop_sequences/since/0.csv

Exporting stops to KML and GPX

The API also supports exporting the stops to Google Earth/Google Maps KML and GPS Exchange Format (GPX). These are the calls:

GET /v1/stops.kml
GET /v1/stops.gpx

Example:

Setting up your own panatrans API server

If you want to set up you own server for development purposes here you have the instructions.

The API has been developed in Ruby on Rails. It has been tested using Ruby 2.1.2 and Rails 4.1.6.

panatrans-api is also heroku-friendly, so you can test the project on that environment.

Setup: short version

To create a local version of the server API run these commands:

 $ git clone https://github.com/merlos/panatrans-api.git
 $ cd panatrans-api
 $ bundle install
 $ rake db:migrate
 $ rake gtfs:import[https://github.com/merlos/panatrans-dataset/archive/master.zip] # You can use any GTFS feed
	 # ...
	 # This may take a while
	 # ...
 $ rails server

Now you can open your browse at http://localhost:3000/v1/, and you'll see the list of API calls available.

You may also want to check the panatrans-web project, a web client developed with angularjs that makes usage of this API.

Configuration

Read only mode (read_only_mode)

At this moment, the API only supports read calls (GET). In the future it will support create, update and delete calls.

Read only mode will forbid to change on the database made through JSON API calls. That is, it will disable all the create, update, and delete actions.

In order to activate the read only mode (by default is off), you just have to change the variable read_only_mode on the config/application.rb file.

 # config/application.rb
   config.x.read_only_mode = true

Rake tasks

TODO these tasks are deprecated and it is pending to clean them. Meanwhile you should use the rake tasks with the gtfs prefix (use rake -T to get them) documentation of these tasks is available on gtfs_api project

This is the list of custom rake tasks developed for the project:

rake dataset:download                   # downloads dataset from git (TODO options: DATASET_GIT_URL=github.com/merlos/panatrans-dataset, DATASET_DIR=./tmp/dataset, DATASET_GIT_BRANCH=master]
rake dataset:fixtures                   # Loads development fixtures in current environment database (clears database)
rake dataset:import                     # imports dataset csv files into database (db not cleared) (TODO options: DATASET_DIR=./tmp/dataset/)
rake dataset:reset                      # clears the database and then imports last downloaded csv files
rake dataset:update                     # updates dataset

To get a complete list of rake tasks run rake -T

How to run the test suite

The project includes a set of tests which use the default testing suite that comes with rails (minitest). To run the tests:

  $ rake test

More about test a rails app.

Fixtures

Fixtures are stored in tests/fixtures.

To load the sample data in your development environment run:

rake dataset:fixtures

More info about fixtures.

Changelog

  • March 2015. First release.
  • V1.0.0 April 2016. Added only read option (tag v1.0.0)
  • V1.1.0 September 2016. Support of GTFS backend. Disabled create, update and delete

License

Distributed under the MIT License (MIT)

Copyright (c) 2015 Juan M. Merlos

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.