mirror of https://github.com/pelias/api.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
919 lines
33 KiB
919 lines
33 KiB
var Router = require('express').Router; |
|
var elasticsearch = require('elasticsearch'); |
|
|
|
const all = require('predicates').all; |
|
const any = require('predicates').any; |
|
const not = require('predicates').not; |
|
const _ = require('lodash'); |
|
|
|
/** ----------------------- sanitizers ----------------------- **/ |
|
var sanitizers = { |
|
autocomplete: require('../sanitizer/autocomplete'), |
|
place: require('../sanitizer/place'), |
|
search: require('../sanitizer/search'), |
|
defer_to_addressit: require('../sanitizer/defer_to_addressit'), |
|
structured_geocoding: require('../sanitizer/structured_geocoding'), |
|
reverse: require('../sanitizer/reverse'), |
|
nearby: require('../sanitizer/nearby') |
|
}; |
|
|
|
/** ----------------------- middleware ------------------------ **/ |
|
var middleware = { |
|
calcSize: require('../middleware/sizeCalculator'), |
|
requestLanguage: require('../middleware/requestLanguage') |
|
}; |
|
|
|
/** ----------------------- controllers ----------------------- **/ |
|
|
|
var controllers = { |
|
coarse_reverse: require('../controller/coarse_reverse'), |
|
mdToHTML: require('../controller/markdownToHtml'), |
|
libpostal: require('../controller/libpostal'), |
|
structured_libpostal: require('../controller/structured_libpostal'), |
|
place: require('../controller/place'), |
|
placeholder: require('../controller/placeholder'), |
|
search: require('../controller/search'), |
|
search_with_ids: require('../controller/search_with_ids'), |
|
status: require('../controller/status') |
|
}; |
|
|
|
var queries = { |
|
cascading_fallback: require('../query/search'), |
|
very_old_prod: require('../query/search_original'), |
|
structured_geocoding: require('../query/structured_geocoding'), |
|
reverse: require('../query/reverse'), |
|
autocomplete: require('../query/autocomplete'), |
|
address_using_ids: require('../query/address_search_using_ids') |
|
}; |
|
|
|
/** ----------------------- controllers ----------------------- **/ |
|
|
|
var postProc = { |
|
trimByGranularity: require('../middleware/trimByGranularity'), |
|
trimByGranularityStructured: require('../middleware/trimByGranularityStructured'), |
|
distances: require('../middleware/distance'), |
|
confidenceScores: require('../middleware/confidenceScore'), |
|
confidenceScoresFallback: require('../middleware/confidenceScoreFallback'), |
|
confidenceScoresReverse: require('../middleware/confidenceScoreReverse'), |
|
accuracy: require('../middleware/accuracy'), |
|
dedupe: require('../middleware/dedupe'), |
|
interpolate: require('../middleware/interpolate'), |
|
localNamingConventions: require('../middleware/localNamingConventions'), |
|
renamePlacenames: require('../middleware/renamePlacenames'), |
|
geocodeJSON: require('../middleware/geocodeJSON'), |
|
sendJSON: require('../middleware/sendJSON'), |
|
parseBoundingBox: require('../middleware/parseBBox'), |
|
normalizeParentIds: require('../middleware/normalizeParentIds'), |
|
assignLabels: require('../middleware/assignLabels'), |
|
changeLanguage: require('../middleware/changeLanguage'), |
|
sortResponseData: require('../middleware/sortResponseData') |
|
}; |
|
|
|
// predicates that drive whether controller/search runs |
|
const hasResponseData = require('../controller/predicates/has_response_data'); |
|
const hasRequestErrors = require('../controller/predicates/has_request_errors'); |
|
const isCoarseReverse = require('../controller/predicates/is_coarse_reverse'); |
|
const isAdminOnlyAnalysis = require('../controller/predicates/is_admin_only_analysis'); |
|
const hasResultsAtLayers = require('../controller/predicates/has_results_at_layers'); |
|
const isAddressItParse = require('../controller/predicates/is_addressit_parse'); |
|
const hasRequestCategories = require('../controller/predicates/has_request_parameter')('categories'); |
|
const isOnlyNonAdminLayers = require('../controller/predicates/is_only_non_admin_layers'); |
|
// this can probably be more generalized |
|
const isRequestSourcesOnlyWhosOnFirst = require('../controller/predicates/is_request_sources_only_whosonfirst'); |
|
const hasRequestParameter = require('../controller/predicates/has_request_parameter'); |
|
const hasParsedTextProperties = require('../controller/predicates/has_parsed_text_properties'); |
|
|
|
// shorthand for standard early-exit conditions |
|
const hasResponseDataOrRequestErrors = any(hasResponseData, hasRequestErrors); |
|
const hasAdminOnlyResults = not(hasResultsAtLayers(['venue', 'address', 'street'])); |
|
|
|
const hasNumberButNotStreet = all( |
|
hasParsedTextProperties.any('number'), |
|
not(hasParsedTextProperties.any('street')) |
|
); |
|
|
|
const serviceWrapper = require('pelias-microservice-wrapper').service; |
|
const PlaceHolder = require('../service/configurations/PlaceHolder'); |
|
const PointInPolygon = require('../service/configurations/PointInPolygon'); |
|
const Language = require('../service/configurations/Language'); |
|
const Interpolation = require('../service/configurations/Interpolation'); |
|
const Libpostal = require('../service/configurations/Libpostal'); |
|
|
|
/** |
|
* Append routes to app |
|
* |
|
* @param {object} app |
|
* @param {object} peliasConfig |
|
*/ |
|
function addRoutes(app, peliasConfig) { |
|
const esclient = elasticsearch.Client(peliasConfig.esclient); |
|
|
|
const pipConfiguration = new PointInPolygon(_.defaultTo(peliasConfig.api.services.pip, {})); |
|
const pipService = serviceWrapper(pipConfiguration); |
|
const isPipServiceEnabled = _.constant(pipConfiguration.isEnabled()); |
|
|
|
const placeholderConfiguration = new PlaceHolder(_.defaultTo(peliasConfig.api.services.placeholder, {})); |
|
const placeholderService = serviceWrapper(placeholderConfiguration); |
|
const isPlaceholderServiceEnabled = _.constant(placeholderConfiguration.isEnabled()); |
|
|
|
const changeLanguageConfiguration = new Language(_.defaultTo(peliasConfig.api.services.placeholder, {})); |
|
const changeLanguageService = serviceWrapper(changeLanguageConfiguration); |
|
const isChangeLanguageEnabled = _.constant(changeLanguageConfiguration.isEnabled()); |
|
|
|
const interpolationConfiguration = new Interpolation(_.defaultTo(peliasConfig.api.services.interpolation, {})); |
|
const interpolationService = serviceWrapper(interpolationConfiguration); |
|
const isInterpolationEnabled = _.constant(interpolationConfiguration.isEnabled()); |
|
|
|
// standard libpostal should use req.clean.text for the `address` parameter |
|
const libpostalConfiguration = new Libpostal( |
|
_.defaultTo(peliasConfig.api.services.libpostal, {}), |
|
_.property('clean.text')); |
|
const libpostalService = serviceWrapper(libpostalConfiguration); |
|
|
|
// structured libpostal should use req.clean.parsed_text.address for the `address` parameter |
|
const structuredLibpostalConfiguration = new Libpostal( |
|
_.defaultTo(peliasConfig.api.services.libpostal, {}), |
|
_.property('clean.parsed_text.address')); |
|
const structuredLibpostalService = serviceWrapper(structuredLibpostalConfiguration); |
|
|
|
// fallback to coarse reverse when regular reverse didn't return anything |
|
const coarseReverseShouldExecute = all( |
|
isPipServiceEnabled, not(hasRequestErrors), not(hasResponseData), not(isOnlyNonAdminLayers) |
|
); |
|
|
|
const libpostalShouldExecute = all( |
|
not(hasRequestErrors), |
|
not(isRequestSourcesOnlyWhosOnFirst) |
|
); |
|
|
|
// for libpostal to execute for structured requests, req.clean.parsed_text.address must exist |
|
const structuredLibpostalShouldExecute = all( |
|
not(hasRequestErrors), |
|
hasParsedTextProperties.all('address') |
|
); |
|
|
|
// execute placeholder if libpostal only parsed as admin-only and needs to |
|
// be geodisambiguated |
|
const placeholderGeodisambiguationShouldExecute = all( |
|
not(hasResponseDataOrRequestErrors), |
|
isPlaceholderServiceEnabled, |
|
// check request.clean for several conditions first |
|
not( |
|
any( |
|
// layers only contains venue, address, or street |
|
isOnlyNonAdminLayers, |
|
// don't geodisambiguate if categories were requested |
|
hasRequestCategories |
|
) |
|
), |
|
any( |
|
// only geodisambiguate if libpostal returned only admin areas or libpostal was skipped |
|
isAdminOnlyAnalysis, |
|
isRequestSourcesOnlyWhosOnFirst |
|
) |
|
); |
|
|
|
// execute placeholder if libpostal identified address parts but ids need to |
|
// be looked up for admin parts |
|
const placeholderIdsLookupShouldExecute = all( |
|
not(hasResponseDataOrRequestErrors), |
|
isPlaceholderServiceEnabled, |
|
// check clean.parsed_text for several conditions that must all be true |
|
all( |
|
// run placeholder if clean.parsed_text has 'street' |
|
hasParsedTextProperties.any('street'), |
|
// don't run placeholder if there's a query or category |
|
not(hasParsedTextProperties.any('query', 'category')), |
|
// run placeholder if there are any adminareas identified |
|
hasParsedTextProperties.any('neighbourhood', 'borough', 'city', 'county', 'state', 'country') |
|
) |
|
); |
|
|
|
const searchWithIdsShouldExecute = all( |
|
not(hasRequestErrors), |
|
// don't search-with-ids if there's a query or category |
|
not(hasParsedTextProperties.any('query', 'category')), |
|
// there must be a street |
|
hasParsedTextProperties.any('street') |
|
); |
|
|
|
// placeholder should have executed, useful for determining whether to actually |
|
// fallback or not (don't fallback to old search if the placeholder response |
|
// should be honored as is) |
|
const placeholderShouldHaveExecuted = any( |
|
placeholderGeodisambiguationShouldExecute, |
|
placeholderIdsLookupShouldExecute |
|
); |
|
|
|
// don't execute the cascading fallback query IF placeholder should have executed |
|
// that way, if placeholder didn't return anything, don't try to find more things the old way |
|
const fallbackQueryShouldExecute = all( |
|
not(hasRequestErrors), |
|
not(hasResponseData), |
|
not(placeholderShouldHaveExecuted) |
|
); |
|
|
|
// defer to addressit for analysis IF there's no response AND placeholder should not have executed |
|
const shouldDeferToAddressIt = all( |
|
not(hasRequestErrors), |
|
not(hasResponseData), |
|
not(placeholderShouldHaveExecuted) |
|
); |
|
|
|
// call very old prod query if addressit was the parser |
|
const oldProdQueryShouldExecute = all( |
|
not(hasRequestErrors), |
|
isAddressItParse |
|
); |
|
|
|
// get language adjustments if: |
|
// - there's a response |
|
// - theres's a lang parameter in req.clean |
|
const changeLanguageShouldExecute = all( |
|
hasResponseData, |
|
not(hasRequestErrors), |
|
isChangeLanguageEnabled, |
|
hasRequestParameter('lang') |
|
); |
|
|
|
// interpolate if: |
|
// - there's a number and street |
|
// - there are street-layer results (these are results that need to be interpolated) |
|
const interpolationShouldExecute = all( |
|
not(hasRequestErrors), |
|
isInterpolationEnabled, |
|
hasParsedTextProperties.all('number', 'street'), |
|
hasResultsAtLayers('street') |
|
); |
|
|
|
// execute under the following conditions: |
|
// - there are no errors or data |
|
// - request is not coarse OR pip service is disabled |
|
const nonCoarseReverseShouldExecute = all( |
|
not(hasResponseDataOrRequestErrors), |
|
any( |
|
not(isCoarseReverse), |
|
not(isPipServiceEnabled) |
|
) |
|
); |
|
|
|
// helpers to replace vague booleans |
|
const geometricFiltersApply = true; |
|
const geometricFiltersDontApply = false; |
|
|
|
var base = '/v1/'; |
|
|
|
/** ------------------------- routers ------------------------- **/ |
|
|
|
var routers = { |
|
index: createRouter([ |
|
controllers.mdToHTML(peliasConfig.api, './public/apiDoc.md') |
|
]), |
|
attribution: createRouter([ |
|
controllers.mdToHTML(peliasConfig.api, './public/attribution.md') |
|
]), |
|
search: createRouter([ |
|
sanitizers.search.middleware(peliasConfig.api), |
|
middleware.requestLanguage, |
|
middleware.calcSize(), |
|
controllers.libpostal(libpostalService, libpostalShouldExecute), |
|
controllers.placeholder(placeholderService, geometricFiltersApply, placeholderGeodisambiguationShouldExecute), |
|
controllers.placeholder(placeholderService, geometricFiltersDontApply, placeholderIdsLookupShouldExecute), |
|
controllers.search_with_ids(peliasConfig.api, esclient, queries.address_using_ids, searchWithIdsShouldExecute), |
|
// 3rd parameter is which query module to use, use fallback first, then |
|
// use original search strategy if first query didn't return anything |
|
controllers.search(peliasConfig.api, esclient, queries.cascading_fallback, fallbackQueryShouldExecute), |
|
sanitizers.defer_to_addressit(shouldDeferToAddressIt), |
|
controllers.search(peliasConfig.api, esclient, queries.very_old_prod, oldProdQueryShouldExecute), |
|
postProc.trimByGranularity(), |
|
postProc.distances('focus.point.'), |
|
postProc.confidenceScores(peliasConfig.api), |
|
postProc.confidenceScoresFallback(), |
|
postProc.interpolate(interpolationService, interpolationShouldExecute), |
|
postProc.sortResponseData(require('pelias-sorting'), hasAdminOnlyResults), |
|
postProc.dedupe(), |
|
postProc.accuracy(), |
|
postProc.localNamingConventions(), |
|
postProc.renamePlacenames(), |
|
postProc.parseBoundingBox(), |
|
postProc.normalizeParentIds(), |
|
postProc.changeLanguage(changeLanguageService, changeLanguageShouldExecute), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
structured: createRouter([ |
|
sanitizers.structured_geocoding.middleware(peliasConfig.api), |
|
middleware.requestLanguage, |
|
middleware.calcSize(), |
|
controllers.structured_libpostal(structuredLibpostalService, structuredLibpostalShouldExecute), |
|
controllers.search(peliasConfig.api, esclient, queries.structured_geocoding, not(hasResponseDataOrRequestErrors)), |
|
postProc.trimByGranularityStructured(), |
|
postProc.distances('focus.point.'), |
|
postProc.confidenceScores(peliasConfig.api), |
|
postProc.confidenceScoresFallback(), |
|
postProc.interpolate(interpolationService, interpolationShouldExecute), |
|
postProc.dedupe(), |
|
postProc.accuracy(), |
|
postProc.localNamingConventions(), |
|
postProc.renamePlacenames(), |
|
postProc.parseBoundingBox(), |
|
postProc.normalizeParentIds(), |
|
postProc.changeLanguage(changeLanguageService, changeLanguageShouldExecute), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
autocomplete: createRouter([ |
|
sanitizers.autocomplete.middleware(peliasConfig.api), |
|
middleware.requestLanguage, |
|
controllers.search(peliasConfig.api, esclient, queries.autocomplete, not(hasResponseDataOrRequestErrors)), |
|
postProc.distances('focus.point.'), |
|
postProc.confidenceScores(peliasConfig.api), |
|
postProc.dedupe(), |
|
postProc.accuracy(), |
|
postProc.localNamingConventions(), |
|
postProc.renamePlacenames(), |
|
postProc.parseBoundingBox(), |
|
postProc.normalizeParentIds(), |
|
postProc.changeLanguage(changeLanguageService, changeLanguageShouldExecute), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
reverse: createRouter([ |
|
sanitizers.reverse.middleware, |
|
middleware.requestLanguage, |
|
middleware.calcSize(2), |
|
controllers.search(peliasConfig.api, esclient, queries.reverse, nonCoarseReverseShouldExecute), |
|
controllers.coarse_reverse(pipService, coarseReverseShouldExecute), |
|
postProc.distances('point.'), |
|
// reverse confidence scoring depends on distance from origin |
|
// so it must be calculated first |
|
postProc.confidenceScoresReverse(), |
|
postProc.dedupe(), |
|
postProc.accuracy(), |
|
postProc.localNamingConventions(), |
|
postProc.renamePlacenames(), |
|
postProc.parseBoundingBox(), |
|
postProc.normalizeParentIds(), |
|
postProc.changeLanguage(changeLanguageService, changeLanguageShouldExecute), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
nearby: createRouter([ |
|
sanitizers.nearby.middleware, |
|
middleware.requestLanguage, |
|
middleware.calcSize(), |
|
controllers.search(peliasConfig.api, esclient, queries.reverse, not(hasResponseDataOrRequestErrors)), |
|
postProc.distances('point.'), |
|
// reverse confidence scoring depends on distance from origin |
|
// so it must be calculated first |
|
postProc.confidenceScoresReverse(), |
|
postProc.dedupe(), |
|
postProc.accuracy(), |
|
postProc.localNamingConventions(), |
|
postProc.renamePlacenames(), |
|
postProc.parseBoundingBox(), |
|
postProc.normalizeParentIds(), |
|
postProc.changeLanguage(changeLanguageService, changeLanguageShouldExecute), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
place: createRouter([ |
|
sanitizers.place.middleware, |
|
middleware.requestLanguage, |
|
controllers.place(peliasConfig.api, esclient), |
|
postProc.accuracy(), |
|
postProc.localNamingConventions(), |
|
postProc.renamePlacenames(), |
|
postProc.parseBoundingBox(), |
|
postProc.normalizeParentIds(), |
|
postProc.changeLanguage(changeLanguageService, changeLanguageShouldExecute), |
|
postProc.assignLabels(), |
|
postProc.geocodeJSON(peliasConfig.api, base), |
|
postProc.sendJSON |
|
]), |
|
status: createRouter([ |
|
controllers.status |
|
]) |
|
}; |
|
|
|
|
|
//Models |
|
/** |
|
* @swagger |
|
* definitions: |
|
* standardPeliasReturn: |
|
* properties: |
|
* geocoding: |
|
* type: object |
|
* $ref: '#/definitions/geocodingObject' |
|
* type: |
|
* type: string |
|
* features: |
|
* type: array |
|
* items: |
|
* $ref: '#/definitions/featureObject' |
|
* bbox: |
|
* type: array |
|
* items: number |
|
* standardPeliasErrorReturn: |
|
* properties: |
|
* geocoding: |
|
* type: object |
|
* $ref: '#/definitions/geocodingErrorObject' |
|
* type: |
|
* type: string |
|
* features: |
|
* type: array |
|
* items: |
|
* $ref: '#/definitions/featureObject' |
|
* bbox: |
|
* type: array |
|
* items: number |
|
* geocodingObject: |
|
* properties: |
|
* version: |
|
* type: string |
|
* attribution: |
|
* type: string |
|
* query: |
|
* type: object |
|
* engine: |
|
* type: object |
|
* timestamp: |
|
* type: string |
|
* geocodingErrorObject: |
|
* properties: |
|
* version: |
|
* type: string |
|
* attribution: |
|
* type: string |
|
* query: |
|
* type: object |
|
* errors: |
|
* type: array |
|
* items: string |
|
* timestamp: |
|
* type: string |
|
* featureObject: |
|
* properties: |
|
* type: |
|
* type: string |
|
* geometry: |
|
* type: object |
|
* properties: |
|
* type: object |
|
* bbox: |
|
* type: array |
|
* items: number |
|
* convertReturn: |
|
* properties: |
|
* type: |
|
* type: string |
|
* geometry: |
|
* type: object |
|
* properties: |
|
* type: object |
|
* $ref: '#/definitions/convertPropertiesObject' |
|
* bbox: |
|
* type: array |
|
* items: number |
|
* convertPropertiesObject: |
|
* properties: |
|
* from: |
|
* type: string |
|
* to: |
|
* type: string |
|
* name: |
|
* type: string |
|
* convertErrorReturn: |
|
* properties: |
|
* errors: |
|
* type: string |
|
*/ |
|
|
|
/** |
|
* @swagger |
|
* /v1: |
|
* get: |
|
* tags: |
|
* - v1 |
|
* operationId: v1 |
|
* produces: |
|
* - application/json |
|
* summary: Landing page |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* examples: |
|
* application/json: { "markdown": "# Pelias API\n### Version: [1.0](https://github.com/venicegeo/pelias-api/releases)\n### |
|
* [View our documentation on GitHub](https://github.com/venicegeo/pelias-documentation/blob/master/README.md)\n", "html": "<style>ht |
|
* ml{font-family:monospace}</style><h1>Pelias API</h1>\n\n<h3>Version: <a href=\"https://github.com/venicegeo/pelias-api/releases\"> |
|
* 1.0</a></h3>\n\n<h3><a href=\"https://github.com/venicegeo/pelias-documentation/blob/master/README.md\">View our documentation |
|
* on GitHub</a></h3>" } |
|
*/ |
|
app.get ( base, routers.index ); |
|
/** |
|
* @swagger |
|
* /v1/attribution: |
|
* get: |
|
* tags: |
|
* - v1 |
|
* operationId: attribution |
|
* produces: |
|
* - application/json |
|
* summary: landing page w/attribution |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* examples: |
|
* application/json: { |
|
* "markdown": "# Pelias API\n### Version: [1.0](https://github.com/venicegeo/pelias-api/releases)\n |
|
* ### [View our documentation on GitHub](https://github.com/venicegeo/pelias-documentation/blob/master/README.md)\n |
|
* ## Attribution\n* Geocoding by [Pelias](https://pelias.io).\n* Data from\n * [OpenStreetMap](http://www.openstreetmap.org/copyright) |
|
* © OpenStreetMap contributors under [ODbL](http://opendatacommons.org/licenses/odbl/). Also see the [OSM Geocoding Guidelines] |
|
* (https://wiki.osmfoundation.org/wiki/Licence/Community_Guidelines/Geocoding_-_Guideline) for acceptable use.\n |
|
* * [OpenAddresses](http://openaddresses.io) under [various public-domain and share-alike licenses](http://results.openaddresses.io/)\n |
|
* * [GeoNames](http://www.geonames.org/) under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/)\n * [WhosOnFirst] |
|
* (https://www.whosonfirst.org/) under [various CC-BY or CC-0 equivalent licenses](https://whosonfirst.org/docs/licenses/)", |
|
* "html": "<style>html{font-family:monospace}</style><h1>Pelias API</h1>\n\n<h3>Version: |
|
* <a href=\"https://github.com/venicegeo/pelias-api/releases\">1.0</a></h3>\n\n<h3> |
|
* <a href=\"https://github.com/venicegeo/pelias-documentation/blob/master/README.md\">View our documentation on GitHub</a></h3>\n\n |
|
* <h2>Attribution</h2>\n\n<ul><li>Geocoding by <a href=\"https://pelias.io\">Pelias</a>.</li><li>Data from<ul><li> |
|
* <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a> © OpenStreetMap contributors under |
|
* <a href=\"http://opendatacommons.org/licenses/odbl/\">ODbL</a>. Also see the <a href=\"https://wiki.osmfoundation.org/wiki/ |
|
* Licence/Community_Guidelines/Geocoding_-_Guideline\">OSM Geocoding Guidelines</a> for acceptable use.</li><li> |
|
* <a href=\"http://openaddresses.io\">OpenAddresses</a> under <a href=\"http://results.openaddresses.io/\">various |
|
* public-domain and share-alike licenses</a></li><li><a href=\"http://www.geonames.org/\">GeoNames</a> under |
|
* <a href=\"https://creativecommons.org/licenses/by/4.0/\">CC-BY-4.0</a></li><li><a href=\"https://www.whosonfirst.org/\"> |
|
* WhosOnFirst</a> under <a href=\"https://whosonfirst.org/docs/licenses/\">various CC-BY or CC-0 equivalent licenses</a> |
|
* </li></ul></li></ul>" |
|
} |
|
*/ |
|
app.get ( base + 'attribution', routers.attribution ); |
|
app.get ( '/attribution', routers.attribution ); |
|
/** |
|
* @swagger |
|
* /status: |
|
* get: |
|
* tags: |
|
* - base |
|
* operationId: attribution |
|
* produces: |
|
* - text/plain |
|
* summary: Landing page w/attribution |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* examples: |
|
* text/plain: "status: ok" |
|
*/ |
|
app.get ( '/status', routers.status ); |
|
|
|
// backend dependent endpoints |
|
|
|
|
|
/** |
|
* @swagger |
|
* /v1/place: |
|
* get: |
|
* tags: |
|
* - v1 |
|
* operationId: place |
|
* produces: |
|
* - application/json |
|
* summary: For querying specific place ID(s) |
|
* parameters: |
|
* - name: ids |
|
* description: for details on a place returned from a previous query |
|
* in: query |
|
* required: true |
|
* type: array |
|
* items: {"type":"string", "pattern":"^[A-z]*.:[A-z]*.:[0-9]*$"} |
|
* |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasReturn' |
|
* 400: |
|
* description: 400 bad request |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasErrorReturn' |
|
*/ |
|
app.get ( base + 'place', routers.place ); |
|
/** |
|
* @swagger |
|
* /v1/autocomplete: |
|
* get: |
|
* tags: |
|
* - v1 |
|
* operationId: autocomplete |
|
* summary: to give real-time result suggestions without having to type the whole location |
|
* produces: |
|
* - application/json |
|
* parameters: |
|
* - name: text |
|
* description: Text query |
|
* in: query |
|
* required: true |
|
* type: string |
|
* - name: focus.point.lat |
|
* description: Focus point latitude |
|
* in: query |
|
* type: number |
|
* - name: focus.point.lon |
|
* description: Focus point longitude |
|
* in: query |
|
* type: number |
|
* - name: boundary.rect.min_lon |
|
* description: Bounding box minimum longitude |
|
* in: query |
|
* type: number |
|
* - name: boundary.rect.max_lon |
|
* description: Bounding box maximum longitude |
|
* in: query |
|
* type: number |
|
* - name: boundary.rect.min_lat |
|
* description: Bounding box minimum latitude |
|
* in: query |
|
* type: number |
|
* - name: boundary.rect.max_lat |
|
* description: Bounding box maximum latitude |
|
* in: query |
|
* type: number |
|
* - name: sources |
|
* description: Sources |
|
* in: query |
|
* type: string |
|
* enum: [openstreetmap, openaddresses, whosonfirst, geonames] |
|
* - name: layers |
|
* description: Layers |
|
* in: query |
|
* type: string |
|
* enum: [venue, address, street, country, macroregion, region, macrocounty, county, locality, localadmin, borough, |
|
* neighbourhood, coarse] |
|
* - name: boundary.county |
|
* description: Country boundary |
|
* in: query |
|
* type: string |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasReturn' |
|
* 400: |
|
* description: 400 bad request |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasErrorReturn' |
|
*/ |
|
app.get ( base + 'autocomplete', routers.autocomplete ); |
|
/** |
|
* @swagger |
|
* /v1/search: |
|
* get: |
|
* tags: |
|
* - v1 |
|
* operationId: search |
|
* summary: to find a place by searching for an address or name |
|
* produces: |
|
* - application/json |
|
* parameters: |
|
* - name: text |
|
* description: Text query |
|
* in: query |
|
* required: true |
|
* type: string |
|
* - name: size |
|
* description: used to limit the number of results returned. |
|
* in: query |
|
* type: number |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasReturn' |
|
* 400: |
|
* description: 400 bad request |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasErrorReturn' |
|
* post: |
|
* tags: |
|
* - v1 |
|
* operationId: search |
|
* summary: to find a place by searching for an address or name |
|
* produces: |
|
* - application/json |
|
* parameters: |
|
* - name: text |
|
* description: Text query |
|
* in: query |
|
* required: true |
|
* type: string |
|
* - name: size |
|
* description: used to limit the number of results returned. |
|
* in: query |
|
* type: number |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasReturn' |
|
* 400: |
|
* description: 400 bad request |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasErrorReturn' |
|
*/ |
|
|
|
app.get ( base + 'search', routers.search ); |
|
app.post( base + 'search', routers.search ); |
|
/** |
|
* @swagger |
|
* /v1/search/structured: |
|
* get: |
|
* tags: |
|
* - v1 |
|
* operationId: structured |
|
* summary: to find a place with data already separated into housenumber, street, city, etc. |
|
* produces: |
|
* - application/json |
|
* parameters: |
|
* - name: text |
|
* description: Text query |
|
* in: query |
|
* required: true |
|
* type: string |
|
* - name: venue |
|
* description: WOF Venue |
|
* in: query |
|
* type: string |
|
* - name: address |
|
* description: can contain a full address with house number or only a street name. |
|
* in: query |
|
* type: string |
|
* - name: neighbourhood |
|
* description: vernacular geographic entities that may not necessarily be official administrative divisions but are important |
|
* nonetheless. |
|
* in: query |
|
* type: string |
|
* - name: borough |
|
* description: mostly known in the context of New York City, even though they may exist in other cities, such as Mexico City. |
|
* in: query |
|
* type: string |
|
* - name: locality |
|
* description: equivalent to what are commonly referred to as cities. |
|
* in: query |
|
* type: string |
|
* - name: county |
|
* description: administrative divisions between localities and regions. |
|
* in: query |
|
* type: string |
|
* - name: region |
|
* description: the first-level administrative divisions within countries, analogous to states and provinces in the United States |
|
* and Canada, respectively, though most other countries contain regions as well |
|
* in: query |
|
* type: string |
|
* - name: postalcode |
|
* description: used to aid in sorting mail with the format dictated by an administrative division |
|
* in: query |
|
* type: string |
|
* - name: country |
|
* description: highest-level administrative divisions supported in a search. In addition to full names, countries have common |
|
* two- and three-letter abbreviations that are also supported values for the country parameter. |
|
* in: query |
|
* type: string |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasReturn' |
|
* 400: |
|
* description: 400 bad request |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasErrorReturn' |
|
*/ |
|
app.get ( base + 'search/structured', routers.structured ); |
|
/** |
|
* @swagger |
|
* /v1/reverse: |
|
* get: |
|
* tags: |
|
* - v1 |
|
* operationId: reverse |
|
* summary: to find what is located at a certain coordinate location |
|
* produces: |
|
* - application/json |
|
* parameters: |
|
* - name: point.lat |
|
* description: Latitude (decimal degrees) |
|
* in: query |
|
* required: true |
|
* type: string |
|
* - name: point.lon |
|
* description: Longitude (decimal degrees) |
|
* in: query |
|
* required: true |
|
* type: string |
|
* - name: boundary.circle.radius |
|
* description: Bounding circle radius |
|
* in: query |
|
* type: number |
|
* - name: size |
|
* description: used to limit the number of results returned. |
|
* in: query |
|
* type: number |
|
* - name: sources |
|
* description: one or more valid source names |
|
* in: query |
|
* type: string |
|
* enum: [openstreetmap, openaddresses, whosonfirst, geonames] |
|
* - name: layers |
|
* description: Layers |
|
* in: query |
|
* type: string |
|
* enum: [venue, address, street, country, macroregion, region, macrocounty, county, locality, localadmin, borough, |
|
* neighbourhood, coarse] |
|
* - name: boundary.county |
|
* description: Country boundary |
|
* in: query |
|
* type: string |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasReturn' |
|
* 400: |
|
* description: 400 bad request |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasErrorReturn' |
|
*/ |
|
app.get ( base + 'reverse', routers.reverse ); |
|
/** |
|
* @swagger |
|
* /v1/nearby: |
|
* get: |
|
* tags: |
|
* - v1 |
|
* operationId: nearby |
|
* summary: reverse geocode search including surrounding areas |
|
* produces: |
|
* - application/json |
|
* parameters: |
|
* - name: point.lat |
|
* description: Latitude (decimal degrees) |
|
* in: query |
|
* required: true |
|
* type: string |
|
* - name: point.lon |
|
* description: Longitude (decimal degrees) |
|
* in: query |
|
* required: true |
|
* type: string |
|
* responses: |
|
* 200: |
|
* description: 200 ok |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasReturn' |
|
* 400: |
|
* description: 400 bad request |
|
* schema: |
|
* type: object |
|
* $ref: '#/definitions/standardPeliasErrorReturn' |
|
* |
|
*/ |
|
app.get ( base + 'nearby', routers.nearby ); |
|
} |
|
|
|
/** |
|
* Helper function for creating routers |
|
* |
|
* @param {[{function}]} functions |
|
* @returns {express.Router} |
|
*/ |
|
function createRouter(functions) { |
|
var router = Router(); // jshint ignore:line |
|
functions.forEach(function (f) { |
|
router.use(f); |
|
}); |
|
return router; |
|
} |
|
|
|
|
|
module.exports.addRoutes = addRoutes;
|
|
|