'use strict';
/**
* ### Shape Query Functions
* These functions query the `gtfs_shapes` table in the Right Track Database.
* @module query/shapes
*/
const cache = require('memory-cache');
const Shape = require('../gtfs/Shape.js');
const Route = require('../gtfs/Route.js');
const Agency = require('../gtfs/Agency.js');
// ==== QUERY FUNCTIONS ==== //
/**
* Get all of the Shape's Points for the specified Shape ID
* @param {RightTrackDB} db The Right Track DB to query
* @param {string} id Shape ID
* @param {function} callback Callback function
* @param {Error} callback.error Database Query Error
* @param {Shape[]} callback.shapes The Shape's Points for the specified Shape ID
*/
let getShape = function(db, id, callback) {
// Check cache for shape
let cacheKey = db.id + "-" + id;
let cache = cache_shapesById.get(cacheKey);
if ( cache !== null ) {
return callback(null, cache);
}
// Query the DB
let select = "SELECT shape_id, shape_pt_lat, shape_pt_lon, shape_pt_sequence, shape_dist_traveled FROM gtfs_shapes WHERE shape_id = '" + id + "';";
db.select(select, function(err, results) {
// Database Query Error
if ( err ) {
return callback(err);
}
// Build the Shapes
let rtn = [];
for ( let i = 0; i < results.length; i++ ) {
let row = results[i];
let shape = new Shape(
row.shape_id,
row.shape_pt_lat,
row.shape_pt_lon,
row.shape_pt_sequence,
{
shapeDistTraveled: row.shape_dist_traveled
}
);
rtn.push(shape);
}
// Sort the Shapes
rtn.sort(Shape.sortBySequence);
// Return results
cache_shapesById.put(cacheKey, rtn);
return callback(null, rtn);
});
}
/**
* Get all of the Points grouped by Shape from the database
* @param {RightTrackDB} db The Right Track DB to query
* @param {function} callback Callback function
* @param {Error} callback.error Database Query Error
* @param {Shape[][]} callback.shapes A 2D-Array of Shape Points
*/
let getShapes = function(db, callback) {
// Check cache for shapes
let cacheKey = db.id + "-shapes";
let cache = cache_shapes.get(cacheKey);
if ( cache !== null ) {
return callback(null, cache);
}
// Set counters and return shapes
let count = 0;
let done = 1;
let rtn = [];
// Get the Shape IDs
let select = "SELECT DISTINCT(shape_id) FROM gtfs_trips WHERE shape_id IS NOT NULL AND shape_id <> '' ORDER BY shape_id ASC;";
db.select(select, function(err, results) {
if ( err ) {
return callback(err);
}
// No shapes found
if ( results.length === 0 ) {
return callback(null, rtn);
}
// Get each Shape
done = results.length;
for ( let i = 0; i < results.length; i++ ) {
let row = results[i];
getShape(db, row.shape_id, _finish);
}
});
// Return to th main callback once all queries are done
function _finish(err, shape) {
if ( err ) {
return callback();
}
else {
count++;
rtn.push(shape);
if ( count >= done ) {
cache_shapes.put(cacheKey, rtn);
return callback(null, rtn);
}
}
}
}
/**
* Get the details of the Route(s) that correspond to the specified
* Shape (or all of the Shapes)
* @param {RightTrackDB} db The Right Track DB to query
* @param {string} [id] Shape ID
* @param {function} callback Callback function
* @param {Error} callback.error Database Query Error
* @param {Object|Route[]} callback.routes Routes associated with each Shape
*/
let getShapeRoutes = function(db, id, callback) {
if ( !callback && typeof id === 'function' ) {
callback = id;
id = undefined;
}
// Check cache for routes
let cacheKey = db.id + "-" + id + "-routes";
let cache = cache_shapeRoutes.get(cacheKey);
if ( cache !== null ) {
return callback(null, cache);
}
// Build query
let select = "SELECT shape_id, ";
select += "gtfs_routes.route_id, route_short_name, route_long_name, route_desc, route_type, route_url, route_color, route_text_color, ";
select += "gtfs_agency.agency_id, agency_name, agency_url, agency_timezone, agency_lang, agency_phone, agency_fare_url";
select += " FROM gtfs_trips";
select += " LEFT JOIN gtfs_routes USING (route_id)";
select += " LEFT JOIN gtfs_agency USING (agency_id)";
select += " WHERE shape_id IS NOT NULL AND shape_id <> ''";
if ( id ) select += " AND shape_id = '" + id + "'";
select += " GROUP BY shape_id, route_id;";
// Get the route details for the shape(s)
db.select(select, function(err, rows) {
if ( err ) {
return callback(err);
}
// Build response
let rtn = {};
for ( let i = 0; i < rows.length; i++ ) {
let row = rows[i];
let shape_id = row.shape_id;
// Create Agency
let agency = new Agency(
row.agency_name,
row.agency_url,
row.agency_timezone,
{
id: row.agency_id,
lang: row.agency_lang,
phone: row.agency_phone,
fareUrl: row.agency_fare_url
}
);
// Create Route
let route = new Route(
row.route_id,
row.route_short_name,
row.route_long_name,
row.route_type,
{
agency: agency,
description: row.route_desc,
url: row.route_url,
color: row.route_color,
textColor: row.route_text_color
}
);
// Add Route to return
if ( rtn.hasOwnProperty(shape_id) ) {
rtn[shape_id].push(route);
}
else {
rtn[shape_id] = [route];
}
}
// Return the Routes
return callback(null, id ? rtn[id] : rtn);
});
}
/**
* Get the coordinates of the center of the specified Shape (or all of the Shapes)
* @param {RightTrackDB} db The Right Track DB to query
* @param {string} [id] The Shape ID
* @param {function} callback Callback function
* @param {Error} callback.error Database Query Error
* @param {Object} callback.center The coordinates of the Shape's center
* @param {number} callback.center.lat The latitiude of the Shape's center
* @param {number} callback.center.lon The longitude of the Shape's center
*/
let getShapeCenter = function(db, id, callback) {
if ( !callback && typeof id === 'function' ) {
callback = id;
id = undefined;
}
// Check cache for shapes
let cacheKey = db.id + "-" + id + "-center";
let cache = cache_shapeCenter.get(cacheKey);
if ( cache !== null ) {
return callback(null, cache);
}
// Get the average lat and lon
let select = "SELECT AVG(shape_pt_lat) AS avg_lat, AVG(shape_pt_lon) AS avg_lon FROM gtfs_shapes";
if ( id ) {
select += " WHERE shape_id = '" + id + "'";
}
db.get(select, function(err, row) {
let rtn = {
lat: row && row.avg_lat ? row.avg_lat : 0,
lon: row && row.avg_lon ? row.avg_lon : 0
};
cache_shapeCenter.put(cacheKey, rtn);
return callback(err, rtn);
});
}
// ==== SETUP CACHES ==== //
let cache_shapesById = new cache.Cache();
let cache_shapes = new cache.Cache();
let cache_shapeRoutes = new cache.Cache();
let cache_shapeCenter = new cache.Cache();
/**
* Clear the ShapesTable caches
* @private
*/
function clearCache() {
cache_shapesById.clear();
cache_shapes.clear();
cache_shapeRoutes.clear();
cache_shapeCenter.clear();
}
// Export Functions
module.exports = {
getShape: getShape,
getShapes: getShapes,
getShapeRoutes: getShapeRoutes,
getShapeCenter: getShapeCenter,
clearCache: clearCache
}