node-geocoder
Version:
Node Geocoder, node geocoding library, supports google maps, mapquest, open street map, tom tom, promise
171 lines (147 loc) • 4.14 kB
JavaScript
const AbstractGeocoder = require('./abstractgeocoder');
/**
* available options
* @see https://docs.mapbox.com/api/search/geocoding/
*/
const OPTIONS = [
'apiKey',
'language',
'country',
'autocomplete',
'bbox',
'fuzzyMatch',
'limit',
'proximity',
'routing'
];
const OPTIONS_MAP = {
apiKey: 'access_token'
};
/**
* Constructor
* @param <object> httpAdapter Http Adapter
* @param <object> options Options (apiKey, language, country, autocomplete, bbox, fuzzyMatch, limit, proximity, routing)
*/
class MapBoxGeocoder extends AbstractGeocoder {
constructor(httpAdapter, options) {
super(httpAdapter, options);
this.options = options || {};
if (!this.options.apiKey) {
throw new Error('You must specify apiKey to use MapBox Geocoder');
}
}
/**
* Geocode
* @param <string> value Value to geocode (Address)
* @param <function> callback Callback method
*/
_geocode(value, callback) {
let params = this._prepareQueryString({});
let searchtext = value;
if (value.address) {
params = this._prepareQueryString(value);
searchtext = value.address;
}
const endpoint = `${this._geocodeEndpoint}/${encodeURIComponent(
searchtext
)}.json`;
this.httpAdapter.get(endpoint, params, (err, result) => {
let results = [];
results.raw = result;
if (err) {
return callback(err, results);
} else {
const view = result.features;
if (!view) {
return callback(false, results);
}
results = view.map(this._formatResult);
results.raw = result;
callback(false, results);
}
});
}
/**
* Reverse geocoding
* @param {lat:<number>,lon:<number>} lat: Latitude, lon: Longitude
* @param <function> callback Callback method
*/
_reverse(query, callback) {
const { lat, lon, ...other } = query;
const params = this._prepareQueryString(other);
const endpoint = `${this._geocodeEndpoint}/${encodeURIComponent(
`${lon},${lat}`
)}.json`;
this.httpAdapter.get(endpoint, params, (err, result) => {
let results = [];
results.raw = result;
if (err) {
return callback(err, results);
} else {
const view = result.features;
if (!view) {
return callback(false, results);
}
results = view.map(this._formatResult);
results.raw = result;
callback(false, results);
}
});
}
_formatResult(result) {
const context = (result.context || []).reduce((o, item) => {
// possible types: country, region, postcode, district, place, locality, neighborhood, address
const [type] = item.id.split('.');
if (type) {
o[type] = item.text;
if (type === 'country' && item.short_code) {
o.countryCode = item.short_code.toUpperCase();
}
}
return o;
}, {});
// get main type
const [type] = result.id.split('.');
if (type) {
context[type] = result.text;
}
const properties = result.properties || {};
const extractedObj = {
latitude: result.center[1],
longitude: result.center[0],
formattedAddress: result.place_name,
country: context.country,
countryCode: context.countryCode,
state: context.region,
district: context.district,
city: context.place,
zipcode: context.postcode,
neighbourhood: context.neighborhood || context.locality,
extra: {
id: result.id,
address: properties.address || context.address,
category: properties.category,
bbox: result.bbox
}
};
return extractedObj;
}
_prepareQueryString(params) {
OPTIONS.forEach(key => {
const val = this.options[key];
if (val) {
const _key = OPTIONS_MAP[key] || key;
params[_key] = val;
}
});
return params;
}
}
Object.defineProperties(MapBoxGeocoder.prototype, {
_geocodeEndpoint: {
get: function () {
return 'https://api.mapbox.com/geocoding/v5/mapbox.places';
}
}
});
module.exports = MapBoxGeocoder;