// version 0.6.0 (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.mapboxSdk = factory()); }(this, (function () { 'use strict'; // Like https://github.com/thlorenz/lib/parse-link-header but without any // additional dependencies. function parseParam(param) { var parts = param.match(/\s*(.+)\s*=\s*"?([^"]+)"?/); if (!parts) return null; return { key: parts[1], value: parts[2] }; } function parseLink(link) { var parts = link.match(/]*)>(.*)/); if (!parts) return null; var linkUrl = parts[1]; var linkParams = parts[2].split(';'); var rel = null; var parsedLinkParams = linkParams.reduce(function(result, param) { var parsed = parseParam(param); if (!parsed) return result; if (parsed.key === 'rel') { if (!rel) { rel = parsed.value; } return result; } result[parsed.key] = parsed.value; return result; }, {}); if (!rel) return null; return { url: linkUrl, rel: rel, params: parsedLinkParams }; } /** * Parse a Link header. * * @param {string} linkHeader * @returns {{ * [string]: { * url: string, * params: { [string]: string } * } * }} */ function parseLinkHeader(linkHeader) { if (!linkHeader) return {}; return linkHeader.split(/,\s*= 400) { var mapiError$$1 = new mapiError({ request: request, body: xhr.response, statusCode: xhr.status }); reject(mapiError$$1); return; } resolve(xhr); }; var body = request.body; // matching service needs to send a www-form-urlencoded request if (typeof body === 'string') { xhr.send(body); } else if (body) { xhr.send(JSON.stringify(body)); } else if (file) { xhr.send(file); } else { xhr.send(); } requestsUnderway[request.id] = xhr; }).then(function(xhr) { return createResponse(request, xhr); }); } // The accessToken argument gives this function flexibility // for Mapbox's internal client. function createRequestXhr(request, accessToken) { var url = request.url(accessToken); var xhr = new window.XMLHttpRequest(); xhr.open(request.method, url); Object.keys(request.headers).forEach(function(key) { xhr.setRequestHeader(key, request.headers[key]); }); return xhr; } function browserSend(request) { return Promise.resolve().then(function() { var xhr = createRequestXhr(request, request.client.accessToken); return sendRequestXhr(request, xhr); }); } var browserLayer = { browserAbort: browserAbort, sendRequestXhr: sendRequestXhr, browserSend: browserSend, createRequestXhr: createRequestXhr }; var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var base64 = createCommonjsModule(function (module, exports) { (function(root) { // Detect free variables `exports`. var freeExports = exports; // Detect free variable `module`. var freeModule = module && module.exports == freeExports && module; // Detect free variable `global`, from Node.js or Browserified code, and use // it as `root`. var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal; if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { root = freeGlobal; } /*--------------------------------------------------------------------------*/ var InvalidCharacterError = function(message) { this.message = message; }; InvalidCharacterError.prototype = new Error; InvalidCharacterError.prototype.name = 'InvalidCharacterError'; var error = function(message) { // Note: the error messages used throughout this file match those used by // the native `atob`/`btoa` implementation in Chromium. throw new InvalidCharacterError(message); }; var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; // http://whatwg.org/html/common-microsyntaxes.html#space-character var REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g; // `decode` is designed to be fully compatible with `atob` as described in the // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob // The optimized base64-decoding algorithm used is based on @atk’s excellent // implementation. https://gist.github.com/atk/1020396 var decode = function(input) { input = String(input) .replace(REGEX_SPACE_CHARACTERS, ''); var length = input.length; if (length % 4 == 0) { input = input.replace(/==?$/, ''); length = input.length; } if ( length % 4 == 1 || // http://whatwg.org/C#alphanumeric-ascii-characters /[^+a-zA-Z0-9/]/.test(input) ) { error( 'Invalid character: the string to be decoded is not correctly encoded.' ); } var bitCounter = 0; var bitStorage; var buffer; var output = ''; var position = -1; while (++position < length) { buffer = TABLE.indexOf(input.charAt(position)); bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer; // Unless this is the first of a group of 4 characters… if (bitCounter++ % 4) { // …convert the first 8 bits to a single ASCII character. output += String.fromCharCode( 0xFF & bitStorage >> (-2 * bitCounter & 6) ); } } return output; }; // `encode` is designed to be fully compatible with `btoa` as described in the // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa var encode = function(input) { input = String(input); if (/[^\0-\xFF]/.test(input)) { // Note: no need to special-case astral symbols here, as surrogates are // matched, and the input is supposed to only contain ASCII anyway. error( 'The string to be encoded contains characters outside of the ' + 'Latin1 range.' ); } var padding = input.length % 3; var output = ''; var position = -1; var a; var b; var c; var buffer; // Make sure any padding is handled outside of the loop. var length = input.length - padding; while (++position < length) { // Read three bytes, i.e. 24 bits. a = input.charCodeAt(position) << 16; b = input.charCodeAt(++position) << 8; c = input.charCodeAt(++position); buffer = a + b + c; // Turn the 24 bits into four chunks of 6 bits each, and append the // matching character for each of them to the output. output += ( TABLE.charAt(buffer >> 18 & 0x3F) + TABLE.charAt(buffer >> 12 & 0x3F) + TABLE.charAt(buffer >> 6 & 0x3F) + TABLE.charAt(buffer & 0x3F) ); } if (padding == 2) { a = input.charCodeAt(position) << 8; b = input.charCodeAt(++position); buffer = a + b; output += ( TABLE.charAt(buffer >> 10) + TABLE.charAt((buffer >> 4) & 0x3F) + TABLE.charAt((buffer << 2) & 0x3F) + '=' ); } else if (padding == 1) { buffer = input.charCodeAt(position); output += ( TABLE.charAt(buffer >> 2) + TABLE.charAt((buffer << 4) & 0x3F) + '==' ); } return output; }; var base64 = { 'encode': encode, 'decode': decode, 'version': '0.1.0' }; // Some AMD build optimizers, like r.js, check for specific condition patterns // like the following: if (freeExports && !freeExports.nodeType) { if (freeModule) { // in Node.js or RingoJS v0.8.0+ freeModule.exports = base64; } else { // in Narwhal or RingoJS v0.7.0- for (var key in base64) { base64.hasOwnProperty(key) && (freeExports[key] = base64[key]); } } } else { // in Rhino or a web browser root.base64 = base64; } }(commonjsGlobal)); }); var tokenCache = {}; function parseToken(token) { if (tokenCache[token]) { return tokenCache[token]; } var parts = token.split('.'); var usage = parts[0]; var rawPayload = parts[1]; if (!rawPayload) { throw new Error('Invalid token'); } var parsedPayload = parsePaylod(rawPayload); var result = { usage: usage, user: parsedPayload.u }; if (has(parsedPayload, 'a')) result.authorization = parsedPayload.a; if (has(parsedPayload, 'exp')) result.expires = parsedPayload.exp * 1000; if (has(parsedPayload, 'iat')) result.created = parsedPayload.iat * 1000; if (has(parsedPayload, 'scopes')) result.scopes = parsedPayload.scopes; if (has(parsedPayload, 'client')) result.client = parsedPayload.client; if (has(parsedPayload, 'll')) result.lastLogin = parsedPayload.ll; if (has(parsedPayload, 'iu')) result.impersonator = parsedPayload.iu; tokenCache[token] = result; return result; } function parsePaylod(rawPayload) { try { return JSON.parse(base64.decode(rawPayload)); } catch (parseError) { throw new Error('Invalid token'); } } function has(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); } var parseMapboxToken = parseToken; var immutable = extend; var hasOwnProperty = Object.prototype.hasOwnProperty; function extend() { var target = {}; for (var i = 0; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target } var eventemitter3 = createCommonjsModule(function (module) { var has = Object.prototype.hasOwnProperty , prefix = '~'; /** * Constructor to create a storage for our `EE` objects. * An `Events` instance is a plain object whose properties are event names. * * @constructor * @private */ function Events() {} // // We try to not inherit from `Object.prototype`. In some engines creating an // instance in this way is faster than calling `Object.create(null)` directly. // If `Object.create(null)` is not supported we prefix the event names with a // character to make sure that the built-in object properties are not // overridden or used as an attack vector. // if (Object.create) { Events.prototype = Object.create(null); // // This hack is needed because the `__proto__` property is still inherited in // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. // if (!new Events().__proto__) prefix = false; } /** * Representation of a single event listener. * * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} [once=false] Specify if the listener is a one-time listener. * @constructor * @private */ function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } /** * Add a listener for a given event. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} once Specify if the listener is a one-time listener. * @returns {EventEmitter} * @private */ function addListener(emitter, event, fn, context, once) { if (typeof fn !== 'function') { throw new TypeError('The listener must be a function'); } var listener = new EE(fn, context || emitter, once) , evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } /** * Clear event by name. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} evt The Event name. * @private */ function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events(); else delete emitter._events[evt]; } /** * Minimal `EventEmitter` interface that is molded against the Node.js * `EventEmitter` interface. * * @constructor * @public */ function EventEmitter() { this._events = new Events(); this._eventsCount = 0; } /** * Return an array listing the events for which the emitter has registered * listeners. * * @returns {Array} * @public */ EventEmitter.prototype.eventNames = function eventNames() { var names = [] , events , name; if (this._eventsCount === 0) return names; for (name in (events = this._events)) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; /** * Return the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Array} The registered listeners. * @public */ EventEmitter.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event , handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; /** * Return the number of listeners listening to a given event. * * @param {(String|Symbol)} event The event name. * @returns {Number} The number of listeners. * @public */ EventEmitter.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event , listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; /** * Calls each of the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Boolean} `true` if the event had listeners, else `false`. * @public */ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt] , len = arguments.length , args , i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len -1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length , j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; /** * Add a listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; /** * Add a one-time listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; /** * Remove the listeners of a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn Only remove the listeners that match this function. * @param {*} context Only remove the listeners that have this context. * @param {Boolean} once Only remove one-time listeners. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if ( listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context) ) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if ( listeners[i].fn !== fn || (once && !listeners[i].once) || (context && listeners[i].context !== context) ) { events.push(listeners[i]); } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; /** * Remove all listeners, or those of the specified event. * * @param {(String|Symbol)} [event] The event name. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; // // Alias methods names because people roll like that. // EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.addListener = EventEmitter.prototype.on; // // Expose the prefix. // EventEmitter.prefixed = prefix; // // Allow `EventEmitter` to be imported as module namespace. // EventEmitter.EventEmitter = EventEmitter; // // Expose the module. // { module.exports = EventEmitter; } }); // Encode each item of an array individually. The comma // delimiters should not themselves be encoded. function encodeArray(arrayValue) { return arrayValue.map(encodeURIComponent).join(','); } function encodeValue(value) { if (Array.isArray(value)) { return encodeArray(value); } return encodeURIComponent(String(value)); } /** * Append a query parameter to a URL. * * @param {string} url * @param {string} key * @param {string|number|boolean|Array<*>>} [value] - Provide an array * if the value is a list and commas between values need to be * preserved, unencoded. * @returns {string} - Modified URL. */ function appendQueryParam(url, key, value) { if (value === false || value === null) { return url; } var punctuation = /\?/.test(url) ? '&' : '?'; var query = encodeURIComponent(key); if (value !== undefined && value !== '' && value !== true) { query += '=' + encodeValue(value); } return '' + url + punctuation + query; } /** * Derive a query string from an object and append it * to a URL. * * @param {string} url * @param {Object} [queryObject] - Values should be primitives. * @returns {string} - Modified URL. */ function appendQueryObject(url, queryObject) { if (!queryObject) { return url; } var result = url; Object.keys(queryObject).forEach(function(key) { var value = queryObject[key]; if (value === undefined) { return; } if (Array.isArray(value)) { value = value .filter(function(v) { return !!v; }) .join(','); } result = appendQueryParam(result, key, value); }); return result; } /** * Prepend an origin to a URL. If the URL already has an * origin, do nothing. * * @param {string} url * @param {string} origin * @returns {string} - Modified URL. */ function prependOrigin(url, origin) { if (!origin) { return url; } if (url.slice(0, 4) === 'http') { return url; } var delimiter = url[0] === '/' ? '' : '/'; return '' + origin.replace(/\/$/, '') + delimiter + url; } /** * Interpolate values into a route with express-style, * colon-prefixed route parameters. * * @param {string} route * @param {Object} [params] - Values should be primitives * or arrays of primitives. Provide an array if the value * is a list and commas between values need to be * preserved, unencoded. * @returns {string} - Modified URL. */ function interpolateRouteParams(route, params) { if (!params) { return route; } return route.replace(/\/:([a-zA-Z0-9]+)/g, function(_, paramId) { var value = params[paramId]; if (value === undefined) { throw new Error('Unspecified route parameter ' + paramId); } var preppedValue = encodeValue(value); return '/' + preppedValue; }); } var urlUtils = { appendQueryObject: appendQueryObject, appendQueryParam: appendQueryParam, prependOrigin: prependOrigin, interpolateRouteParams: interpolateRouteParams }; var requestId = 1; /** * A Mapbox API request. * * Note that creating a `MapiRequest` does *not* send the request automatically. * Use the request's `send` method to send it off and get a `Promise`. * * The `emitter` property is an `EventEmitter` that emits the following events: * * - `'response'` - Listeners will be called with a `MapiResponse`. * - `'error'` - Listeners will be called with a `MapiError`. * - `'downloadProgress'` - Listeners will be called with `ProgressEvents`. * - `'uploadProgress'` - Listeners will be called with `ProgressEvents`. * Upload events are only available when the request includes a file. * * @class MapiRequest * @property {EventEmitter} emitter - An event emitter. See above. * @property {MapiClient} client - This request's `MapiClient`. * @property {MapiResponse|null} response - If this request has been sent and received * a response, the response is available on this property. * @property {MapiError|Error|null} error - If this request has been sent and * received an error in response, the error is available on this property. * @property {boolean} aborted - If the request has been aborted * (via [`abort`](#abort)), this property will be `true`. * @property {boolean} sent - If the request has been sent, this property will * be `true`. You cannot send the same request twice, so if you need to create * a new request that is the equivalent of an existing one, use * [`clone`](#clone). * @property {string} path - The request's path, including colon-prefixed route * parameters. * @property {string} origin - The request's origin. * @property {string} method - The request's HTTP method. * @property {Object} query - A query object, which will be transformed into * a URL query string. * @property {Object} params - A route parameters object, whose values will * be interpolated the path. * @property {Object} headers - The request's headers. * @property {Object|string|null} body - Data to send with the request. * If the request has a body, it will also be sent with the header * `'Content-Type: application/json'`. * @property {Blob|ArrayBuffer|string|ReadStream} file - A file to * send with the request. The browser client accepts Blobs and ArrayBuffers; * the Node client accepts strings (filepaths) and ReadStreams. */ /** * @ignore * @param {MapiClient} client * @param {Object} options * @param {string} options.method * @param {string} options.path * @param {Object} [options.query={}] * @param {Object} [options.params={}] * @param {string} [options.origin] * @param {Object} [options.headers] * @param {Object} [options.body=null] * @param {Blob|ArrayBuffer|string|ReadStream} [options.file=null] */ function MapiRequest(client, options) { if (!client) { throw new Error('MapiRequest requires a client'); } if (!options || !options.path || !options.method) { throw new Error( 'MapiRequest requires an options object with path and method properties' ); } var defaultHeaders = {}; if (options.body) { defaultHeaders['content-type'] = 'application/json'; } var headersWithDefaults = immutable(defaultHeaders, options.headers); // Disallows duplicate header names of mixed case, // e.g. Content-Type and content-type. var headers = Object.keys(headersWithDefaults).reduce(function(memo, name) { memo[name.toLowerCase()] = headersWithDefaults[name]; return memo; }, {}); this.id = requestId++; this._options = options; this.emitter = new eventemitter3(); this.client = client; this.response = null; this.error = null; this.sent = false; this.aborted = false; this.path = options.path; this.method = options.method; this.origin = options.origin || client.origin; this.query = options.query || {}; this.params = options.params || {}; this.body = options.body || null; this.file = options.file || null; this.headers = headers; } /** * Get the URL of the request. * * @param {string} [accessToken] - By default, the access token of the request's * client is used. * @return {string} */ MapiRequest.prototype.url = function url(accessToken) { var url = urlUtils.prependOrigin(this.path, this.origin); url = urlUtils.appendQueryObject(url, this.query); var routeParams = this.params; if (accessToken) { url = urlUtils.appendQueryParam(url, 'access_token', accessToken); var accessTokenOwnerId = parseMapboxToken(accessToken).user; routeParams = immutable({ ownerId: accessTokenOwnerId }, routeParams); } url = urlUtils.interpolateRouteParams(url, routeParams); return url; }; /** * Send the request. Returns a Promise that resolves with a `MapiResponse`. * You probably want to use `response.body`. * * `send` only retrieves the first page of paginated results. You can get * the next page by using the `MapiResponse`'s [`nextPage`](#nextpage) * function, or iterate through all pages using [`eachPage`](#eachpage) * instead of `send`. * * @returns {Promise} */ MapiRequest.prototype.send = function send() { var self = this; if (self.sent) { throw new Error( 'This request has already been sent. Check the response and error properties. Create a new request with clone().' ); } self.sent = true; return self.client.sendRequest(self).then( function(response) { self.response = response; self.emitter.emit(constants.EVENT_RESPONSE, response); return response; }, function(error) { self.error = error; self.emitter.emit(constants.EVENT_ERROR, error); throw error; } ); }; /** * Abort the request. * * Any pending `Promise` returned by [`send`](#send) will be rejected with * an error with `type: 'RequestAbortedError'`. If you've created a request * that might be aborted, you need to catch and handle such errors. * * This method will also abort any requests created while fetching subsequent * pages via [`eachPage`](#eachpage). * * If the request has not been sent or has already been aborted, nothing * will happen. */ MapiRequest.prototype.abort = function abort() { if (this._nextPageRequest) { this._nextPageRequest.abort(); delete this._nextPageRequest; } if (this.response || this.error || this.aborted) return; this.aborted = true; this.client.abortRequest(this); }; /** * Invoke a callback for each page of a paginated API response. * * The callback should have the following signature: * * ```js * ( * error: MapiError, * response: MapiResponse, * next: () => void * ) => void * ``` * * **The next page will not be fetched until you've invoked the * `next` callback**, indicating that you're ready for it. * * @param {Function} callback */ MapiRequest.prototype.eachPage = function eachPage(callback) { var self = this; function handleResponse(response) { function getNextPage() { delete self._nextPageRequest; var nextPageRequest = response.nextPage(); if (nextPageRequest) { self._nextPageRequest = nextPageRequest; getPage(nextPageRequest); } } callback(null, response, getNextPage); } function handleError(error) { callback(error, null, function() {}); } function getPage(request) { request.send().then(handleResponse, handleError); } getPage(this); }; /** * Clone this request. * * Each request can only be sent *once*. So if you'd like to send the * same request again, clone it and send away. * * @returns {MapiRequest} - A new `MapiRequest` configured just like this one. */ MapiRequest.prototype.clone = function clone() { return this._extend(); }; /** * @ignore */ MapiRequest.prototype._extend = function _extend(options) { var extendedOptions = immutable(this._options, options); return new MapiRequest(this.client, extendedOptions); }; var mapiRequest = MapiRequest; /** * A low-level Mapbox API client. Use it to create service clients * that share the same configuration. * * Services and `MapiRequest`s use the underlying `MapiClient` to * determine how to create, send, and abort requests in a way * that is appropriate to the configuration and environment * (Node or the browser). * * @class MapiClient * @property {string} accessToken - The Mapbox access token assigned * to this client. * @property {string} [origin] - The origin * to use for API requests. Defaults to https://api.mapbox.com. */ function MapiClient(options) { if (!options || !options.accessToken) { throw new Error('Cannot create a client without an access token'); } // Try parsing the access token to determine right away if it's valid. parseMapboxToken(options.accessToken); this.accessToken = options.accessToken; this.origin = options.origin || constants.API_ORIGIN; } MapiClient.prototype.createRequest = function createRequest(requestOptions) { return new mapiRequest(this, requestOptions); }; var mapiClient = MapiClient; function BrowserClient(options) { mapiClient.call(this, options); } BrowserClient.prototype = Object.create(mapiClient.prototype); BrowserClient.prototype.constructor = BrowserClient; BrowserClient.prototype.sendRequest = browserLayer.browserSend; BrowserClient.prototype.abortRequest = browserLayer.browserAbort; /** * Create a client for the browser. * * @param {Object} options * @param {string} options.accessToken * @param {string} [options.origin] * @returns {MapiClient} */ function createBrowserClient(options) { return new BrowserClient(options); } var browserClient = createBrowserClient; var toString = Object.prototype.toString; var isPlainObj = function (x) { var prototype; return toString.call(x) === '[object Object]' && (prototype = Object.getPrototypeOf(x), prototype === null || prototype === Object.getPrototypeOf({})); }; /** * Validators are functions which assert certain type. * They can return a string which can then be used * to display a helpful error message. * They can also return a function for a custom error message. */ var DEFAULT_ERROR_PATH = 'value'; var NEWLINE_INDENT = '\n '; var v = {}; /** * Runners * * Take root validators and run assertion */ v.assert = function(rootValidator, options) { options = options || {}; return function(value) { var message = validate(rootValidator, value); // all good if (!message) { return; } var errorMessage = processMessage(message, options); if (options.apiName) { errorMessage = options.apiName + ': ' + errorMessage; } throw new Error(errorMessage); }; }; /** * Higher Order Validators * * validators which take other validators as input * and output a new validator */ v.shape = function shape(validatorObj) { var validators = objectEntries(validatorObj); return function shapeValidator(value) { var validationResult = validate(v.plainObject, value); if (validationResult) { return validationResult; } var key, validator; var errorMessages = []; for (var i = 0; i < validators.length; i++) { key = validators[i].key; validator = validators[i].value; validationResult = validate(validator, value[key]); if (validationResult) { // return [key].concat(validationResult); errorMessages.push([key].concat(validationResult)); } } if (errorMessages.length < 2) { return errorMessages[0]; } // enumerate all the error messages return function(options) { errorMessages = errorMessages.map(function(message) { var key = message[0]; var renderedMessage = processMessage(message, options) .split('\n') .join(NEWLINE_INDENT); // indents any inner nesting return '- ' + key + ': ' + renderedMessage; }); var objectId = options.path.join('.'); var ofPhrase = objectId === DEFAULT_ERROR_PATH ? '' : ' of ' + objectId; return ( 'The following properties' + ofPhrase + ' have invalid values:' + NEWLINE_INDENT + errorMessages.join(NEWLINE_INDENT) ); }; }; }; v.strictShape = function strictShape(validatorObj) { var shapeValidator = v.shape(validatorObj); return function strictShapeValidator(value) { var shapeResult = shapeValidator(value); if (shapeResult) { return shapeResult; } var invalidKeys = Object.keys(value).reduce(function(memo, valueKey) { if (validatorObj[valueKey] === undefined) { memo.push(valueKey); } return memo; }, []); if (invalidKeys.length !== 0) { return function() { return 'The following keys are invalid: ' + invalidKeys.join(', '); }; } }; }; v.arrayOf = function arrayOf(validator) { return createArrayValidator(validator); }; v.tuple = function tuple() { var validators = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments); return createArrayValidator(validators); }; // Currently array validation fails when the first invalid item is found. function createArrayValidator(validators) { var validatingTuple = Array.isArray(validators); var getValidator = function(index) { if (validatingTuple) { return validators[index]; } return validators; }; return function arrayValidator(value) { var validationResult = validate(v.plainArray, value); if (validationResult) { return validationResult; } if (validatingTuple && value.length !== validators.length) { return 'an array with ' + validators.length + ' items'; } for (var i = 0; i < value.length; i++) { validationResult = validate(getValidator(i), value[i]); if (validationResult) { return [i].concat(validationResult); } } }; } v.required = function required(validator) { function requiredValidator(value) { if (value == null) { return function(options) { return formatErrorMessage( options, isArrayCulprit(options.path) ? 'cannot be undefined/null.' : 'is required.' ); }; } return validator.apply(this, arguments); } requiredValidator.__required = true; return requiredValidator; }; v.oneOfType = function oneOfType() { var validators = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments); return function oneOfTypeValidator(value) { var messages = validators .map(function(validator) { return validate(validator, value); }) .filter(Boolean); // If we don't have as many messages as no. of validators, // then at least one validator was ok with the value. if (messages.length !== validators.length) { return; } // check primitive type if ( messages.every(function(message) { return message.length === 1 && typeof message[0] === 'string'; }) ) { return orList( messages.map(function(m) { return m[0]; }) ); } // Complex oneOfTypes like // `v.oneOftypes(v.shape({name: v.string})`, `v.shape({name: v.number}))` // are complex ¯\_(ツ)_/¯. For the current scope only returning the longest message. return messages.reduce(function(max, arr) { return arr.length > max.length ? arr : max; }); }; }; /** * Meta Validators * which take options as argument (not validators) * and return a new primitive validator */ v.equal = function equal(compareWith) { return function equalValidator(value) { if (value !== compareWith) { return JSON.stringify(compareWith); } }; }; v.oneOf = function oneOf() { var options = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments); var validators = options.map(function(value) { return v.equal(value); }); return v.oneOfType.apply(this, validators); }; v.range = function range(compareWith) { var min = compareWith[0]; var max = compareWith[1]; return function rangeValidator(value) { var validationResult = validate(v.number, value); if (validationResult || value < min || value > max) { return 'number between ' + min + ' & ' + max + ' (inclusive)'; } }; }; /** * Primitive validators * * simple validators which return a string or undefined */ v.any = function any() { return; }; v.boolean = function boolean(value) { if (typeof value !== 'boolean') { return 'boolean'; } }; v.number = function number(value) { if (typeof value !== 'number') { return 'number'; } }; v.plainArray = function plainArray(value) { if (!Array.isArray(value)) { return 'array'; } }; v.plainObject = function plainObject(value) { if (!isPlainObj(value)) { return 'object'; } }; v.string = function string(value) { if (typeof value !== 'string') { return 'string'; } }; v.func = function func(value) { if (typeof value !== 'function') { return 'function'; } }; function validate(validator, value) { // assertions are optional by default unless wrapped in v.require if (value == null && !validator.hasOwnProperty('__required')) { return; } var result = validator(value); if (result) { return Array.isArray(result) ? result : [result]; } } function processMessage(message, options) { // message array follows the convention // [...path, result] // path is an array of object keys / array indices // result is output of the validator var len = message.length; var result = message[len - 1]; var path = message.slice(0, len - 1); if (path.length === 0) { path = [DEFAULT_ERROR_PATH]; } options = immutable(options, { path: path }); return typeof result === 'function' ? result(options) // allows customization of result : formatErrorMessage(options, prettifyResult(result)); } function orList(list) { if (list.length < 2) { return list[0]; } if (list.length === 2) { return list.join(' or '); } return list.slice(0, -1).join(', ') + ', or ' + list.slice(-1); } function prettifyResult(result) { return 'must be ' + addArticle(result) + '.'; } function addArticle(nounPhrase) { if (/^an? /.test(nounPhrase)) { return nounPhrase; } if (/^[aeiou]/i.test(nounPhrase)) { return 'an ' + nounPhrase; } if (/^[a-z]/i.test(nounPhrase)) { return 'a ' + nounPhrase; } return nounPhrase; } function formatErrorMessage(options, prettyResult) { var arrayCulprit = isArrayCulprit(options.path); var output = options.path.join('.') + ' ' + prettyResult; var prepend = arrayCulprit ? 'Item at position ' : ''; return prepend + output; } function isArrayCulprit(path) { return typeof path[path.length - 1] == 'number' || typeof path[0] == 'number'; } function objectEntries(obj) { return Object.keys(obj || {}).map(function(key) { return { key: key, value: obj[key] }; }); } v.validate = validate; v.processMessage = processMessage; var lib = v; function file(value) { // If we're in a browser so Blob is available, the file must be that. // In Node, however, it could be a filepath or a pipeable (Readable) stream. if (typeof window !== 'undefined') { if (value instanceof commonjsGlobal.Blob || value instanceof commonjsGlobal.ArrayBuffer) { return; } return 'Blob or ArrayBuffer'; } if (typeof value === 'string' || value.pipe !== undefined) { return; } return 'Filename or Readable stream'; } function assertShape(validatorObj, apiName) { return lib.assert(lib.strictShape(validatorObj), apiName); } function date(value) { var msg = 'date'; if (typeof value === 'boolean') { return msg; } try { var date = new Date(value); if (date.getTime && isNaN(date.getTime())) { return msg; } } catch (e) { return msg; } } function coordinates(value) { return lib.tuple(lib.number, lib.number)(value); } var validator = immutable(lib, { file: file, date: date, coordinates: coordinates, assertShape: assertShape }); /** * Create a new object by picking properties off an existing object. * The second param can be overloaded as a callback for * more fine grained picking of properties. * @param {Object} source * @param {Array|function(string, Object):boolean} keys * @returns {Object} */ function pick(source, keys) { var filter = function(key, val) { return keys.indexOf(key) !== -1 && val !== undefined; }; if (typeof keys === 'function') { filter = keys; } return Object.keys(source) .filter(function(key) { return filter(key, source[key]); }) .reduce(function(result, key) { result[key] = source[key]; return result; }, {}); } var pick_1 = pick; // This will create the environment-appropriate client. function createServiceFactory(ServicePrototype) { return function(clientOrConfig) { var client; if (mapiClient.prototype.isPrototypeOf(clientOrConfig)) { client = clientOrConfig; } else { client = browserClient(clientOrConfig); } var service = Object.create(ServicePrototype); service.client = client; return service; }; } var createServiceFactory_1 = createServiceFactory; /** * Datasets API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#datasets). */ var Datasets = {}; /** * List datasets in your account. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#list-datasets). * * @return {MapiRequest} * * @example * datasetsClient.listDatasets() * .send() * .then(response => { * const datasets = response.body; * }); * * @example * datasetsClient.listDatasets() * .eachPage((error, response, next) => { * // Handle error or response and call next. * }); */ Datasets.listDatasets = function() { return this.client.createRequest({ method: 'GET', path: '/datasets/v1/:ownerId' }); }; /** * Create a new, empty dataset. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#create-a-dataset). * * @param {Object} config * @param {string} [config.name] * @param {string} [config.description] * @return {MapiRequest} * * @example * datasetsClient.createDataset({ * name: 'example', * description: 'An example dataset' * }) * .send() * .then(response => { * const datasetMetadata = response.body; * }); */ Datasets.createDataset = function(config) { validator.assertShape({ name: validator.string, description: validator.string })(config); return this.client.createRequest({ method: 'POST', path: '/datasets/v1/:ownerId', body: config }); }; /** * Get metadata about a dataset. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-a-dataset). * * @param {Object} config * @param {string} config.datasetId * @return {MapiRequest} * * @example * datasetsClient.getMetadata({ * datasetId: 'dataset-id' * }) * .send() * .then(response => { * const datasetMetadata = response.body; * }) */ Datasets.getMetadata = function(config) { validator.assertShape({ datasetId: validator.required(validator.string), description: validator.string })(config); return this.client.createRequest({ method: 'GET', path: '/datasets/v1/:ownerId/:datasetId', params: config }); }; /** * Update user-defined properties of a dataset's metadata. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#update-a-dataset). * * @param {Object} config * @param {string} config.datasetId * @param {string} [config.name] * @param {string} [config.description] * @return {MapiRequest} * * @example * datasetsClient.updateMetadata({ * datasetId: 'dataset-id', * name: 'foo' * }) * .send() * .then(response => { * const datasetMetadata = response.body; * }); */ Datasets.updateMetadata = function(config) { validator.assertShape({ datasetId: validator.required(validator.string), name: validator.string, description: validator.string })(config); return this.client.createRequest({ method: 'PATCH', path: '/datasets/v1/:ownerId/:datasetId', params: pick_1(config, ['datasetId']), body: pick_1(config, ['name', 'description']) }); }; /** * Delete a dataset, including all features it contains. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#delete-a-dataset). * * @param {Object} config * @param {string} config.datasetId * @return {MapiRequest} * * @example * datasetsClient.deleteDataset({ * datasetId: 'dataset-id' * }) * .send() * .then(response => { * // Dataset is successfully deleted. * }); */ Datasets.deleteDataset = function(config) { validator.assertShape({ datasetId: validator.required(validator.string) })(config); return this.client.createRequest({ method: 'DELETE', path: '/datasets/v1/:ownerId/:datasetId', params: config }); }; /** * List features in a dataset. * * This endpoint supports pagination. Use `MapiRequest#eachPage` or manually specify * the `limit` and `start` options. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#list-features). * * @param {Object} config * @param {string} config.datasetId * @param {number} [config.limit] - Only list this number of features. * @param {string} [config.start] - The ID of the feature from which the listing should * start. * @return {MapiRequest} * * @example * datasetsClient.listFeatures({ * datasetId: 'dataset-id' * }) * .send() * .then(response => { * const features = response.body; * }); */ Datasets.listFeatures = function(config) { validator.assertShape({ datasetId: validator.required(validator.string), limit: validator.number, start: validator.string })(config); return this.client.createRequest({ method: 'GET', path: '/datasets/v1/:ownerId/:datasetId/features', params: pick_1(config, ['datasetId']), query: pick_1(config, ['limit', 'start']) }); }; /** * Add a feature to a dataset or update an existing one. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#insert-or-update-a-feature). * * @param {Object} config * @param {string} config.datasetId * @param {string} config.featureId * @param {Object} config.feature - Valid GeoJSON that is not a `FeatureCollection`. * If the feature has a top-level `id` property, it must match the `featureId` you specify. * @return {MapiRequest} * * @example * datasetsClient.putFeature({ * datasetId: 'dataset-id', * featureId: 'null-island', * feature: { * "type": "Feature", * "properties": { "name": "Null Island" }, * "geometry": { * "type": "Point", * "coordinates": [0, 0] * } * } * }) * .send() * .then(response => { * const feature = response.body; * }); */ Datasets.putFeature = function(config) { validator.assertShape({ datasetId: validator.required(validator.string), featureId: validator.required(validator.string), feature: validator.required(validator.plainObject) })(config); if ( config.feature.id !== undefined && config.feature.id !== config.featureId ) { throw new Error('featureId must match the id property of the feature'); } return this.client.createRequest({ method: 'PUT', path: '/datasets/v1/:ownerId/:datasetId/features/:featureId', params: pick_1(config, ['datasetId', 'featureId']), body: config.feature }); }; /** * Get a feature in a dataset. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-a-feature). * * @param {Object} config * @param {string} config.datasetId * @param {string} config.featureId * @return {MapiRequest} * * @example * datasetsClient.getFeature({ * datasetId: 'dataset-id', * featureId: 'feature-id' * }) * .send() * .then(response => { * const feature = response.body; * }); */ Datasets.getFeature = function(config) { validator.assertShape({ datasetId: validator.required(validator.string), featureId: validator.required(validator.string) })(config); return this.client.createRequest({ method: 'GET', path: '/datasets/v1/:ownerId/:datasetId/features/:featureId', params: config }); }; /** * Delete a feature in a dataset. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#delete-a-feature). * * @param {Object} config * @param {string} config.datasetId * @param {string} config.featureId * @return {MapiRequest} * * @example * datasetsClient.deleteFeature({ * datasetId: 'dataset-id', * featureId: 'feature-id' * }) * .send() * .then(response => { * // Feature is successfully deleted. * }); */ Datasets.deleteFeature = function(config) { validator.assertShape({ datasetId: validator.required(validator.string), featureId: validator.required(validator.string) })(config); return this.client.createRequest({ method: 'DELETE', path: '/datasets/v1/:ownerId/:datasetId/features/:featureId', params: config }); }; var datasets = createServiceFactory_1(Datasets); function objectClean(obj) { return pick_1(obj, function(_, val) { return val != null; }); } var objectClean_1 = objectClean; function objectMap(obj, cb) { return Object.keys(obj).reduce(function(result, key) { result[key] = cb(key, obj[key]); return result; }, {}); } var objectMap_1 = objectMap; /** * Stringify all the boolean values in an object, so true becomes "true". * * @param {Object} obj * @returns {Object} */ function stringifyBoolean(obj) { return objectMap_1(obj, function(_, value) { return typeof value === 'boolean' ? JSON.stringify(value) : value; }); } var stringifyBooleans = stringifyBoolean; /** * Directions API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/navigation/#directions). */ var Directions = {}; /** * Get directions. * * Please read [the full HTTP service documentation](https://www.mapbox.com/api-documentation/navigation/#directions) * to understand all of the available options. * * @param {Object} config * @param {'driving-traffic'|'driving'|'walking'|'cycling'} [config.profile="driving"] * @param {Array} config.waypoints - An ordered array of [`DirectionsWaypoint`](#directionswaypoint) objects, between 2 and 25 (inclusive). * @param {boolean} [config.alternatives=false] - Whether to try to return alternative routes. * @param {Array<'duration'|'distance'|'speed'|'congestion'>} [config.annotations] - Specify additional metadata that should be returned. * @param {boolean} [config.bannerInstructions=false] - Should be used in conjunction with `steps`. * @param {boolean} [config.continueStraight] - Sets the allowed direction of travel when departing intermediate waypoints. * @param {string} [config.exclude] - Exclude certain road types from routing. See HTTP service documentation for options. * @param {'geojson'|'polyline'|'polyline6'} [config.geometries="polyline"] - Format of the returned geometry. * @param {string} [config.language="en"] - Language of returned turn-by-turn text instructions. * See options listed in [the HTTP service documentation](https://www.mapbox.com/api-documentation/navigation/#instructions-languages). * @param {'simplified'|'full'|'false'} [config.overview="simplified"] - Type of returned overview geometry. * @param {boolean} [config.roundaboutExits=false] - Emit insbtructions at roundabout exits. * @param {boolean} [config.steps=false] - Whether to return steps and turn-by-turn instructions. * @param {boolean} [config.voiceInstructions=false] - Whether or not to return SSML marked-up text for voice guidance along the route. * @param {'imperial'|'metric'} [config.voiceUnits="imperial"] - Which type of units to return in the text for voice instructions. * @return {MapiRequest} * * @example * directionsClient.getDirections({ * profile: 'driving-traffic', * waypoints: [ * { * coordinates: [13.4301, 52.5109], * approach: 'unrestricted' * }, * { * coordinates: [13.4265, 52.508] * }, * { * coordinates: [13.4194, 52.5072], * bearing: [100, 60] * } * ] * }) * .send() * .then(response => { * const directions = response.body; * }); */ Directions.getDirections = function(config) { validator.assertShape({ profile: validator.oneOf('driving-traffic', 'driving', 'walking', 'cycling'), waypoints: validator.required( validator.arrayOf( validator.shape({ coordinates: validator.required(validator.coordinates), approach: validator.oneOf('unrestricted', 'curb'), bearing: validator.arrayOf(validator.range([0, 360])), radius: validator.oneOfType(validator.number, validator.equal('unlimited')), waypointName: validator.string }) ) ), alternatives: validator.boolean, annotations: validator.arrayOf( validator.oneOf('duration', 'distance', 'speed', 'congestion') ), bannerInstructions: validator.boolean, continueStraight: validator.boolean, exclude: validator.string, geometries: validator.string, language: validator.string, overview: validator.string, roundaboutExits: validator.boolean, steps: validator.boolean, voiceInstructions: validator.boolean, voiceUnits: validator.string })(config); config.profile = config.profile || 'driving'; var path = { coordinates: [], approach: [], bearing: [], radius: [], waypointName: [] }; var waypointCount = config.waypoints.length; if (waypointCount < 2 || waypointCount > 25) { throw new Error( 'waypoints must include between 2 and 25 DirectionsWaypoints' ); } /** * @typedef {Object} DirectionsWaypoint * @property {Coordinates} coordinates * @property {'unrestricted'|'curb'} [approach="unrestricted"] - Used to indicate how requested routes consider from which side of the road to approach the waypoint. * @property {[number, number]} [bearing] - Used to filter the road segment the waypoint will be placed on by direction and dictates the angle of approach. * This option should always be used in conjunction with a `radius`. The first value is an angle clockwise from true north between 0 and 360, * and the second is the range of degrees the angle can deviate by. * @property {number|'unlimited'} [radius] - Maximum distance in meters that the coordinate is allowed to move when snapped to a nearby road segment. * @property {string} [waypointName] - Custom name for the waypoint used for the arrival instruction in banners and voice instructions. */ config.waypoints.forEach(function(waypoint) { path.coordinates.push( waypoint.coordinates[0] + ',' + waypoint.coordinates[1] ); // join props which come in pairs ['bearing'].forEach(function(prop) { if (waypoint.hasOwnProperty(prop) && waypoint[prop] != null) { waypoint[prop] = waypoint[prop].join(','); } }); ['approach', 'bearing', 'radius', 'waypointName'].forEach(function(prop) { if (waypoint.hasOwnProperty(prop) && waypoint[prop] != null) { path[prop].push(waypoint[prop]); } else { path[prop].push(''); } }); }); ['approach', 'bearing', 'radius', 'waypointName'].forEach(function(prop) { // avoid sending params which are all `;` if ( path[prop].every(function(char) { return char === ''; }) ) { delete path[prop]; } else { path[prop] = path[prop].join(';'); } }); var query = stringifyBooleans({ alternatives: config.alternatives, annotations: config.annotations, banner_instructions: config.bannerInstructions, continue_straight: config.continueStraight, exclude: config.exclude, geometries: config.geometries, language: config.language, overview: config.overview, roundabout_exits: config.roundaboutExits, steps: config.steps, voice_instructions: config.voiceInstructions, voice_units: config.voiceUnits, approaches: path.approach, bearings: path.bearing, radiuses: path.radius, waypoint_names: path.waypointName }); return this.client.createRequest({ method: 'GET', path: '/directions/v5/mapbox/:profile/:coordinates', params: { profile: config.profile, coordinates: path.coordinates.join(';') }, query: objectClean_1(query) }); }; var directions = createServiceFactory_1(Directions); /** * Geocoding API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/search/#geocoding). */ var Geocoding = {}; var featureTypes = [ 'country', 'region', 'postcode', 'district', 'place', 'locality', 'neighborhood', 'address', 'poi', 'poi.landmark' ]; /** * Search for a place. * * See the [public documentation](https://www.mapbox.com/api-documentation/search/#forward-geocoding). * * @param {Object} config * @param {string} config.query - A place name. * @param {'mapbox.places'|'mapbox.places-permanent'} [config.mode="mapbox.places"] - Either `mapbox.places` for ephemeral geocoding, or `mapbox.places-permanent` for storing results and batch geocoding. * @param {Array} [config.countries] - Limits results to the specified countries. * Each item in the array should be an [ISO 3166 alpha 2 country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). * @param {Coordinates} [config.proximity] - Bias local results based on a provided location. * @param {Array<'country'|'region'|'postcode'|'district'|'place'|'locality'|'neighborhood'|'address'|'poi'|'poi.landmark'>} [config.types] - Filter results by feature types. * @param {boolean} [config.autocomplete=true] - Return autocomplete results or not. * @param {BoundingBox} [config.bbox] - Limit results to a bounding box. * @param {number} [config.limit=5] - Limit the number of results returned. * @param {Array} [config.language] - Specify the language to use for response text and, for forward geocoding, query result weighting. * Options are [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag) comprised of a mandatory * [ISO 639-1 language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) and optionally one or more IETF subtags for country or script. * @return {MapiRequest} * * @example * geocodingClient.forwardGeocode({ * query: 'Paris, France', * limit: 2 * }) * .send() * .then(response => { * const match = response.body; * }); * * @example * // geocoding with proximity * geocodingClient.forwardGeocode({ * query: 'Paris, France', * proximity: [-95.4431142, 33.6875431] * }) * .send() * .then(response => { * const match = response.body; * }); * * // geocoding with countries * geocodingClient.forwardGeocode({ * query: 'Paris, France', * countries: ['fr'] * }) * .send() * .then(response => { * const match = response.body; * }); * * // geocoding with bounding box * geocodingClient.forwardGeocode({ * query: 'Paris, France', * bbox: [2.14, 48.72, 2.55, 48.96] * }) * .send() * .then(response => { * const match = response.body; * }); */ Geocoding.forwardGeocode = function(config) { validator.assertShape({ query: validator.required(validator.string), mode: validator.oneOf('mapbox.places', 'mapbox.places-permanent'), countries: validator.arrayOf(validator.string), proximity: validator.coordinates, types: validator.arrayOf(validator.oneOf(featureTypes)), autocomplete: validator.boolean, bbox: validator.arrayOf(validator.number), limit: validator.number, language: validator.arrayOf(validator.string) })(config); config.mode = config.mode || 'mapbox.places'; var query = stringifyBooleans( immutable( { country: config.countries }, pick_1(config, [ 'proximity', 'types', 'autocomplete', 'bbox', 'limit', 'language' ]) ) ); return this.client.createRequest({ method: 'GET', path: '/geocoding/v5/:mode/:query.json', params: pick_1(config, ['mode', 'query']), query: query }); }; /** * Search for places near coordinates. * * See the [public documentation](https://www.mapbox.com/api-documentation/search/#reverse-geocoding). * * @param {Object} config * @param {Coordinates} config.query - Coordinates at which features will be searched. * @param {'mapbox.places'|'mapbox.places-permanent'} [config.mode="mapbox.places"] - Either `mapbox.places` for ephemeral geocoding, or `mapbox.places-permanent` for storing results and batch geocoding. * @param {Array} [config.countries] - Limits results to the specified countries. * Each item in the array should be an [ISO 3166 alpha 2 country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). * @param {Array<'country'|'region'|'postcode'|'district'|'place'|'locality'|'neighborhood'|'address'|'poi'|'poi.landmark'>} [config.types] - Filter results by feature types. * @param {BoundingBox} [config.bbox] - Limit results to a bounding box. * @param {number} [config.limit=1] - Limit the number of results returned. If using this option, you must provide a single item for `types`. * @param {Array} [config.language] - Specify the language to use for response text and, for forward geocoding, query result weighting. * Options are [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag) comprised of a mandatory * [ISO 639-1 language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) and optionally one or more IETF subtags for country or script. * @param {'distance'|'score'} [config.reverseMode='distance'] - Set the factors that are used to sort nearby results. * @return {MapiRequest} * * @example * geocodingClient.reverseGeocode({ * query: [-95.4431142, 33.6875431], * limit: 2 * }) * .send() * .then(response => { * // GeoJSON document with geocoding matches * const match = response.body; * }); */ Geocoding.reverseGeocode = function(config) { validator.assertShape({ query: validator.required(validator.coordinates), mode: validator.oneOf('mapbox.places', 'mapbox.places-permanent'), countries: validator.arrayOf(validator.string), types: validator.arrayOf(validator.oneOf(featureTypes)), bbox: validator.arrayOf(validator.number), limit: validator.number, language: validator.arrayOf(validator.string), reverseMode: validator.oneOf('distance', 'score') })(config); config.mode = config.mode || 'mapbox.places'; var query = stringifyBooleans( immutable( { country: config.countries }, pick_1(config, [ 'country', 'types', 'bbox', 'limit', 'language', 'reverseMode' ]) ) ); return this.client.createRequest({ method: 'GET', path: '/geocoding/v5/:mode/:query.json', params: pick_1(config, ['mode', 'query']), query: query }); }; var geocoding = createServiceFactory_1(Geocoding); /** * Map Matching API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/navigation/#map-matching). */ var MapMatching = {}; /** * Snap recorded location traces to roads and paths. * * @param {Object} config * @param {Array} config.points - An ordered array of [`MapMatchingPoint`](#mapmatchingpoint)s, between 2 and 100 (inclusive). * @param {'driving-traffic'|'driving'|'walking'|'cycling'} [config.profile=driving] - A directions profile ID. * @param {Array<'duration'|'distance'|'speed'>} [config.annotations] - Specify additional metadata that should be returned. * @param {'geojson'|'polyline'|'polyline6'} [config.geometries="polyline"] - Format of the returned geometry. * @param {string} [config.language="en"] - Language of returned turn-by-turn text instructions. * See [supported languages](https://www.mapbox.com/api-documentation/navigation/#instructions-languages). * @param {'simplified'|'full'|'false'} [config.overview="simplified"] - Type of returned overview geometry. * @param {boolean} [config.steps=false] - Whether to return steps and turn-by-turn instructions. * @param {boolean} [config.tidy=false] - Whether or not to transparently remove clusters and re-sample traces for improved map matching results. * @return {MapiRequest} * * @example * mapMatchingClient.getMatch({ * points: [ * { * coordinates: [-117.17283, 32.712041], * approach: 'curb' * }, * { * coordinates: [-117.17291, 32.712256], * isWaypoint: false * }, * { * coordinates: [-117.17292, 32.712444] * }, * { * coordinates: [-117.172922, 32.71257], * waypointName: 'point-a', * approach: 'unrestricted' * }, * { * coordinates: [-117.172985, 32.7126] * }, * { * coordinates: [-117.173143, 32.712597] * }, * { * coordinates: [-117.173345, 32.712546] * } * ], * tidy: false, * }) * .send() * .then(response => { * const matching = response.body; * }) */ MapMatching.getMatch = function(config) { validator.assertShape({ points: validator.required( validator.arrayOf( validator.shape({ coordinates: validator.required(validator.coordinates), approach: validator.oneOf('unrestricted', 'curb'), radius: validator.range([0, 50]), isWaypoint: validator.boolean, waypointName: validator.string, timestamp: validator.date }) ) ), profile: validator.oneOf('driving-traffic', 'driving', 'walking', 'cycling'), annotations: validator.arrayOf(validator.oneOf('duration', 'distance', 'speed')), geometries: validator.oneOf('geojson', 'polyline', 'polyline6'), language: validator.string, overview: validator.oneOf('full', 'simplified', 'false'), steps: validator.boolean, tidy: validator.boolean })(config); var pointCount = config.points.length; if (pointCount < 2 || pointCount > 100) { throw new Error('points must include between 2 and 100 MapMatchingPoints'); } config.profile = config.profile || 'driving'; var path = { coordinates: [], approach: [], radius: [], isWaypoint: [], waypointName: [], timestamp: [] }; /** * @typedef {Object} MapMatchingPoint * @property {Coordinates} coordinates * @property {'unrestricted'|'curb'} [approach="unrestricted"] - Used to indicate how requested routes consider from which side of the road to approach a waypoint. * @property {number} [radius=5] - A number in meters indicating the assumed precision of the used tracking device. * @property {boolean} [isWaypoint=true] - Whether this coordinate is waypoint or not. The first and last coordinates will always be waypoints. * @property {string} [waypointName] - Custom name for the waypoint used for the arrival instruction in banners and voice instructions. Will be ignored unless `isWaypoint` is `true`. * @property {tring | number | Date} [timestamp] - Datetime corresponding to the coordinate. */ config.points.forEach(function(obj) { path.coordinates.push(obj.coordinates[0] + ',' + obj.coordinates[1]); // isWaypoint if (obj.hasOwnProperty('isWaypoint') && obj.isWaypoint != null) { path.isWaypoint.push(obj.isWaypoint); } else { path.isWaypoint.push(true); // default value } if (obj.hasOwnProperty('timestamp') && obj.timestamp != null) { path.timestamp.push(Number(new Date(obj.timestamp))); } else { path.timestamp.push(''); } ['approach', 'radius', 'waypointName'].forEach(function(prop) { if (obj.hasOwnProperty(prop) && obj[prop] != null) { path[prop].push(obj[prop]); } else { path[prop].push(''); } }); }); ['coordinates', 'approach', 'radius', 'waypointName', 'timestamp'].forEach( function(prop) { // avoid sending params which are all `;` if ( path[prop].every(function(value) { return value === ''; }) ) { delete path[prop]; } else { path[prop] = path[prop].join(';'); } } ); // the api requires the first and last items to be true. path.isWaypoint[0] = true; path.isWaypoint[path.isWaypoint.length - 1] = true; if ( path.isWaypoint.every(function(value) { return value === true; }) ) { delete path.isWaypoint; } else { // the api requires the indexes to be sent path.isWaypoint = path.isWaypoint .map(function(val, i) { return val === true ? i : ''; }) .join(';'); } var body = stringifyBooleans( objectClean_1({ annotations: config.annotations, geometries: config.geometries, language: config.language, overview: config.overview, steps: config.steps, tidy: config.tidy, approaches: path.approach, radiuses: path.radius, waypoints: path.isWaypoint, timestamps: path.timestamp, waypoint_names: path.waypointName, coordinates: path.coordinates }) ); // the matching api expects a form-urlencoded // post request. return this.client.createRequest({ method: 'POST', path: '/matching/v5/mapbox/:profile', params: { profile: config.profile }, body: urlUtils.appendQueryObject('', body).substring(1), // need to remove the char`?` headers: { 'content-type': 'application/x-www-form-urlencoded' } }); }; var mapMatching = createServiceFactory_1(MapMatching); /** * Map Matching API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/navigation/#matrix). */ var Matrix = {}; /** * Get a duration and/or distance matrix showing travel times and distances between coordinates. * * @param {Object} config * @param {Array} config.points - An ordered array of [`MatrixPoint`](#matrixpoint)s, between 2 and 100 (inclusive). * @param {'driving-traffic'|'driving'|'walking'|'cycling'} [config.profile=driving] - A Mapbox Directions routing profile ID. * @param {'all'|Array} [config.sources] - Use coordinates with given index as sources. * @param {'all'|Array} [config.destinations] - Use coordinates with given index as destinations. * @param {Array<'distance'|'duration'>} [config.annotations] - Used to specify resulting matrices. * @return {MapiRequest} * * @example * matrixClient.getMatrix({ * points: [ * { * coordinates: [2.2, 1.1] * }, * { * coordinates: [2.2, 1.1], * approach: 'curb' * }, * { * coordinates: [3.2, 1.1] * }, * { * coordinates: [4.2, 1.1] * } * ], * profile: 'walking' * }) * .send() * .then(response => { * const matrix = response.body; * }); */ Matrix.getMatrix = function(config) { validator.assertShape({ points: validator.required( validator.arrayOf( validator.shape({ coordinates: validator.required(validator.coordinates), approach: validator.oneOf('unrestricted', 'curb') }) ) ), profile: validator.oneOf('driving-traffic', 'driving', 'walking', 'cycling'), annotations: validator.arrayOf(validator.oneOf('duration', 'distance')), sources: validator.oneOfType(validator.equal('all'), validator.arrayOf(validator.number)), destinations: validator.oneOfType(validator.equal('all'), validator.arrayOf(validator.number)) })(config); var pointCount = config.points.length; if (pointCount < 2 || pointCount > 100) { throw new Error('points must include between 2 and 100 MatrixPoints'); } config.profile = config.profile || 'driving'; var path = { coordinates: [], approach: [] }; /** * @typedef {Object} MatrixPoint * @property {Coordinates} coordinates - `[longitude, latitude]` * @property {'unrestricted'|'curb'} [approach="unrestricted"] - Used to indicate how requested routes consider from which side of the road to approach the point. */ config.points.forEach(function(obj) { path.coordinates.push(obj.coordinates[0] + ',' + obj.coordinates[1]); if (obj.hasOwnProperty('approach') && obj.approach != null) { path.approach.push(obj.approach); } else { path.approach.push(''); // default value } }); if ( path.approach.every(function(value) { return value === ''; }) ) { delete path.approach; } else { path.approach = path.approach.join(';'); } var query = { sources: Array.isArray(config.sources) ? config.sources.join(';') : config.sources, destinations: Array.isArray(config.destinations) ? config.destinations.join(';') : config.destinations, approaches: path.approach, annotations: config.annotations && config.annotations.join(',') }; return this.client.createRequest({ method: 'GET', path: '/directions-matrix/v1/mapbox/:profile/:coordinates', params: { profile: config.profile, coordinates: path.coordinates.join(';') }, query: objectClean_1(query) }); }; var matrix = createServiceFactory_1(Matrix); /** * Optimization API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/navigation/#optimization). */ var Optimization = {}; /** * Get a duration-optimized route. * * Please read [the full HTTP service documentation](https://www.mapbox.com/api-documentation/navigation/#optimization) * to understand all of the available options. * * @param {Object} config * @param {'driving'|'walking'|'cycling'} [config.profile="driving"] * @param {Array} config.waypoints - An ordered array of [`OptimizationWaypoint`](#optimizationwaypoint) objects, between 2 and 12 (inclusive). * @param {Array<'duration'|'distance'|'speed'>} [config.annotations] - Specify additional metadata that should be returned. * @param {'any'|'last'} [config.destination="any"] - Returned route ends at `any` or `last` coordinate. * @param {Array} [config.distributions] - An ordered array of [`Distribution`](#distribution) objects, each of which includes a `pickup` and `dropoff` property. `pickup` and `dropoff` properties correspond to an index in the OptimizationWaypoint array. * @param {'geojson'|'polyline'|'polyline6'} [config.geometries="polyline"] - Format of the returned geometries. * @param {string} [config.language="en"] - Language of returned turn-by-turn text instructions. * See options listed in [the HTTP service documentation](https://www.mapbox.com/api-documentation/navigation/#instructions-languages). * @param {'simplified'|'full'|'false'} [config.overview="simplified"] - Type of returned overview geometry. * @param {boolean} [config.roundtrip=true] - Specifies whether the trip should complete by returning to the first location. * @param {'any'|'first'} [config.source="any"] - To begin the route, start either from the first coordinate or let the Optimization API choose. * @param {boolean} [config.steps=false] - Whether to return steps and turn-by-turn instructions. * @return {MapiRequest} */ Optimization.getOptimization = function(config) { validator.assertShape({ profile: validator.oneOf('driving', 'walking', 'cycling'), waypoints: validator.required( validator.arrayOf( validator.shape({ coordinates: validator.required(validator.coordinates), approach: validator.oneOf('unrestricted', 'curb'), bearing: validator.arrayOf(validator.range([0, 360])), radius: validator.oneOfType(validator.number, validator.equal('unlimited')) }) ) ), annotations: validator.arrayOf(validator.oneOf('duration', 'distance', 'speed')), geometries: validator.oneOf('geojson', 'polyline', 'polyline6'), language: validator.string, overview: validator.oneOf('simplified', 'full', 'false'), roundtrip: validator.boolean, steps: validator.boolean, source: validator.oneOf('any', 'first'), destination: validator.oneOf('any', 'last'), distributions: validator.arrayOf( validator.shape({ pickup: validator.number, dropoff: validator.number }) ) })(config); var path = { coordinates: [], approach: [], bearing: [], radius: [], distributions: [] }; var waypointCount = config.waypoints.length; if (waypointCount < 2 || waypointCount > 12) { throw new Error( 'waypoints must include between 2 and 12 OptimizationWaypoints' ); } /** * @typedef {Object} OptimizationWaypoint * @property {Coordinates} coordinates * @property {'unrestricted'|'curb'} [approach="unrestricted"] - Used to indicate how requested routes consider from which side of the road to approach the waypoint. * @property {[number, number]} [bearing] - Used to filter the road segment the waypoint will be placed on by direction and dictates the angle of approach. * This option should always be used in conjunction with a `radius`. The first value is an angle clockwise from true north between 0 and 360, * and the second is the range of degrees the angle can deviate by. * @property {number|'unlimited'} [radius] - Maximum distance in meters that the coordinate is allowed to move when snapped to a nearby road segment. */ config.waypoints.forEach(function(waypoint) { path.coordinates.push( waypoint.coordinates[0] + ',' + waypoint.coordinates[1] ); // join props which come in pairs ['bearing'].forEach(function(prop) { if (waypoint.hasOwnProperty(prop) && waypoint[prop] != null) { waypoint[prop] = waypoint[prop].join(','); } }); ['approach', 'bearing', 'radius'].forEach(function(prop) { if (waypoint.hasOwnProperty(prop) && waypoint[prop] != null) { path[prop].push(waypoint[prop]); } else { path[prop].push(''); } }); }); /** * @typedef {Object} Distribution * @property {number} pickup - Array index of the item containing coordinates for the pick-up location in the OptimizationWaypoint array. * @property {number} dropoff - Array index of the item containing coordinates for the drop-off location in the OptimizationWaypoint array. */ // distributions aren't a property of OptimizationWaypoint, so join them separately if (config.distributions) { config.distributions.forEach(function(dist) { path.distributions.push(dist.pickup + ',' + dist.dropoff); }); } ['approach', 'bearing', 'radius', 'distributions'].forEach(function(prop) { // avoid sending params which are all `;` if ( path[prop].every(function(char) { return char === ''; }) ) { delete path[prop]; } else { path[prop] = path[prop].join(';'); } }); var query = stringifyBooleans({ geometries: config.geometries, language: config.language, overview: config.overview, roundtrip: config.roundtrip, steps: config.steps, source: config.source, destination: config.destination, distributions: path.distributions, approaches: path.approach, bearings: path.bearing, radiuses: path.radius }); return this.client.createRequest({ method: 'GET', path: '/optimized-trips/v1/mapbox/:profile/:coordinates', params: { profile: config.profile || 'driving', coordinates: path.coordinates.join(';') }, query: objectClean_1(query) }); }; var optimization = createServiceFactory_1(Optimization); var polyline_1 = createCommonjsModule(function (module) { /** * Based off of [the offical Google document](https://developers.google.com/maps/documentation/utilities/polylinealgorithm) * * Some parts from [this implementation](http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/PolylineEncoder.js) * by [Mark McClure](http://facstaff.unca.edu/mcmcclur/) * * @module polyline */ var polyline = {}; function py2_round(value) { // Google's polyline algorithm uses the same rounding strategy as Python 2, which is different from JS for negative values return Math.floor(Math.abs(value) + 0.5) * (value >= 0 ? 1 : -1); } function encode(current, previous, factor) { current = py2_round(current * factor); previous = py2_round(previous * factor); var coordinate = current - previous; coordinate <<= 1; if (current - previous < 0) { coordinate = ~coordinate; } var output = ''; while (coordinate >= 0x20) { output += String.fromCharCode((0x20 | (coordinate & 0x1f)) + 63); coordinate >>= 5; } output += String.fromCharCode(coordinate + 63); return output; } /** * Decodes to a [latitude, longitude] coordinates array. * * This is adapted from the implementation in Project-OSRM. * * @param {String} str * @param {Number} precision * @returns {Array} * * @see https://github.com/Project-OSRM/osrm-frontend/blob/master/WebContent/routing/OSRM.RoutingGeometry.js */ polyline.decode = function(str, precision) { var index = 0, lat = 0, lng = 0, coordinates = [], shift = 0, result = 0, byte = null, latitude_change, longitude_change, factor = Math.pow(10, precision || 5); // Coordinates have variable length when encoded, so just keep // track of whether we've hit the end of the string. In each // loop iteration, a single coordinate is decoded. while (index < str.length) { // Reset shift, result, and byte byte = null; shift = 0; result = 0; do { byte = str.charCodeAt(index++) - 63; result |= (byte & 0x1f) << shift; shift += 5; } while (byte >= 0x20); latitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1)); shift = result = 0; do { byte = str.charCodeAt(index++) - 63; result |= (byte & 0x1f) << shift; shift += 5; } while (byte >= 0x20); longitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1)); lat += latitude_change; lng += longitude_change; coordinates.push([lat / factor, lng / factor]); } return coordinates; }; /** * Encodes the given [latitude, longitude] coordinates array. * * @param {Array.>} coordinates * @param {Number} precision * @returns {String} */ polyline.encode = function(coordinates, precision) { if (!coordinates.length) { return ''; } var factor = Math.pow(10, precision || 5), output = encode(coordinates[0][0], 0, factor) + encode(coordinates[0][1], 0, factor); for (var i = 1; i < coordinates.length; i++) { var a = coordinates[i], b = coordinates[i - 1]; output += encode(a[0], b[0], factor); output += encode(a[1], b[1], factor); } return output; }; function flipped(coords) { var flipped = []; for (var i = 0; i < coords.length; i++) { flipped.push(coords[i].slice().reverse()); } return flipped; } /** * Encodes a GeoJSON LineString feature/geometry. * * @param {Object} geojson * @param {Number} precision * @returns {String} */ polyline.fromGeoJSON = function(geojson, precision) { if (geojson && geojson.type === 'Feature') { geojson = geojson.geometry; } if (!geojson || geojson.type !== 'LineString') { throw new Error('Input must be a GeoJSON LineString'); } return polyline.encode(flipped(geojson.coordinates), precision); }; /** * Decodes to a GeoJSON LineString geometry. * * @param {String} str * @param {Number} precision * @returns {Object} */ polyline.toGeoJSON = function(str, precision) { var coords = polyline.decode(str, precision); return { type: 'LineString', coordinates: flipped(coords) }; }; if (module.exports) { module.exports = polyline; } }); /** * Static API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#static). */ var Static = {}; /** * Get a static map image. * * **If you just want the URL for the static map image, create a request * and get it's URL with `MapiRequest#url`.** This is what prior versions of the * SDK returned. * * @param {Object} config * @param {string} config.ownerId - The owner of the map style. * @param {string} config.styleId - The map's style ID. * @param {number} config.width - Width of the image in pixels, between 1 and 1280. * @param {number} config.height - Height of the image in pixels, between 1 and 1280. * @param {'auto'|Object} config.position - If `"auto"`, the viewport will fit the * bounds of the overlay(s). Otherwise, the maps' position is described by an object * with the following properties: * `coordinates` (required): [`coordinates`](#coordinates) for the center of image. * `zoom` (required): Between 0 and 20. * `bearing` (optional): Between 0 and 360. * `pitch` (optional): Between 0 and 60. * * @param {Array} [config.overlays] - Overlays should be in z-index * order: the first in the array will be on the bottom; the last will be on * the top. Overlays are objects that match one of the following types: * [`SimpleMarkerOverlay`](#simplemarkeroverlay), * [`CustomMarkerOverlay`](#custommarkeroverlay), * [`PathOverlay`](#pathoverlay), * [`GeoJsonOverlay`](#geojsonoverlay) * * @param {boolean} [config.highRes=false] * @param {string} [config.insertOverlayBeforeLayer] - The ID of the style layer * that overlays should be inserted *before*. * @param {boolean} [config.attribution=true] - Whether there is attribution * on the map image. * @param {boolean} [config.logo=true] - Whether there is a Mapbox logo * on the map image. * @return {MapiRequest} * * @example * staticClient.getStaticImage({ * ownerId: 'mapbox', * styleId: 'streets-v11', * width: 200, * height: 300, * position: { * coordinates: [12, 13], * zoom: 4 * } * }) * .send() * .then(response => { * const image = response.body; * }); * * @example * staticClient.getStaticImage({ * ownerId: 'mapbox', * styleId: 'streets-v11', * width: 200, * height: 300, * position: { * coordinates: [12, 13], * zoom: 3 * }, * overlays: [ * // Simple markers. * { * marker: { * coordinates: [12.2, 12.8] * } * }, * { * marker: { * size: 'large', * coordinates: [14, 13.2], * label: 'm', * color: '#000' * } * }, * { * marker: { * coordinates: [15, 15.2], * label: 'airport', * color: '#ff0000' * } * }, * // Custom marker * { * marker: { * coordinates: [10, 11], * url: 'https://upload.wikimedia.org/wikipedia/commons/6/6f/0xff_timetracker.png' * } * } * ] * }) * .send() * .then(response => { * const image = response.body; * }); * * @example * // To get the URL instead of the image, create a request * // and get its URL without sending it. * const request = staticClient * .getStaticImage({ * ownerId: 'mapbox', * styleId: 'streets-v11', * width: 200, * height: 300, * position: { * coordinates: [12, 13], * zoom: 4 * } * }); * const staticImageUrl = request.url(); * // Now you can open staticImageUrl in a browser. */ Static.getStaticImage = function(config) { validator.assertShape({ ownerId: validator.required(validator.string), styleId: validator.required(validator.string), width: validator.required(validator.range([1, 1280])), height: validator.required(validator.range([1, 1280])), position: validator.required( validator.oneOfType( validator.oneOf('auto'), validator.strictShape({ coordinates: validator.required(validator.coordinates), zoom: validator.required(validator.range([0, 20])), bearing: validator.range([0, 360]), pitch: validator.range([0, 60]) }) ) ), overlays: validator.arrayOf(validator.plainObject), highRes: validator.boolean, insertOverlayBeforeLayer: validator.string, attribution: validator.boolean, logo: validator.boolean })(config); var encodedOverlay = (config.overlays || []) .map(function(overlayItem) { if (overlayItem.marker) { return encodeMarkerOverlay(overlayItem.marker); } if (overlayItem.path) { return encodePathOverlay(overlayItem.path); } return encodeGeoJsonOverlay(overlayItem.geoJson); }) .join(','); var encodedPosition = encodePosition(config.position); var encodedDimensions = config.width + 'x' + config.height; if (config.highRes) { encodedDimensions += '@2x'; } var preEncodedUrlParts = [encodedOverlay, encodedPosition, encodedDimensions] .filter(Boolean) .join('/'); var query = {}; if (config.attribution !== undefined) { query.attribution = String(config.attribution); } if (config.logo !== undefined) { query.logo = String(config.logo); } if (config.insertOverlayBeforeLayer !== undefined) { query.before_layer = config.insertOverlayBeforeLayer; } return this.client.createRequest({ method: 'GET', path: '/styles/v1/:ownerId/:styleId/static/' + preEncodedUrlParts, params: pick_1(config, ['ownerId', 'styleId']), query: query }); }; function encodePosition(position) { if (position === 'auto') return 'auto'; return position.coordinates .concat([position.zoom, position.bearing, position.pitch]) .filter(Boolean) .join(','); } function encodeMarkerOverlay(o) { if (o.url) { return encodeCustomMarkerOverlay(o); } return encodeSimpleMarkerOverlay(o); } /** * A simple marker overlay. * @typedef {Object} SimpleMarkerOverlay * @property {Object} marker * @property {[number, number]} marker.coordinates - `[longitude, latitude]` * @property {'large'|'small'} [marker.size='small'] * @property {string} [marker.label] - Marker symbol. Options are an alphanumeric label `a` * through `z`, `0` through `99`, or a valid [Maki](https://www.mapbox.com/maki/) * icon. If a letter is requested, it will be rendered in uppercase only. * @property {string} [marker.color] - A 3- or 6-digit hexadecimal color code. */ function encodeSimpleMarkerOverlay(o) { validator.assertShape({ coordinates: validator.required(validator.coordinates), size: validator.oneOf('large', 'small'), label: validator.string, color: validator.string })(o); var result = o.size === 'large' ? 'pin-l' : 'pin-s'; if (o.label) { result += '-' + String(o.label).toLowerCase(); } if (o.color) { result += '+' + sanitizeHexColor(o.color); } result += '(' + o.coordinates.join(',') + ')'; return result; } /** * A marker overlay with a custom image. * @typedef {Object} CustomMarkerOverlay * @property {Object} marker * @property {[number, number]} marker.coordinates - `[longitude, latitude]` * @property {string} marker.url */ function encodeCustomMarkerOverlay(o) { validator.assertShape({ coordinates: validator.required(validator.coordinates), url: validator.required(validator.string) })(o); var result = 'url-' + encodeURIComponent(o.url); result += '(' + o.coordinates.join(',') + ')'; return result; } /** * A stylable line. * @typedef {Object} PathOverlay * @property {Object} path * @property {Array} path.coordinates - An array of coordinates * describing the path. * @property {number} [path.strokeWidth] * @property {string} [path.strokeColor] * @property {number} [path.strokeOpacity] - Must be paired with strokeColor. * @property {string} [path.fillColor] - Must be paired with strokeColor. * @property {number} [path.fillOpacity] - Must be paired with fillColor. */ function encodePathOverlay(o) { validator.assertShape({ coordinates: validator.required(validator.arrayOf(validator.coordinates)), strokeWidth: validator.number, strokeColor: validator.string, strokeOpacity: validator.number, fillColor: validator.string, fillOpacity: validator.number })(o); if (o.strokeOpacity !== undefined && o.strokeColor === undefined) { throw new Error('strokeOpacity requires strokeColor'); } if (o.fillColor !== undefined && o.strokeColor === undefined) { throw new Error('fillColor requires strokeColor'); } if (o.fillOpacity !== undefined && o.fillColor === undefined) { throw new Error('fillOpacity requires fillColor'); } var result = 'path'; if (o.strokeWidth) { result += '-' + o.strokeWidth; } if (o.strokeColor) { result += '+' + sanitizeHexColor(o.strokeColor); } if (o.strokeOpacity) { result += '-' + o.strokeOpacity; } if (o.fillColor) { result += '+' + sanitizeHexColor(o.fillColor); } if (o.fillOpacity) { result += '-' + o.fillOpacity; } // polyline expects each coordinate to be in reversed order: [lat, lng] var reversedCoordinates = o.coordinates.map(function(c) { return c.reverse(); }); var encodedPolyline = polyline_1.encode(reversedCoordinates); result += '(' + encodeURIComponent(encodedPolyline) + ')'; return result; } /** * GeoJSON to overlay the map. * @typedef {Object} GeoJsonOverlay * @property {Object} geoJson - Valid GeoJSON. */ function encodeGeoJsonOverlay(o) { validator.assert(validator.required(validator.plainObject))(o); return 'geojson(' + encodeURIComponent(JSON.stringify(o)) + ')'; } function sanitizeHexColor(color) { return color.replace(/^#/, ''); } var _static = createServiceFactory_1(Static); /** * Styles API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#styles). */ var Styles = {}; /** * Get a style. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-a-style). * * @param {Object} config * @param {string} config.styleId * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.getStyle({ * styleId: 'style-id' * }) * .send() * .then(response => { * const style = response.body; * }); */ Styles.getStyle = function(config) { validator.assertShape({ styleId: validator.required(validator.string), ownerId: validator.string })(config); return this.client.createRequest({ method: 'GET', path: '/styles/v1/:ownerId/:styleId', params: config }); }; /** * Create a style. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#create-a-style). * * @param {Object} config * @param {Object} config.style - Stylesheet JSON object. * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.createStyle({ * style: { * version: 8, * name: "My Awesome Style", * metadata: {}, * sources: {}, * layers: [], * glyphs: "mapbox://fonts/{owner}/{fontstack}/{range}.pbf" * } * }) * .send() * .then(response => { * const style = response.body; * }); */ Styles.createStyle = function(config) { validator.assertShape({ style: validator.plainObject, ownerId: validator.string })(config); return this.client.createRequest({ method: 'POST', path: '/styles/v1/:ownerId', params: pick_1(config, ['ownerId']), body: config.style }); }; /** * Update a style. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#update-a-style). * * @param {Object} config * @param {string} config.styleId * @param {Object} config.style - Stylesheet JSON object. * @param {string | number | Date} [config.lastKnownModification] - Datetime of last * known update. Passed as 'If-Unmodified-Since' HTTP header. * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.updateStyle({ * styleId: 'style-id', * style: { * version: 8, * name: 'My Awesome Style', * metadata: {}, * sources: {}, * layers: [], * glyphs: 'mapbox://fonts/{owner}/{fontstack}/{range}.pbf' * } * }) * .send() * .then(response => { * const style = response.body; * }); */ Styles.updateStyle = function(config) { validator.assertShape({ styleId: validator.required(validator.string), style: validator.required(validator.plainObject), lastKnownModification: validator.date, ownerId: validator.string })(config); var headers = {}; if (config.lastKnownModification) { headers['If-Unmodified-Since'] = new Date( config.lastKnownModification ).toUTCString(); } return this.client.createRequest({ method: 'PATCH', path: '/styles/v1/:ownerId/:styleId', params: pick_1(config, ['styleId', 'ownerId']), headers: headers, body: config.style }); }; /** * Delete a style. * * @param {Object} config * @param {string} config.styleId * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.deleteStyle({ * styleId: 'style-id' * }) * .send() * .then(response => { * // delete successful * }); */ Styles.deleteStyle = function(config) { validator.assertShape({ styleId: validator.required(validator.string), ownerId: validator.string })(config); return this.client.createRequest({ method: 'DELETE', path: '/styles/v1/:ownerId/:styleId', params: config }); }; /** * List styles in your account. * * @param {Object} [config] * @param {string} [config.start] - The style ID to start at, for paginated results. * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.listStyles() * .send() * .then(response => { * const styles = response.body; * }); */ Styles.listStyles = function(config) { config = config || {}; validator.assertShape({ start: validator.string, ownerId: validator.string })(config); var query = {}; if (config.start) { query.start = config.start; } return this.client.createRequest({ method: 'GET', path: '/styles/v1/:ownerId', params: pick_1(config, ['ownerId']), query: query }); }; /** * Add an icon to a style, or update an existing one. * * @param {Object} config * @param {string} config.styleId * @param {string} config.iconId * @param {UploadableFile} config.file - An SVG file. * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.putStyleIcon({ * styleId: 'foo', * iconId: 'bar', * // The string filename value works in Node. * // In the browser, provide a Blob. * file: 'path/to/file.svg' * }) * .send() * .then(response => { * const newSprite = response.body; * }); */ Styles.putStyleIcon = function(config) { validator.assertShape({ styleId: validator.required(validator.string), iconId: validator.required(validator.string), file: validator.file, ownerId: validator.string })(config); return this.client.createRequest({ method: 'PUT', path: '/styles/v1/:ownerId/:styleId/sprite/:iconId', params: pick_1(config, ['ownerId', 'styleId', 'iconId']), file: config.file }); }; /** * Remove an icon from a style. * * @param {Object} config * @param {string} config.styleId * @param {string} config.iconId * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.deleteStyleIcon({ * styleId: 'foo', * iconId: 'bar' * }) * .send() * .then(response => { * // delete successful * }); */ Styles.deleteStyleIcon = function(config) { validator.assertShape({ styleId: validator.required(validator.string), iconId: validator.required(validator.string), ownerId: validator.string })(config); return this.client.createRequest({ method: 'DELETE', path: '/styles/v1/:ownerId/:styleId/sprite/:iconId', params: config }); }; /** * Get a style sprite's image or JSON document. * * See [the corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-a-sprite-image-or-json). * * @param {Object} config * @param {string} config.styleId * @param {'json' | 'png'} [config.format="json"] * @param {boolean} [config.highRes] - If true, returns spritesheet with 2x * resolution. * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.getStyleSprite({ * format: 'json', * styleId: 'foo', * highRes: true * }) * .send() * .then(response => { * const sprite = response.body; * }); */ Styles.getStyleSprite = function(config) { validator.assertShape({ styleId: validator.required(validator.string), format: validator.oneOf('json', 'png'), highRes: validator.boolean, ownerId: validator.string })(config); var format = config.format || 'json'; var fileName = 'sprite' + (config.highRes ? '@2x' : '') + '.' + format; return this.client.createRequest({ method: 'GET', path: '/styles/v1/:ownerId/:styleId/:fileName', params: immutable(pick_1(config, ['ownerId', 'styleId']), { fileName: fileName }) }); }; /** * Get a font glyph range. * * See [the corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-font-glyph-ranges). * * @param {Object} config * @param {string|Array} config.fonts - An array of font names. * @param {number} config.start - Character code of the starting glyph. * @param {number} config.end - Character code of the last glyph, * typically equivalent to`config.start + 255`. * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * stylesClient.getFontGlyphRange({ * fonts: 'Arial Unicode', * start: 0, * end: 255 * }) * .send() * .then(response => { * const glyph = response.body; * }); */ Styles.getFontGlyphRange = function(config) { validator.assertShape({ fonts: validator.required(validator.oneOfType(validator.string, validator.arrayOf(validator.string))), start: validator.required(validator.number), end: validator.required(validator.number), ownerId: validator.string })(config); var fileName = config.start + '-' + config.end + '.pbf'; return this.client.createRequest({ method: 'GET', path: '/fonts/v1/:ownerId/:fontList/:fileName', params: immutable(pick_1(config, ['ownerId']), { fontList: [].concat(config.fonts), fileName: fileName }) }); }; /** * Get embeddable HTML displaying a map. * * See [the corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#request-embeddable-html). * * @param {Object} config * @param {string} styleId * @param {boolean} [scrollZoom=true] - If `false`, zooming the map by scrolling will * be disabled. * @param {boolean} [title=false] - If `true`, the map's title and owner is displayed * in the upper right corner of the map. * @param {ownerId} [ownerId] */ Styles.getEmbeddableHtml = function(config) { validator.assertShape({ styleId: validator.required(validator.string), scrollZoom: validator.boolean, title: validator.boolean, ownerId: validator.string })(config); var fileName = config.styleId + '.html'; var query = {}; if (config.scrollZoom !== undefined) { query.zoomwheel = String(config.scrollZoom); } if (config.title !== undefined) { query.title = String(config.title); } return this.client.createRequest({ method: 'GET', path: '/styles/v1/:ownerId/:fileName', params: immutable(pick_1(config, ['ownerId']), { fileName: fileName }), query: query }); }; var styles = createServiceFactory_1(Styles); /** * Tilequery API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#tilequery). */ var Tilequery = {}; /** * List features within a radius of a point on a map (or several maps). * * @param {Object} config * @param {Array} config.mapIds - The maps being queried. * If you need to composite multiple layers, provide multiple map IDs. * @param {Coordinates} config.coordinates - The longitude and latitude to be queried. * @param {number} [config.radius=0] - The approximate distance in meters to query for features. * @param {number} [config.limit=5] - The number of features to return, between 1 and 50. * @param {boolean} [config.dedupe=true] - Whether or not to deduplicate results. * @param {'polygon'|'linestring'|'point'} [config.geometry] - Queries for a specific geometry type. * @param {Array} [config.layers] - IDs of vector layers to query. * @return {MapiRequest} * * @example * tilequeryClient.listFeatures({ * mapIds: ['mapbox.mapbox-streets-v8'], * coordinates: [-122.42901, 37.80633], * radius: 10 * }) * .send() * .then(response => { * const features = response.body; * }); */ Tilequery.listFeatures = function(config) { validator.assertShape({ mapIds: validator.required(validator.arrayOf(validator.string)), coordinates: validator.required(validator.coordinates), radius: validator.number, limit: validator.range([1, 50]), dedupe: validator.boolean, layers: validator.arrayOf(validator.string) })(config); return this.client.createRequest({ method: 'GET', path: '/v4/:mapIds/tilequery/:coordinates.json', params: { mapIds: config.mapIds, coordinates: config.coordinates }, query: pick_1(config, ['radius', 'limit', 'dedupe', 'layers']) }); }; var tilequery = createServiceFactory_1(Tilequery); /** * Tilesets API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#tilesets). */ var Tilesets = {}; /** * List a user's tilesets. * * @param {Object} [config] * @param {string} [config.ownerId] * @return {MapiRequest} * * @example * tilesetsClient.listTilesets() * .then(response => { * const tilesets = response.body; * }); * * @example * tilesetsClient.listTilesets() * .eachPage((error, response, next) => { * // Handle error or response and call next. * }); */ Tilesets.listTilesets = function(config) { validator.assertShape({ ownerId: validator.string })(config); return this.client.createRequest({ method: 'GET', path: '/tilesets/v1/:ownerId', params: config }); }; var tilesets = createServiceFactory_1(Tilesets); /** * Tokens API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/accounts/#tokens). */ var Tokens = {}; /** * List your access tokens. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/accounts/#list-tokens). * * @return {MapiRequest} * * @example * tokensClient.listTokens() * .send() * .then(response => { * const tokens = response.body; * }); */ Tokens.listTokens = function() { return this.client.createRequest({ method: 'GET', path: '/tokens/v2/:ownerId' }); }; /** * Create a new access token. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/accounts/#create-a-token). * * @param {Object} [config] * @param {string} [config.note] * @param {Array} [config.scopes] * @param {Array} [config.resources] * @param {Array} [config.allowedUrls] * @return {MapiRequest} * * @example * tokensClient.createToken({ * note: 'datasets-token', * scopes: ['datasets:write', 'datasets:read'] * }) * .send() * .then(response => { * const token = response.body; * }); */ Tokens.createToken = function(config) { config = config || {}; validator.assertShape({ note: validator.string, scopes: validator.arrayOf(validator.string), resources: validator.arrayOf(validator.string), allowedUrls: validator.arrayOf(validator.string) })(config); var body = {}; body.scopes = config.scopes || []; if (config.note !== undefined) { body.note = config.note; } if (config.resources) { body.resources = config.resources; } if (config.allowedUrls) { body.allowedUrls = config.allowedUrls; } return this.client.createRequest({ method: 'POST', path: '/tokens/v2/:ownerId', params: pick_1(config, ['ownerId']), body: body }); }; /** * Create a new temporary access token. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/accounts/#create-a-temporary-token). * * @param {Object} config * @param {string} config.expires * @param {Array} config.scopes * @return {MapiRequest} * * @example * tokensClient.createTemporaryToken({ * scopes: ['datasets:write', 'datasets:read'] * }) * .send() * .then(response => { * const token = response.body; * }); */ Tokens.createTemporaryToken = function(config) { validator.assertShape({ expires: validator.required(validator.date), scopes: validator.required(validator.arrayOf(validator.string)) })(config); return this.client.createRequest({ method: 'POST', path: '/tokens/v2/:ownerId', params: pick_1(config, ['ownerId']), body: { expires: new Date(config.expires).toISOString(), scopes: config.scopes } }); }; /** * Update an access token. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/accounts/#update-a-token). * * @param {Object} config * @param {string} config.tokenId * @param {string} [config.note] * @param {Array} [config.scopes] * @param {Array} [config.resources] * @param {Array} [config.allowedUrls] * @return {MapiRequest} * * @example * tokensClient.updateToken({ * tokenId: 'cijucimbe000brbkt48d0dhcx', * note: 'datasets-token', * scopes: ['datasets:write', 'datasets:read'] * }) * .send() * .then(response => { * const token = response.body; * }); */ Tokens.updateToken = function(config) { validator.assertShape({ tokenId: validator.required(validator.string), note: validator.string, scopes: validator.arrayOf(validator.string), resources: validator.arrayOf(validator.string), allowedUrls: validator.arrayOf(validator.string) })(config); var body = {}; if (config.scopes) { body.scopes = config.scopes; } if (config.note !== undefined) { body.note = config.note; } if (config.resources || config.resources === null) { body.resources = config.resources; } if (config.allowedUrls || config.allowedUrls === null) { body.allowedUrls = config.allowedUrls; } return this.client.createRequest({ method: 'PATCH', path: '/tokens/v2/:ownerId/:tokenId', params: pick_1(config, ['ownerId', 'tokenId']), body: body }); }; /** * Get data about the client's access token. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/accounts/#retrieve-a-token). * * @return {MapiRequest} * * @example * tokensClient.getToken() * .send() * .then(response => { * const token = response.body; * }); */ Tokens.getToken = function() { return this.client.createRequest({ method: 'GET', path: '/tokens/v2' }); }; /** * Delete an access token. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/accounts/#delete-a-token). * * @param {Object} config * @param {string} config.tokenId * @return {MapiRequest} * * @example * tokensClient.deleteToken({ * tokenId: 'cijucimbe000brbkt48d0dhcx' * }) * .send() * .then(response => { * // Token successfully deleted. * }); */ Tokens.deleteToken = function(config) { validator.assertShape({ tokenId: validator.required(validator.string) })(config); return this.client.createRequest({ method: 'DELETE', path: '/tokens/v2/:ownerId/:tokenId', params: pick_1(config, ['ownerId', 'tokenId']) }); }; /** * List your available scopes. Each item is a metadata * object about the scope, not just the string scope. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/accounts/#list-scopes). * * @return {MapiRequest} * * @example * tokensClient.listScopes() * .send() * .then(response => { * const scopes = response.body; * }); */ Tokens.listScopes = function() { return this.client.createRequest({ method: 'GET', path: '/scopes/v1/:ownerId' }); }; var tokens = createServiceFactory_1(Tokens); /** * Uploads API service. * * Learn more about this service and its responses in * [the HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#uploads). */ var Uploads = {}; /** * List the statuses of all recent uploads. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-recent-upload-statuses). * * @param {Object} [config] * @param {boolean} [config.reverse] - List uploads in chronological order, rather than reverse chronological order. * @return {MapiRequest} * * @example * uploadsClient.listUploads() * .send() * .then(response => { * const uploads = response.body; * }); */ Uploads.listUploads = function(config) { validator.assertShape({ reverse: validator.boolean })(config); return this.client.createRequest({ method: 'GET', path: '/uploads/v1/:ownerId', query: config }); }; /** * Create S3 credentials. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-s3-credentials). * * @return {MapiRequest} * * @example * const AWS = require('aws-sdk'); * const getCredentials = () => { * return uploadsClient * .createUploadCredentials() * .send() * .then(response => response.body); * } * const putFileOnS3 = (credentials) => { * const s3 = new AWS.S3({ * accessKeyId: credentials.accessKeyId, * secretAccessKey: credentials.secretAccessKey, * sessionToken: credentials.sessionToken, * region: 'us-east-1' * }); * return s3.putObject({ * Bucket: credentials.bucket, * Key: credentials.key, * Body: fs.createReadStream('/path/to/file.mbtiles') * }).promise(); * }; * * getCredentials().then(putFileOnS3); */ Uploads.createUploadCredentials = function() { return this.client.createRequest({ method: 'POST', path: '/uploads/v1/:ownerId/credentials' }); }; /** * Create an upload. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#create-an-upload). * * @param {Object} config * @param {string} config.mapId - The map ID to create or replace in the format `username.nameoftileset`. * Limited to 32 characters (only `-` and `_` special characters allowed; limit does not include username). * @param {string} config.url - Either of the following: * - HTTPS URL of the S3 object provided by [`createUploadCredentials`](#createuploadcredentials) * - The `mapbox://` URL of an existing dataset that you'd like to export to a tileset. * This should be in the format `mapbox://datasets/{username}/{datasetId}`. * @param {string} [config.tilesetName] - Name for the tileset. Limited to 64 characters. * @return {MapiRequest} * * @example * // Response from a call to createUploadCredentials * const credentials = { * accessKeyId: '{accessKeyId}', * bucket: '{bucket}', * key: '{key}', * secretAccessKey: '{secretAccessKey}', * sessionToken: '{sessionToken}', * url: '{s3 url}' * }; * uploadsClient.createUpload({ * mapId: `${myUsername}.${myTileset}`, * url: credentials.url * }) * .send() * .then(response => { * const upload = response.body; * }); */ Uploads.createUpload = function(config) { validator.assertShape({ mapId: validator.required(validator.string), url: validator.required(validator.string), tilesetName: validator.string })(config); return this.client.createRequest({ method: 'POST', path: '/uploads/v1/:ownerId', body: { tileset: config.mapId, url: config.url, name: config.tilesetName } }); }; /** * Get an upload's status. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-upload-status). * * @param {Object} config * @param {string} config.uploadId * @return {MapiRequest} * * @example * uploadsClient.getUpload({ * uploadId: '{upload_id}' * }) * .send() * .then(response => { * const status = response.body; * }); */ Uploads.getUpload = function(config) { validator.assertShape({ uploadId: validator.required(validator.string) })(config); return this.client.createRequest({ method: 'GET', path: '/uploads/v1/:ownerId/:uploadId', params: config }); }; /** * Delete an upload. * * See the [corresponding HTTP service documentation](https://www.mapbox.com/api-documentation/maps/#remove-an-upload-status). * * @param {Object} config * @param {string} config.uploadId * @return {MapiRequest} * * @example * uploadsClient.deleteUpload({ * uploadId: '{upload_id}' * }) * .send() * .then(response => { * // Upload successfully deleted. * }); */ Uploads.deleteUpload = function(config) { validator.assertShape({ uploadId: validator.required(validator.string) })(config); return this.client.createRequest({ method: 'DELETE', path: '/uploads/v1/:ownerId/:uploadId', params: config }); }; var uploads = createServiceFactory_1(Uploads); function mapboxSdk(options) { var client = browserClient(options); client.datasets = datasets(client); client.directions = directions(client); client.geocoding = geocoding(client); client.mapMatching = mapMatching(client); client.matrix = matrix(client); client.optimization = optimization(client); client.static = _static(client); client.styles = styles(client); client.tilequery = tilequery(client); client.tilesets = tilesets(client); client.tokens = tokens(client); client.uploads = uploads(client); return client; } var bundle = mapboxSdk; return bundle; })));