mapbox
Version:
interface to mapbox services
272 lines (241 loc) • 11.6 kB
JavaScript
;
var invariant = require('../../vendor/invariant');
var makeService = require('../make_service');
/**
* @class MapboxGeocoding
*/
var MapboxGeocoding = makeService('MapboxGeocoding');
var API_GEOCODING_FORWARD = '/geocoding/v5/{dataset}/{query}.json{?access_token,proximity,country,types,bbox,limit,autocomplete,language}';
var API_GEOCODING_REVERSE = '/geocoding/v5/{dataset}/{longitude},{latitude}.json{?access_token,types,limit,language}';
var REVERSE_GEOCODING_PRECISION = 5;
var FORWARD_GEOCODING_PROXIMITY_PRECISION = 3;
function roundTo(value, places) {
var mult = Math.pow(10, places);
return Math.round(value * mult) / mult;
}
/**
* Search for a location with a string, using the
* [Mapbox Geocoding API](https://www.mapbox.com/api-documentation/#geocoding).
*
* The `query` parmeter can be an array of strings only if batch geocoding
* is used by specifying `mapbox.places-permanent` as the `dataset` option.
*
* @param {string|Array<string>} query desired location
* @param {Object} [options={}] additional options meant to tune
* the request
* @param {Object} options.proximity a proximity argument: this is
* a geographical point given as an object with latitude and longitude
* properties. Search results closer to this point will be given
* higher priority.
* @param {Array} options.bbox a bounding box argument: this is
* a bounding box given as an array in the format [minX, minY, maxX, maxY].
* Search results will be limited to the bounding box.
* @param {Array<string>|string} options.language Specify the language to use for response text and, for forward geocoding, query result weighting. Options are IETF language tags comprised of a mandatory ISO 639-1 language code and optionally one or more IETF subtags for country or script. More than one value can also be specified, as an array or separated by commas.
* @param {Array<string>|string} options.types an array or comma seperated list of types that filter
* results to match those specified. See https://www.mapbox.com/developers/api/geocoding/#filter-type
* for available types.
* @param {number} [options.limit=5] is the maximum number of results to return, between 1 and 10 inclusive.
* Some very specific queries may return fewer results than the limit.
* @param {Array<string>|string} options.country an array or comma separated list of country codes to
* limit results to specified country or countries.
* @param {boolean} [options.autocomplete=true] whether to include results that include
* the query only as a prefix. This is useful for UIs where users type
* values, but if you have complete addresses as input, you'll want to turn it off
* @param {string} [options.dataset=mapbox.places] the desired data to be
* geocoded against. The default, mapbox.places, does not permit unlimited
* caching. `mapbox.places-permanent` is available on request and does
* permit permanent caching.
* @param {Function} callback called with (err, results)
* @returns {Promise} response
* @example
* var mapboxClient = new MapboxClient('ACCESSTOKEN');
* mapboxClient.geocodeForward('Paris, France', function(err, res) {
* // res is a GeoJSON document with geocoding matches
* });
* // using the proximity option to weight results closer to texas
* mapboxClient.geocodeForward('Paris, France', {
* proximity: { latitude: 33.6875431, longitude: -95.4431142 }
* }, function(err, res) {
* // res is a GeoJSON document with geocoding matches
* });
* // using the bbox option to limit results to a portion of Washington, D.C.
* mapboxClient.geocodeForward('Starbucks', {
* bbox: [-77.083056,38.908611,-76.997778,38.959167]
* }, function(err, res) {
* // res is a GeoJSON document with geocoding matches
* });
*/
MapboxGeocoding.prototype.geocodeForward = function(query, options, callback) {
// permit the options argument to be omitted, or the options + callback args to be omitted if using promise syntax
if (callback === undefined && (options === undefined || typeof options === 'function')) {
callback = options;
options = {};
}
// typecheck arguments
if (Array.isArray(query)) {
if (options.dataset !== 'mapbox.places-permanent') {
throw new Error('Batch geocoding is only available with the mapbox.places-permanent endpoint. See https://mapbox.com/api-documentation/#batch-requests for details');
} else {
query = query.join(';');
}
}
invariant(typeof query === 'string', 'query must be a string');
invariant(typeof options === 'object', 'options must be an object');
var queryOptions = {
query: query,
dataset: 'mapbox.places'
};
var precision = FORWARD_GEOCODING_PROXIMITY_PRECISION;
if (options.precision) {
invariant(typeof options.precision === 'number', 'precision option must be number');
precision = options.precision;
}
if (options.proximity) {
invariant(typeof options.proximity.latitude === 'number' &&
typeof options.proximity.longitude === 'number',
'proximity must be an object with numeric latitude & longitude properties');
queryOptions.proximity = roundTo(options.proximity.longitude, precision) + ',' + roundTo(options.proximity.latitude, precision);
}
if (options.bbox) {
invariant(typeof options.bbox[0] === 'number' &&
typeof options.bbox[1] === 'number' &&
typeof options.bbox[2] === 'number' &&
typeof options.bbox[3] === 'number' &&
options.bbox.length === 4,
'bbox must be an array with numeric values in the form [minX, minY, maxX, maxY]');
queryOptions.bbox = options.bbox[0] + ',' + options.bbox[1] + ',' + options.bbox[2] + ',' + options.bbox[3];
}
if (options.limit) {
invariant(typeof options.limit === 'number',
'limit must be a number');
queryOptions.limit = options.limit;
}
if (options.dataset) {
invariant(typeof options.dataset === 'string', 'dataset option must be string');
queryOptions.dataset = options.dataset;
}
if (options.country) {
if (Array.isArray(options.country)) {
queryOptions.country = options.country.join(',');
} else {
invariant(typeof options.country === 'string', 'country option must be an array or string');
queryOptions.country = options.country;
}
}
if (options.language) {
if (Array.isArray(options.language)) {
queryOptions.language = options.language.join(',');
} else {
invariant(typeof options.language === 'string', 'language option must be an array or string');
queryOptions.language = options.language;
}
}
if (options.types) {
if (Array.isArray(options.types)) {
queryOptions.types = options.types.join(',');
} else {
invariant(typeof options.types === 'string', 'types option must be an array or string');
queryOptions.types = options.types;
}
}
if (typeof options.autocomplete === 'boolean') {
invariant(typeof options.autocomplete === 'boolean', 'autocomplete must be a boolean');
queryOptions.autocomplete = options.autocomplete;
}
return this.client({
path: API_GEOCODING_FORWARD,
params: queryOptions,
callback: callback
});
};
/**
* Given a location, determine what geographical features are located
* there. This uses the [Mapbox Geocoding API](https://www.mapbox.com/api-documentation/#geocoding).
*
* @param {Object} location the geographical point to search
* @param {number} location.latitude decimal degrees latitude, in range -90 to 90
* @param {number} location.longitude decimal degrees longitude, in range -180 to 180
* @param {Object} [options={}] additional options meant to tune
* the request.
* @param {Array<string>|string} options.language Specify the language to use for response text and, for forward geocoding, query result weighting. Options are IETF language tags comprised of a mandatory ISO 639-1 language code and optionally one or more IETF subtags for country or script. More than one value can also be specified, separated by commas or as an array.
* @param {Array<string>|string} options.types an array or comma seperated list of types that filter
* results to match those specified. See
* https://www.mapbox.com/api-documentation/#retrieve-places-near-a-location
* for available types.
* @param {number} [options.limit=1] is the maximum number of results to return, between 1 and 5
* inclusive. Requires a single options.types to be specified (see example).
* @param {string} [options.dataset=mapbox.places] the desired data to be
* geocoded against. The default, mapbox.places, does not permit unlimited
* caching. `mapbox.places-permanent` is available on request and does
* permit permanent caching.
* @param {Function} callback called with (err, results)
* @returns {Promise} response
* @example
* var mapboxClient = new MapboxClient('ACCESSTOKEN');
* mapboxClient.geocodeReverse(
* { latitude: 33.6875431, longitude: -95.4431142 },
* function(err, res) {
* // res is a GeoJSON document with geocoding matches
* });
* @example
* var mapboxClient = new MapboxClient('ACCESSTOKEN');
* mapboxClient.geocodeReverse(
* { latitude: 33.6875431, longitude: -95.4431142, options: { types: 'address', limit: 3 } },
* function(err, res) {
* // res is a GeoJSON document with up to 3 geocoding matches
* });
*/
MapboxGeocoding.prototype.geocodeReverse = function(location, options, callback) {
// permit the options argument to be omitted, or the options + callback args to be omitted if using promise syntax
if (callback === undefined && (options === undefined || typeof options === 'function')) {
callback = options;
options = {};
}
// typecheck arguments
invariant((typeof location === 'object' && location !== null), 'location must be an object');
invariant(typeof options === 'object', 'options must be an object');
invariant(typeof location.latitude === 'number' &&
typeof location.longitude === 'number',
'location must be an object with numeric latitude & longitude properties');
var queryOptions = {
dataset: 'mapbox.places'
};
if (options.dataset) {
invariant(typeof options.dataset === 'string', 'dataset option must be string');
queryOptions.dataset = options.dataset;
}
var precision = REVERSE_GEOCODING_PRECISION;
if (options.precision) {
invariant(typeof options.precision === 'number', 'precision option must be number');
precision = options.precision;
}
if (options.language) {
if (Array.isArray(options.language)) {
queryOptions.language = options.language.join(',');
} else {
invariant(typeof options.language === 'string', 'language option must be an array or string');
queryOptions.language = options.language;
}
}
if (options.types) {
if (Array.isArray(options.types)) {
queryOptions.types = options.types.join(',');
} else {
invariant(typeof options.types === 'string', 'types option must be an array or string');
queryOptions.types = options.types;
}
}
if (options.limit) {
invariant(typeof options.limit === 'number', 'limit option must be a number');
invariant(options.types.split(',').length === 1, 'a single type must be specified to use the limit option');
queryOptions.limit = options.limit;
}
queryOptions.longitude = roundTo(location.longitude, precision);
queryOptions.latitude = roundTo(location.latitude, precision);
return this.client({
path: API_GEOCODING_REVERSE,
params: queryOptions,
callback: callback
});
};
module.exports = MapboxGeocoding;