const logger = require('pelias-logger').get('coarse_reverse'); const _ = require('lodash'); const Document = require('pelias-model').Document; const granularities = [ 'neighbourhood', 'borough', 'locality', 'localadmin', 'county', 'macrocounty', 'region', 'macroregion', 'dependency', 'country' ]; function getMostGranularLayer(results) { return granularities.find((granularity) => { return results.hasOwnProperty(granularity); }); } function hasResultsAtRequestedLayers(results, layers) { return _.intersection(layers, Object.keys(results)).length > 0; } function synthesizeDoc(results) { // now create a model.Document from what's level, using the most granular // result available as the starting point // the immediately above cannot be re-used since county may be the most // granular layer requested but the results may start at region (no county found) const most_granular_layer = getMostGranularLayer(results); const id = results[most_granular_layer][0].id; const doc = new Document('whosonfirst', most_granular_layer, id.toString()); doc.setName('default', results[most_granular_layer][0].name); if (results[most_granular_layer][0].hasOwnProperty('centroid')) { doc.setCentroid( results[most_granular_layer][0].centroid ); } if (results[most_granular_layer][0].hasOwnProperty('bounding_box')) { const parsedBoundingBox = results[most_granular_layer][0].bounding_box.split(',').map(parseFloat); doc.setBoundingBox({ upperLeft: { lat: parsedBoundingBox[3], lon: parsedBoundingBox[0] }, lowerRight: { lat: parsedBoundingBox[1], lon: parsedBoundingBox[2] } }); } if (_.has(results, 'country[0].abbr')) { doc.setAlpha3(results.country[0].abbr); } // assign the administrative hierarchy Object.keys(results).forEach((layer) => { if (results[layer][0].hasOwnProperty('abbr')) { doc.addParent(layer, results[layer][0].name, results[layer][0].id.toString(), results[layer][0].abbr); } else { doc.addParent(layer, results[layer][0].name, results[layer][0].id.toString()); } }); const esDoc = doc.toESDocument(); esDoc.data._id = esDoc._id; esDoc.data._type = esDoc._type; return esDoc.data; } function setup(service, should_execute) { function controller(req, res, next) { // do not run controller when a request validation error has occurred if (!should_execute(req, res)) { return next(); } service(req, (err, results) => { // if there's an error, log it and bail if (err) { logger.info(`[controller:coarse_reverse][error]`); logger.error(err); return next(); } // find the finest granularity requested const finest_granularity_requested = granularities.findIndex((granularity) => { return req.clean.layers.indexOf(granularity) !== -1; }); logger.info(`[controller:coarse_reverse][queryType:pip][result_count:${Object.keys(results).length}]`); // now remove everything from the response that is more granular than the // most granular layer requested. that is, if req.clean.layers=['county'], // remove neighbourhoods, localities, and localadmins Object.keys(results).forEach((layer) => { if (granularities.indexOf(layer) < finest_granularity_requested) { delete results[layer]; } }); res.meta = {}; res.data = []; // synthesize a doc from results if there's a result at the request layer(s) if (hasResultsAtRequestedLayers(results, req.clean.layers)) { res.data.push(synthesizeDoc(results)); } return next(); }); } return controller; } module.exports = setup;