mirror of https://github.com/pelias/api.git
Severyn Kozak
10 years ago
42 changed files with 1134 additions and 179 deletions
@ -1,2 +1,5 @@ |
|||||||
node_modules |
node_modules |
||||||
|
coverage |
||||||
|
.idea |
||||||
*.log |
*.log |
||||||
|
reports |
@ -0,0 +1,123 @@ |
|||||||
|
## /search |
||||||
|
|
||||||
|
Full text search endpoint which queries the elasticsearch doc store, slightly slower than suggest. |
||||||
|
|
||||||
|
#### Required Parameters |
||||||
|
* **input**: query string |
||||||
|
|
||||||
|
#### Optional Parameters |
||||||
|
* **lat**: latitude |
||||||
|
* **lon**: longitude |
||||||
|
* **zoom**: zoom level from which you wish to view the world |
||||||
|
* **size**: number of results requested (defaults to 10) |
||||||
|
* **layers**: datasets you wish to query (defaults to ```poi,admin,address```). |
||||||
|
* valid values are ```poi```, ```admin``` or ```address``` |
||||||
|
* ```poi``` expands internally to ```geoname```, ```osmnode```, ```osmway``` |
||||||
|
* ```admin``` expands to ```admin0```, ```admin1```, ```admin2```, ```neighborhood```, ```locality```, ```local_admin``` |
||||||
|
* ```address``` expands to ```osmaddress```, ```openaddresses``` |
||||||
|
* can also be specific to one particular dataset, for example ```geoname``` |
||||||
|
* **bbox**: the bounding box from which you want all your results to come |
||||||
|
* can be one of the following comma separated string values |
||||||
|
* "southwest_lng,southwest_lat,northeast_lng,northeast_lat" ```L.latLngBounds(southwestLatLng, northeastLatLng).toBBoxString()``` |
||||||
|
* bottom left lon, bottom left lat, top right lon, top right lat |
||||||
|
* left, bottom, right, top |
||||||
|
* min longitude, min latitude, max longitude, max latitude |
||||||
|
* **details**: indicates if results should contain detailed, should be `true` or `false` |
||||||
|
* when false results will only contain `id`, `layer`, and `text` properties |
||||||
|
* when true, all available properties will be included in results |
||||||
|
|
||||||
|
|
||||||
|
## /search/coarse |
||||||
|
|
||||||
|
This is a coarse forward geocoder endpoint which only searches admin dataset layers. |
||||||
|
|
||||||
|
#### Required Parameters |
||||||
|
* **input**: query string |
||||||
|
|
||||||
|
#### Optional Parameters |
||||||
|
* **lat**: latitude |
||||||
|
* **lon**: longitude |
||||||
|
* **zoom**: zoom level from which you wish to view the world |
||||||
|
* **bbox**: the bounding box frome which you want all your results to come |
||||||
|
* **size**: (defaults to 10) |
||||||
|
* **layers**: (defaults to ```admin```) |
||||||
|
* **details**: (defaults to `true`) |
||||||
|
|
||||||
|
|
||||||
|
## /suggest |
||||||
|
|
||||||
|
The autocomplete endpoint, it offers fast response time. Mixes results from around the provided lat/lon and also from precision level 1 and 3. |
||||||
|
|
||||||
|
#### Required Parameters |
||||||
|
* **input**: query string |
||||||
|
* **lat**: latitude |
||||||
|
* **lon**: longitude |
||||||
|
* lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) |
||||||
|
|
||||||
|
#### Optional Parameters |
||||||
|
* **zoom**: zoom level from which you wish to view the world |
||||||
|
* **size**: number of results requested (defaults to 10) |
||||||
|
* **layers**: datasets you wish to query (defaults to ```poi,admin,address```) |
||||||
|
* **details**: (defaults to `true`) |
||||||
|
|
||||||
|
|
||||||
|
## /suggest/coarse |
||||||
|
|
||||||
|
Only queries the admin layers. |
||||||
|
|
||||||
|
#### Required Parameters |
||||||
|
* **input**: query string |
||||||
|
* **lat**: latitude from where you are searching |
||||||
|
* **lon**: longitude |
||||||
|
* lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) |
||||||
|
|
||||||
|
#### Optional Parameters |
||||||
|
* **zoom**: zoom level from which you wish to view the world |
||||||
|
* **size**: number of results requested (defaults to 10) |
||||||
|
* **layers**: datasets you wish to query (defaults to ```admin```) |
||||||
|
* **details**: (defaults to `true`) |
||||||
|
|
||||||
|
|
||||||
|
## /suggest/nearby |
||||||
|
|
||||||
|
Works as autocomplete for places located near a latitude/longitude, this endpoint is the same as ```/suggest``` but the results are all from within 50 kilometers of the specified point. Unlike ```/suggest```, ```/suggest/nearby``` does not mix results from different precision levels (500km, 1000km etc from lat/lon). |
||||||
|
|
||||||
|
#### Required Parameters |
||||||
|
* **input**: query string |
||||||
|
* **lat**: latitude |
||||||
|
* **lon**: longitude |
||||||
|
* lat/lon are **required** currently because of this [open issue](https://github.com/elasticsearch/elasticsearch/issues/6444) |
||||||
|
|
||||||
|
#### Optional Parameters |
||||||
|
* **zoom**: zoom level from which you wish to view the world |
||||||
|
* **size**: number of results you need (defaults to 10) |
||||||
|
* **layers**: datasets you wish to query (defaults to ```poi,admin,address```) |
||||||
|
* **details**: (defaults to `true`) |
||||||
|
|
||||||
|
|
||||||
|
## /reverse |
||||||
|
|
||||||
|
Reverse geocoding endpoint. |
||||||
|
|
||||||
|
#### Required Parameters |
||||||
|
* **lat**: latitude |
||||||
|
* **lon**: longitude |
||||||
|
|
||||||
|
#### Optional Parameters |
||||||
|
* **zoom**: zoom level from which you wish to view the world |
||||||
|
* **bbox**: bounding box |
||||||
|
* **layers**: (defaults to ```poi,admin,address```) |
||||||
|
* **details**: (defaults to `true`) |
||||||
|
|
||||||
|
|
||||||
|
## /doc |
||||||
|
|
||||||
|
Retrieves a document or multiple documents at once. |
||||||
|
|
||||||
|
#### Required Parameters |
||||||
|
* one of **id** or **ids** |
||||||
|
* **id**: |
||||||
|
* unique id of the document to be retrieved |
||||||
|
* should be in the form of type:id, for example: ```geoname:4163334``` |
||||||
|
* **ids**: |
||||||
|
* if multiple docs are to be fetched in bulk, an array of ids |
@ -1,22 +1,31 @@ |
|||||||
|
|
||||||
var pkg = require('../package'); |
var pkg = require('../package'); |
||||||
|
var markdown = require('markdown').markdown; |
||||||
|
var fs = require('fs'); |
||||||
|
|
||||||
function setup(){ |
function setup(){ |
||||||
|
|
||||||
function controller( req, res, next ){ |
var styleString = '<style>html{font-family:monospace}</style>'; |
||||||
|
var text = '# Pelias API\n'; |
||||||
|
text += '### Version: ['+ pkg.version+ '](https://github.com/pelias/api/releases)\n'; |
||||||
|
text += fs.readFileSync( './DOCS.md', 'utf8'); |
||||||
|
var indexHtml = styleString + markdown.toHTML(text); |
||||||
|
|
||||||
// stats
|
function controller( req, res, next ) { |
||||||
|
if (req.accepts('html')) { |
||||||
|
res.send(indexHtml); |
||||||
|
return; |
||||||
|
} |
||||||
|
// default behaviour
|
||||||
res.json({ |
res.json({ |
||||||
name: pkg.name, |
name: pkg.name, |
||||||
version: { |
version: { |
||||||
number: pkg.version |
number: pkg.version |
||||||
} |
} |
||||||
}); |
}); |
||||||
|
|
||||||
} |
} |
||||||
|
|
||||||
return controller; |
return controller; |
||||||
|
|
||||||
} |
} |
||||||
|
|
||||||
module.exports = setup; |
module.exports = setup; |
@ -0,0 +1,119 @@ |
|||||||
|
/** |
||||||
|
* These values specify how much a record that matches a certain category |
||||||
|
* should be boosted in elasticsearch results. |
||||||
|
*/ |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
'transport': 10, |
||||||
|
'transport:air': 20, |
||||||
|
'transport:air:aerodrome': 20, |
||||||
|
'transport:air:airport': 20, |
||||||
|
'recreation': 10, |
||||||
|
'religion': 10, |
||||||
|
'education': 10, |
||||||
|
'entertainment': 10, |
||||||
|
'nightlife': 10, |
||||||
|
'food': 10, |
||||||
|
'government': 10, |
||||||
|
'professional': 10, |
||||||
|
'finance': 10, |
||||||
|
'health': 10, |
||||||
|
'retail': 10, |
||||||
|
'transport:public': 10, |
||||||
|
'transport:bus': 10, |
||||||
|
'transport:taxi': 10, |
||||||
|
'transport:sea': 10, |
||||||
|
'accomodation': 10, |
||||||
|
'transport:station': 10, |
||||||
|
'food:bagel': 10, |
||||||
|
'food:barbecue': 10, |
||||||
|
'food:bougatsa': 10, |
||||||
|
'food:burger': 10, |
||||||
|
'food:cake': 10, |
||||||
|
'food:casserole': 10, |
||||||
|
'food:chicken': 10, |
||||||
|
'food:coffee_shop': 10, |
||||||
|
'food:crepe': 10, |
||||||
|
'food:couscous': 10, |
||||||
|
'food:curry': 10, |
||||||
|
'food:dessert': 10, |
||||||
|
'food:donut': 10, |
||||||
|
'food:empanada': 10, |
||||||
|
'food:fish': 10, |
||||||
|
'food:fish_and_chips': 10, |
||||||
|
'food:fried_food': 10, |
||||||
|
'food:friture': 10, |
||||||
|
'food:gyro': 10, |
||||||
|
'food:ice_cream': 10, |
||||||
|
'food:kebab': 10, |
||||||
|
'food:mediterranean': 10, |
||||||
|
'food:noodle': 10, |
||||||
|
'food:pancake': 10, |
||||||
|
'food:pasta': 10, |
||||||
|
'food:pie': 10, |
||||||
|
'food:pizza': 10, |
||||||
|
'food:regional': 10, |
||||||
|
'food:sandwich': 10, |
||||||
|
'food:sausage': 10, |
||||||
|
'food:savory_pancakes': 10, |
||||||
|
'food:seafood': 10, |
||||||
|
'food:steak': 10, |
||||||
|
'food:sub': 10, |
||||||
|
'food:sushi': 10, |
||||||
|
'food:tapas': 10, |
||||||
|
'food:vegan': 10, |
||||||
|
'food:vegetarian': 10, |
||||||
|
'food:wings': 10, |
||||||
|
'food:cuisine:african': 10, |
||||||
|
'food:cuisine:american': 10, |
||||||
|
'food:cuisine:arab': 10, |
||||||
|
'food:cuisine:argentinian': 10, |
||||||
|
'food:cuisine:asian': 10, |
||||||
|
'food:cuisine:australian': 10, |
||||||
|
'food:cuisine:baiana': 10, |
||||||
|
'food:cuisine:balkan': 10, |
||||||
|
'food:cuisine:basque': 10, |
||||||
|
'food:cuisine:bavarian': 10, |
||||||
|
'food:cuisine:belarusian': 10, |
||||||
|
'food:cuisine:brazilian': 10, |
||||||
|
'food:cuisine:cantonese': 10, |
||||||
|
'food:cuisine:capixaba': 10, |
||||||
|
'food:cuisine:caribbean': 10, |
||||||
|
'food:cuisine:chinese': 10, |
||||||
|
'food:cuisine:croatian': 10, |
||||||
|
'food:cuisine:czech': 10, |
||||||
|
'food:cuisine:danish': 10, |
||||||
|
'food:cuisine:french': 10, |
||||||
|
'food:cuisine:gaucho': 10, |
||||||
|
'food:cuisine:german': 10, |
||||||
|
'food:cuisine:greek': 10, |
||||||
|
'food:cuisine:hunan': 10, |
||||||
|
'food:cuisine:hungarian': 10, |
||||||
|
'food:cuisine:indian': 10, |
||||||
|
'food:cuisine:international': 10, |
||||||
|
'food:cuisine:iranian': 10, |
||||||
|
'food:cuisine:italian': 10, |
||||||
|
'food:cuisine:japanese': 10, |
||||||
|
'food:cuisine:korean': 10, |
||||||
|
'food:cuisine:kyo_ryouri': 10, |
||||||
|
'food:cuisine:latin_american': 10, |
||||||
|
'food:cuisine:lebanese': 10, |
||||||
|
'food:cuisine:malagasy': 10, |
||||||
|
'food:cuisine:mexican': 10, |
||||||
|
'food:cuisine:mineira': 10, |
||||||
|
'food:cuisine:okinawa_ryori': 10, |
||||||
|
'food:cuisine:pakistani': 10, |
||||||
|
'food:cuisine:peruvian': 10, |
||||||
|
'food:cuisine:polish': 10, |
||||||
|
'food:cuisine:portuguese': 10, |
||||||
|
'food:cuisine:rhenish': 10, |
||||||
|
'food:cuisine:russian': 10, |
||||||
|
'food:cuisine:shandong': 10, |
||||||
|
'food:cuisine:sichuan': 10, |
||||||
|
'food:cuisine:spanish': 10, |
||||||
|
'food:cuisine:thai': 10, |
||||||
|
'food:cuisine:turkish': 10, |
||||||
|
'food:cuisine:vietnamese': 10, |
||||||
|
'food:cuisine:westphalian': 10, |
||||||
|
'transport:rail': 10 |
||||||
|
}; |
@ -1,9 +1,12 @@ |
|||||||
|
var logger = require( '../src/logger' ); |
||||||
|
|
||||||
// handle application errors
|
// handle application errors
|
||||||
function middleware(err, req, res, next) { |
function middleware(err, req, res, next) { |
||||||
|
logger.error( 'Error:', err ); |
||||||
|
logger.error( 'Stack trace:', err.trace ); |
||||||
res.header('Cache-Control','no-cache'); |
res.header('Cache-Control','no-cache'); |
||||||
if( res.statusCode < 400 ){ res.status(500); } |
if( res.statusCode < 400 ){ res.status(500); } |
||||||
res.json({ error: err }); |
res.json({ error: typeof err === 'string' ? err : 'internal server error' }); |
||||||
} |
} |
||||||
|
|
||||||
module.exports = middleware; |
module.exports = middleware; |
@ -0,0 +1,38 @@ |
|||||||
|
|
||||||
|
var isObject = require('is-object'); |
||||||
|
|
||||||
|
// validate inputs, convert types and apply defaults
|
||||||
|
function sanitize( req ){ |
||||||
|
|
||||||
|
var clean = req.clean || {}; |
||||||
|
var params= req.query; |
||||||
|
|
||||||
|
// ensure the input params are a valid object
|
||||||
|
if( !isObject( params ) ){ |
||||||
|
params = {}; |
||||||
|
} |
||||||
|
|
||||||
|
// default case (no categories specified in GET params)
|
||||||
|
if('string' !== typeof params.categories || !params.categories.length){ |
||||||
|
clean.categories = []; |
||||||
|
} |
||||||
|
else { |
||||||
|
// parse GET params
|
||||||
|
clean.categories = params.categories.split(',') |
||||||
|
.map(function (cat) { |
||||||
|
return cat.toLowerCase().trim(); // lowercase inputs
|
||||||
|
}) |
||||||
|
.filter( function( cat ) { |
||||||
|
return ( cat.length > 0 ); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// pass validated params to next middleware
|
||||||
|
req.clean = clean; |
||||||
|
|
||||||
|
return { 'error': false }; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// export function
|
||||||
|
module.exports = sanitize; |
@ -0,0 +1,39 @@ |
|||||||
|
var isObject = require('is-object'); |
||||||
|
|
||||||
|
// validate inputs, convert types and apply defaults
|
||||||
|
function sanitize( req, default_value ){ |
||||||
|
|
||||||
|
var clean = req.clean || {}; |
||||||
|
var params= req.query; |
||||||
|
|
||||||
|
if (default_value === undefined) { |
||||||
|
default_value = true; |
||||||
|
} |
||||||
|
|
||||||
|
default_value = !!default_value; |
||||||
|
|
||||||
|
// ensure the input params are a valid object
|
||||||
|
if( !isObject( params ) ){ |
||||||
|
params = {}; |
||||||
|
} |
||||||
|
|
||||||
|
if (params.details !== undefined) { |
||||||
|
var details = params.details; |
||||||
|
|
||||||
|
if (typeof params.details === 'string') { |
||||||
|
details = params.details === 'true'; |
||||||
|
} |
||||||
|
|
||||||
|
clean.details = details === true || details === 1;
|
||||||
|
} else { |
||||||
|
clean.details = default_value; |
||||||
|
} |
||||||
|
|
||||||
|
req.clean = clean; |
||||||
|
|
||||||
|
return {'error':false}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// export function
|
||||||
|
module.exports = sanitize; |
@ -1,10 +0,0 @@ |
|||||||
|
|
||||||
#> jsonp |
|
||||||
path: '/?callback=test' |
|
||||||
|
|
||||||
#? content-type header correctly set |
|
||||||
response.should.have.header 'Content-Type','application/javascript; charset=utf-8' |
|
||||||
|
|
||||||
#? should respond with jsonp |
|
||||||
should.exist response.body |
|
||||||
response.body.substr(0,5).should.equal 'test('; |
|
Loading…
Reference in new issue