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. 147
      helper/geojsonify.js
  2. 4
      helper/outputGenerator.js
  3. 12
      helper/outputSchema.json
  4. 19
      middleware/renamePlacenames.js
  5. 169
      test/unit/helper/geojsonify.js
  6. 6
      test/unit/helper/outputSchema.js

147
helper/geojsonify.js

@ -5,70 +5,113 @@ var GeoJSON = require('geojson'),
// Properties to be copied when details=true
var DETAILS_PROPS = [
'alpha3',
'admin0',
'admin1',
'admin1_abbr',
'admin2',
'local_admin',
'locality',
'neighborhood',
'housenumber',
'street',
'category',
'address',
'postalcode',
'country_a',
'country',
'region',
'region_a',
'county',
'localadmin',
'locality',
'neighbourhood'
];
function search( docs, params ){
var META_MAP = {
'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 : {};
details = details === true || details === 1;
// 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;
});
// something went very wrong
if( !doc || !doc.hasOwnProperty( 'center_point' ) ) {
return warning();
}
// convert to geojson
var geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] });
var output = {};
// bounding box calculations
computeBBox(geojson);
// provide metadata to consumer
output.id = doc._id;
output.layer = doc._type;
return geojson;
}
// map center_point
output.lat = parseFloat( doc.center_point.lat );
output.lng = parseFloat( doc.center_point.lon );
function geojsonifyPlace(details, place) {
if (details) {
// map name
if( !doc.name || !doc.name.default ) { return warning(); }
output.name = doc.name.default;
// something went very wrong
if( !place || !place.hasOwnProperty( 'center_point' ) ) {
return warning('No doc or center_point property');
}
copyProperties( doc, DETAILS_PROPS, output );
}
var geocoding = {};
// generate region-specific text string
output.text = outputGenerator( doc );
addMetaData(place, geocoding);
addDetails(details, place, geocoding);
addLabel(place, geocoding);
return output;
var output = {};
// filter-out invalid entries
}).filter( function( doc ){
return doc;
});
output.geocoding = geocoding;
// map center_point for GeoJSON to work properly
// these should not show up in the final feature properties
output.lat = parseFloat(place.center_point.lat);
output.lng = parseFloat(place.center_point.lon);
// convert to geojson
var geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] });
return output;
}
// bounding box calculations
/**
* Add details properties when needed
*
* @param {boolean} details
* @param {object} src
* @param {object} dst
*/
function addDetails(details, src, dst) {
if (details) {
// map name
if( !src.name || !src.name.default ) { return warning(src); }
dst.name = src.name.default;
copyProperties(src, DETAILS_PROPS, dst);
}
}
/**
* Add region-specific label string
*
* @param {object} src
* @param {object} dst
*/
function addLabel(src, dst) {
dst.label = outputGenerator(src);
}
/**
* 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
// eg: https://github.com/pelias/pelias/issues/84
try {
@ -80,8 +123,6 @@ function search( docs, params ){
console.error( 'bbox error', e.message, e.stack );
console.error( 'geojson', JSON.stringify( geojson, null, 2 ) );
}
return geojson;
}
/**
@ -90,16 +131,30 @@ function search( docs, params ){
*
* @param {object} source
* @param {[]} props
* @param {object} dest
* @param {object} dst
*/
function copyProperties( source, props, dest ) {
function copyProperties( source, props, dst ) {
props.forEach( function ( 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
@ -107,9 +162,9 @@ function copyProperties( source, props, dest ) {
* @note: if you see this error, fix it ASAP!
*/
function warning( doc ) {
console.error( 'error: invalid doc', __filename, doc );
console.error( 'error: invalid doc', __filename, doc);
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;
if (record.alpha3 && record.alpha3.length && schemas[record.alpha3]) {
schema = schemas[record.alpha3];
if (record.country_a && record.country_a.length && schemas[record.country_a]) {
schema = schemas[record.country_a];
}
var buildOutput = function(parts, schemaArr, record) {

12
helper/outputSchema.json

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

19
middleware/renamePlacenames.js

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

169
test/unit/helper/geojsonify.js

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

Loading…
Cancel
Save