Browse Source

More property renaming/mapping

* Rename `layer` to `type` and `source`
* Rename `address` block and merge properties to main `geocoding` namespace
pull/210/head
Diana Shkolnikov 9 years ago
parent
commit
1b4b401a66
  1. 141
      helper/geojsonify.js
  2. 4
      helper/outputGenerator.js
  3. 12
      helper/outputSchema.json
  4. 17
      middleware/renamePlacenames.js
  5. 143
      test/unit/helper/geojsonify.js
  6. 6
      test/unit/helper/outputSchema.js

141
helper/geojsonify.js

@ -5,70 +5,113 @@ var GeoJSON = require('geojson'),
// Properties to be copied when details=true // Properties to be copied when details=true
var DETAILS_PROPS = [ var DETAILS_PROPS = [
'alpha3', 'housenumber',
'admin0', 'street',
'admin1',
'admin1_abbr',
'admin2',
'local_admin',
'locality',
'neighborhood',
'category', 'category',
'address', 'postalcode',
'country_a', 'country_a',
'country', 'country',
'region', 'region',
'region_a', 'region_a',
'county', 'county',
'localadmin',
'locality',
'neighbourhood' 'neighbourhood'
]; ];
var META_MAP = {
function search( docs, params ){ 'geoname': { type: '???', source: 'gn' }, // TODO: not sure how to map. will need to use categories?
'osmnode': { type: 'venue', source: 'osm' },
'osmway': { type: 'venue', source: 'osm' },
'admin0': { type: 'country', source: 'qs' },
'admin1': { type: 'region', source: 'qs' },
'admin2': { type: 'county', source: 'qs' },
'neighborhood': { type: 'neighbourhood', source: 'qs' },
'locality': { type: 'locality', source: 'qs' },
'local_admin': { type: 'local_admin', source: 'qs' },
'osmaddress': { type: 'address', source: 'osm' },
'openaddresses': { type: 'address', source: 'oa' }
};
function geojsonifyPlaces( docs, params ){
var details = params ? params.details : {}; var details = params ? params.details : {};
details = details === true || details === 1; details = details === true || details === 1;
// flatten & expand data for geojson conversion // flatten & expand data for geojson conversion
var geodata = docs.map( function( doc ) { var geodata = docs
.map(geojsonifyPlace.bind(null, details))
.filter( function( doc ){
return !!doc;
});
// convert to geojson
var geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] });
// bounding box calculations
computeBBox(geojson);
return geojson;
}
function geojsonifyPlace(details, place) {
// something went very wrong // something went very wrong
if( !doc || !doc.hasOwnProperty( 'center_point' ) ) { if( !place || !place.hasOwnProperty( 'center_point' ) ) {
return warning(); return warning('No doc or center_point property');
} }
var geocoding = {};
addMetaData(place, geocoding);
addDetails(details, place, geocoding);
addLabel(place, geocoding);
var output = {}; var output = {};
// provide metadata to consumer output.geocoding = geocoding;
output.id = doc._id; // map center_point for GeoJSON to work properly
output.layer = doc._type; // these should not show up in the final feature properties
output.lat = parseFloat(place.center_point.lat);
output.lng = parseFloat(place.center_point.lon);
// map center_point return output;
output.lat = parseFloat( doc.center_point.lat ); }
output.lng = parseFloat( doc.center_point.lon );
/**
* Add details properties when needed
*
* @param {boolean} details
* @param {object} src
* @param {object} dst
*/
function addDetails(details, src, dst) {
if (details) { if (details) {
// map name // map name
if( !doc.name || !doc.name.default ) { return warning(); } if( !src.name || !src.name.default ) { return warning(src); }
output.name = doc.name.default; dst.name = src.name.default;
copyProperties( doc, DETAILS_PROPS, output ); copyProperties(src, DETAILS_PROPS, dst);
} }
}
// generate region-specific text string /**
output.text = outputGenerator( doc ); * Add region-specific label string
*
return output; * @param {object} src
* @param {object} dst
// filter-out invalid entries */
}).filter( function( doc ){ function addLabel(src, dst) {
return doc; dst.label = outputGenerator(src);
}); }
// convert to geojson
var geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] });
// bounding box calculations /**
* Compute bbox that encompasses all features in the result set.
* Set bbox property on the geojson object.
*
* @param {object} geojson
*/
function computeBBox(geojson) {
// @note: extent() sometimes throws Errors for unusual data // @note: extent() sometimes throws Errors for unusual data
// eg: https://github.com/pelias/pelias/issues/84 // eg: https://github.com/pelias/pelias/issues/84
try { try {
@ -80,8 +123,6 @@ function search( docs, params ){
console.error( 'bbox error', e.message, e.stack ); console.error( 'bbox error', e.message, e.stack );
console.error( 'geojson', JSON.stringify( geojson, null, 2 ) ); console.error( 'geojson', JSON.stringify( geojson, null, 2 ) );
} }
return geojson;
} }
/** /**
@ -90,16 +131,30 @@ function search( docs, params ){
* *
* @param {object} source * @param {object} source
* @param {[]} props * @param {[]} props
* @param {object} dest * @param {object} dst
*/ */
function copyProperties( source, props, dest ) { function copyProperties( source, props, dst ) {
props.forEach( function ( prop ) { props.forEach( function ( prop ) {
if ( source.hasOwnProperty( prop ) ) { if ( source.hasOwnProperty( prop ) ) {
dest[prop] = source[prop]; dst[prop] = source[prop];
} }
}); });
} }
/**
* Determine and set place id, type, and source
*
* @param {object} src
* @param {object} dst
*/
function addMetaData(src, dst) {
// lookup mapping, or set both values to _type if not found
var meta = META_MAP[src._type] || { type: src._type, source: src._type };
dst.id = src._id;
dst.type = meta.type;
dst.source = meta.source;
}
/** /**
* emit a warning if the doc format is invalid * emit a warning if the doc format is invalid
@ -107,9 +162,9 @@ function copyProperties( source, props, dest ) {
* @note: if you see this error, fix it ASAP! * @note: if you see this error, fix it ASAP!
*/ */
function warning( doc ) { function warning( doc ) {
console.error( 'error: invalid doc', __filename, doc ); console.error( 'error: invalid doc', __filename, doc);
return false; // remove offending doc from results return false; // remove offending doc from results
} }
module.exports.search = search; module.exports.search = geojsonifyPlaces;

4
helper/outputGenerator.js

@ -7,8 +7,8 @@ module.exports = function( record ){
var schema = schemas.default; var schema = schemas.default;
if (record.alpha3 && record.alpha3.length && schemas[record.alpha3]) { if (record.country_a && record.country_a.length && schemas[record.country_a]) {
schema = schemas[record.alpha3]; schema = schemas[record.country_a];
} }
var buildOutput = function(parts, schemaArr, record) { var buildOutput = function(parts, schemaArr, record) {

12
helper/outputSchema.json

@ -1,14 +1,14 @@
{ {
"USA": { "USA": {
"local": ["local_admin", "locality", "neighborhood", "admin2"], "local": ["localadmin", "locality", "neighbourhood", "county"],
"regional": ["admin1_abbr", "admin1", "admin0"] "regional": ["region_a", "region", "country"]
}, },
"GBR": { "GBR": {
"local": ["neighborhood", "admin2", "local_admin", "locality"], "local": ["neighbourhood", "county", "localadmin", "locality"],
"regional": ["admin2","admin0","admin1"] "regional": ["county","country","region"]
}, },
"default": { "default": {
"local": ["local_admin", "locality", "neighborhood", "admin2"], "local": ["localadmin", "locality", "neighbourhood", "county"],
"regional": ["admin1_abbr", "admin1", "admin0"] "regional": ["region_a", "region", "country"]
} }
} }

17
middleware/renamePlacenames.js

@ -1,3 +1,5 @@
var extend = require('extend');
/** /**
- P is a preferred English name - P is a preferred English name
- Q is a preferred name (in other languages) - Q is a preferred name (in other languages)
@ -11,21 +13,23 @@
*/ */
// config mapping of old names to new ones // config mapping of old names to new ones
var NAME_MAP = { var NAME_MAP = {
'number': 'housenumber',
'zip': 'postalcode',
'alpha3': 'country_a', 'alpha3': 'country_a',
'admin0': 'country', 'admin0': 'country',
'admin1': 'region', 'admin1': 'region',
'admin1_abbr': 'region_a', 'admin1_abbr': 'region_a',
'admin2': 'county', 'admin2': 'county',
// TODO: what does "local_admin" map to in WOF??? 'local_admin': 'localadmin',
'neighborhood': 'neighbourhood' 'neighborhood': 'neighbourhood'
}; };
function setup() { function setup() {
return mapPlacenames; return renamePlacenames;
} }
function mapPlacenames(req, res, next) { function renamePlacenames(req, res, next) {
// do nothing if no result data set // do nothing if no result data set
if (!req.results.data) { if (!req.results.data) {
@ -41,7 +45,12 @@ function mapPlacenames(req, res, next) {
function renameProperties(place) { function renameProperties(place) {
var newPlace = {}; var newPlace = {};
Object.keys(place).forEach(function (property) { Object.keys(place).forEach(function (property) {
if (property === 'address') {
extend(newPlace, renameProperties(place[property]));
}
else {
renameProperty(place, newPlace, property); renameProperty(place, newPlace, property);
}
}); });
return newPlace; return newPlace;
} }
@ -51,7 +60,7 @@ function renameProperty(oldObj, newObj, property) {
return; return;
} }
newObj[ (NAME_MAP[property] || property) ] = oldObj[property]; newObj[(NAME_MAP[property] || property)] = oldObj[property];
} }
module.exports = setup; module.exports = setup;

143
test/unit/helper/geojsonify.js

@ -48,20 +48,17 @@ module.exports.tests.search = function(test, common) {
'name': { 'name': {
'default': '\'Round Midnight Jazz and Blues Bar' 'default': '\'Round Midnight Jazz and Blues Bar'
}, },
'type': 'node', 'housenumber': '13',
'address': {
'number': '13',
'street': 'Liverpool Road', 'street': 'Liverpool Road',
'zip': 'N1 0RW' 'postalcode': 'N1 0RW',
}, 'country_a': 'GBR',
'alpha3': 'GBR', 'country': 'United Kingdom',
'admin0': 'United Kingdom', 'region': 'Islington',
'admin1': 'Islington', 'region_a': 'ISL',
'admin1_abbr': 'ISL', 'county': 'Angel',
'admin2': 'Angel', 'localadmin': 'test1',
'local_admin': 'test1',
'locality': 'test2', 'locality': 'test2',
'neighborhood': 'test3', 'neighbourhood': 'test3',
'suggest': { 'suggest': {
'input': [ 'input': [
'\'round midnight jazz and blues bar' '\'round midnight jazz and blues bar'
@ -76,7 +73,6 @@ module.exports.tests.search = function(test, common) {
{ {
'_id': 'id2', '_id': 'id2',
'_type': 'type2', '_type': 'type2',
'type': 'way',
'name': { 'name': {
'default': 'Blues Cafe' 'default': 'Blues Cafe'
}, },
@ -84,14 +80,14 @@ module.exports.tests.search = function(test, common) {
'lat': '51.517806', 'lat': '51.517806',
'lon': '-0.101795' 'lon': '-0.101795'
}, },
'alpha3': 'GBR', 'country_a': 'GBR',
'admin0': 'United Kingdom', 'country': 'United Kingdom',
'admin1': 'City And County Of The City Of London', 'region': 'City And County Of The City Of London',
'admin1_abbr': 'COL', 'region_a': 'COL',
'admin2': 'Smithfield', 'county': 'Smithfield',
'local_admin': 'test1', 'localadmin': 'test1',
'locality': 'test2', 'locality': 'test2',
'neighborhood': 'test3', 'neighbourhood': 'test3',
'suggest': { 'suggest': {
'input': [ 'input': [
'blues cafe' 'blues cafe'
@ -102,7 +98,6 @@ module.exports.tests.search = function(test, common) {
{ {
'_id': '34633854', '_id': '34633854',
'_type': 'osmway', '_type': 'osmway',
'type': 'osmway',
'name': { 'name': {
'default': 'Empire State Building' 'default': 'Empire State Building'
}, },
@ -110,14 +105,14 @@ module.exports.tests.search = function(test, common) {
'lat': '40.748432', 'lat': '40.748432',
'lon': '-73.985656' 'lon': '-73.985656'
}, },
'alpha3': 'USA', 'country_a': 'USA',
'admin0': 'United States', 'country': 'United States',
'admin1': 'New York', 'region': 'New York',
'admin1_abbr': 'NY', 'region_a': 'NY',
'admin2': 'New York', 'county': 'New York',
'local_admin': 'Manhattan', 'localadmin': 'Manhattan',
'locality': 'New York', 'locality': 'New York',
'neighborhood': 'Koreatown', 'neighbourhood': 'Koreatown',
'suggest': { 'suggest': {
'input': [ 'input': [
'empire state building' 'empire state building'
@ -145,23 +140,24 @@ module.exports.tests.search = function(test, common) {
] ]
}, },
'properties': { 'properties': {
'geocoding': {
'id': 'id1', 'id': 'id1',
'layer': 'type1', 'type': 'type1',
'text': '\'Round Midnight Jazz and Blues Bar, test3, Angel', 'source': 'type1',
'label': '\'Round Midnight Jazz and Blues Bar, test3, Angel',
'name': '\'Round Midnight Jazz and Blues Bar', 'name': '\'Round Midnight Jazz and Blues Bar',
'alpha3': 'GBR', 'country_a': 'GBR',
'admin0': 'United Kingdom', 'country': 'United Kingdom',
'admin1': 'Islington', 'region': 'Islington',
'admin1_abbr': 'ISL', 'region_a': 'ISL',
'admin2': 'Angel', 'county': 'Angel',
'local_admin': 'test1', 'localadmin': 'test1',
'locality': 'test2', 'locality': 'test2',
'neighborhood': 'test3', 'neighbourhood': 'test3',
'category': [ 'food', 'nightlife' ], 'category': ['food', 'nightlife'],
'address': { 'housenumber': '13',
'number': '13',
'street': 'Liverpool Road', 'street': 'Liverpool Road',
'zip': 'N1 0RW' 'postalcode': 'N1 0RW'
} }
} }
}, },
@ -175,18 +171,21 @@ module.exports.tests.search = function(test, common) {
] ]
}, },
'properties': { 'properties': {
'geocoding': {
'id': 'id2', 'id': 'id2',
'layer': 'type2', 'type': 'type2',
'text': 'Blues Cafe, test3, Smithfield', 'source': 'type2',
'label': 'Blues Cafe, test3, Smithfield',
'name': 'Blues Cafe', 'name': 'Blues Cafe',
'alpha3': 'GBR', 'country_a': 'GBR',
'admin0': 'United Kingdom', 'country': 'United Kingdom',
'admin1': 'City And County Of The City Of London', 'region': 'City And County Of The City Of London',
'admin1_abbr': 'COL', 'region_a': 'COL',
'admin2': 'Smithfield', 'county': 'Smithfield',
'local_admin': 'test1', 'localadmin': 'test1',
'locality': 'test2', 'locality': 'test2',
'neighborhood': 'test3' 'neighbourhood': 'test3'
}
} }
}, },
{ {
@ -199,19 +198,22 @@ module.exports.tests.search = function(test, common) {
] ]
}, },
'properties': { 'properties': {
'geocoding': {
'id': '34633854', 'id': '34633854',
'layer': 'osmway', 'type': 'venue',
'text': 'Empire State Building, Manhattan, NY', 'source': 'osm',
'label': 'Empire State Building, Manhattan, NY',
'name': 'Empire State Building', 'name': 'Empire State Building',
'alpha3': 'USA', 'country_a': 'USA',
'admin0': 'United States', 'country': 'United States',
'admin1': 'New York', 'region': 'New York',
'admin1_abbr': 'NY', 'region_a': 'NY',
'admin2': 'New York', 'county': 'New York',
'local_admin': 'Manhattan', 'localadmin': 'Manhattan',
'locality': 'New York', 'locality': 'New York',
'neighborhood': 'Koreatown', 'neighbourhood': 'Koreatown',
'category': [ 'tourism', 'transport' ] 'category': ['tourism', 'transport']
}
} }
} }
] ]
@ -247,9 +249,12 @@ module.exports.tests.search = function(test, common) {
] ]
}, },
'properties': { 'properties': {
'geocoding': {
'id': 'id1', 'id': 'id1',
'layer': 'type1', 'type': 'type1',
'text': '\'Round Midnight Jazz and Blues Bar, test3, Angel' 'source': 'type1',
'label': '\'Round Midnight Jazz and Blues Bar, test3, Angel'
}
} }
}, },
{ {
@ -262,9 +267,12 @@ module.exports.tests.search = function(test, common) {
] ]
}, },
'properties': { 'properties': {
'geocoding': {
'id': 'id2', 'id': 'id2',
'layer': 'type2', 'type': 'type2',
'text': 'Blues Cafe, test3, Smithfield' 'source': 'type2',
'label': 'Blues Cafe, test3, Smithfield'
}
} }
}, },
{ {
@ -277,9 +285,12 @@ module.exports.tests.search = function(test, common) {
] ]
}, },
'properties': { 'properties': {
'geocoding': {
'id': '34633854', 'id': '34633854',
'layer': 'osmway', 'type': 'venue',
'text': 'Empire State Building, Manhattan, NY' 'source': 'osm',
'label': 'Empire State Building, Manhattan, NY'
}
} }
} }
] ]

6
test/unit/helper/outputSchema.js

@ -13,10 +13,10 @@ module.exports.tests.interface = function(test, common) {
}; };
module.exports.tests.valid = function(test, common) { module.exports.tests.valid = function(test, common) {
var valid_keys = ['local_admin', 'locality', 'neighborhood', 'admin2', 'admin1_abbr', 'admin1', 'admin0']; var valid_keys = ['localadmin', 'locality', 'neighbourhood', 'county', 'region_a', 'region', 'country'];
var default_schema = { var default_schema = {
local: ['local_admin', 'locality', 'neighborhood', 'admin2'], local: ['localadmin', 'locality', 'neighbourhood', 'county'],
regional: ['admin1_abbr', 'admin1', 'admin0'] regional: ['region_a', 'region', 'country']
}; };
var isValid = function(keys, schema) { var isValid = function(keys, schema) {

Loading…
Cancel
Save