flex-template-web/public/static/scripts/mapbox/mapbox-sdk.js

4480 lines
133 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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*</).reduce(function(result, link) {
var parsed = parseLink(link);
if (!parsed) return result;
// rel value can be multiple whitespace-separated rels.
var splitRel = parsed.rel.split(/\s+/);
splitRel.forEach(function(rel) {
if (!result[rel]) {
result[rel] = {
url: parsed.url,
params: parsed.params
};
}
});
return result;
}, {});
}
var parseLinkHeader_1 = parseLinkHeader;
/**
* A Mapbox API response.
*
* @class MapiResponse
* @property {Object} body - The response body, parsed as JSON.
* @property {string} rawBody - The raw response body.
* @property {number} statusCode - The response's status code.
* @property {Object} headers - The parsed response headers.
* @property {Object} links - The parsed response links.
* @property {MapiRequest} request - The response's originating `MapiRequest`.
*/
/**
* @ignore
* @param {MapiRequest} request
* @param {Object} responseData
* @param {Object} responseData.headers
* @param {string} responseData.body
* @param {number} responseData.statusCode
*/
function MapiResponse(request, responseData) {
this.request = request;
this.headers = responseData.headers;
this.rawBody = responseData.body;
this.statusCode = responseData.statusCode;
try {
this.body = JSON.parse(responseData.body || '{}');
} catch (parseError) {
this.body = responseData.body;
}
this.links = parseLinkHeader_1(this.headers.link);
}
/**
* Check if there is a next page that you can fetch.
*
* @returns {boolean}
*/
MapiResponse.prototype.hasNextPage = function hasNextPage() {
return !!this.links.next;
};
/**
* Create a request for the next page, if there is one.
* If there is no next page, returns `null`.
*
* @returns {MapiRequest | null}
*/
MapiResponse.prototype.nextPage = function nextPage() {
if (!this.hasNextPage()) return null;
return this.request._extend({
path: this.links.next.url
});
};
var mapiResponse = MapiResponse;
var constants = {
API_ORIGIN: 'https://api.mapbox.com',
EVENT_PROGRESS_DOWNLOAD: 'downloadProgress',
EVENT_PROGRESS_UPLOAD: 'uploadProgress',
EVENT_ERROR: 'error',
EVENT_RESPONSE: 'response',
ERROR_HTTP: 'HttpError',
ERROR_REQUEST_ABORTED: 'RequestAbortedError'
};
/**
* A Mapbox API error.
*
* If there's an error during the API transaction,
* the Promise returned by `MapiRequest`'s [`send`](#send)
* method should reject with a `MapiError`.
*
* @class MapiError
* @hideconstructor
* @property {MapiRequest} request - The errored request.
* @property {string} type - The type of error. Usually this is `'HttpError'`.
* If the request was aborted, so the error was
* not sent from the server, the type will be
* `'RequestAbortedError'`.
* @property {number} [statusCode] - The numeric status code of
* the HTTP response.
* @property {Object | string} [body] - If the server sent a response body,
* this property exposes that response, parsed as JSON if possible.
* @property {string} [message] - Whatever message could be derived from the
* call site and HTTP response.
*
* @param {MapiRequest} options.request
* @param {number} [options.statusCode]
* @param {string} [options.body]
* @param {string} [options.message]
* @param {string} [options.type]
*/
function MapiError(options) {
var errorType = options.type || constants.ERROR_HTTP;
var body;
if (options.body) {
try {
body = JSON.parse(options.body);
} catch (e) {
body = options.body;
}
} else {
body = null;
}
var message = options.message || null;
if (!message) {
if (typeof body === 'string') {
message = body;
} else if (body && typeof body.message === 'string') {
message = body.message;
} else if (errorType === constants.ERROR_REQUEST_ABORTED) {
message = 'Request aborted';
}
}
this.message = message;
this.type = errorType;
this.statusCode = options.statusCode || null;
this.request = options.request;
this.body = body;
}
var mapiError = MapiError;
function parseSingleHeader(raw) {
var boundary = raw.indexOf(':');
var name = raw
.substring(0, boundary)
.trim()
.toLowerCase();
var value = raw.substring(boundary + 1).trim();
return {
name: name,
value: value
};
}
/**
* Parse raw headers into an object with lowercase properties.
* Does not fully parse headings into more complete data structure,
* as larger libraries might do. Also does not deal with duplicate
* headers because Node doesn't seem to deal with those well, so
* we shouldn't let the browser either, for consistency.
*
* @param {string} raw
* @returns {Object}
*/
function parseHeaders(raw) {
var headers = {};
if (!raw) {
return headers;
}
raw
.trim()
.split(/[\r|\n]+/)
.forEach(function(rawHeader) {
var parsed = parseSingleHeader(rawHeader);
headers[parsed.name] = parsed.value;
});
return headers;
}
var parseHeaders_1 = parseHeaders;
// Keys are request IDs, values are XHRs.
var requestsUnderway = {};
function browserAbort(request) {
var xhr = requestsUnderway[request.id];
if (!xhr) return;
xhr.abort();
delete requestsUnderway[request.id];
}
function createResponse(request, xhr) {
return new mapiResponse(request, {
body: xhr.response,
headers: parseHeaders_1(xhr.getAllResponseHeaders()),
statusCode: xhr.status
});
}
function normalizeBrowserProgressEvent(event) {
var total = event.total;
var transferred = event.loaded;
var percent = (100 * transferred) / total;
return {
total: total,
transferred: transferred,
percent: percent
};
}
function sendRequestXhr(request, xhr) {
return new Promise(function(resolve, reject) {
xhr.onprogress = function(event) {
request.emitter.emit(
constants.EVENT_PROGRESS_DOWNLOAD,
normalizeBrowserProgressEvent(event)
);
};
var file = request.file;
if (file) {
xhr.upload.onprogress = function(event) {
request.emitter.emit(
constants.EVENT_PROGRESS_UPLOAD,
normalizeBrowserProgressEvent(event)
);
};
}
xhr.onerror = function(error) {
reject(error);
};
xhr.onabort = function() {
var mapiError$$1 = new mapiError({
request: request,
type: constants.ERROR_REQUEST_ABORTED
});
reject(mapiError$$1);
};
xhr.onload = function() {
delete requestsUnderway[request.id];
if (xhr.status < 200 || xhr.status >= 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 @atks 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<MapiResponse>}
*/
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<string>|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<DirectionsWaypoint>} 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<string>} [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<string>} [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<string>} [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<string>} [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<MapMatchingPoint>} 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<MatrixPoint>} 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<number>} [config.sources] - Use coordinates with given index as sources.
* @param {'all'|Array<number>} [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<OptimizationWaypoint>} 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<Distribution>} [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.<Array.<Number>>} 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<Overlay>} [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<Coordinates>} 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<string>} 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<string>} 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<string>} [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<string>} [config.scopes]
* @param {Array<string>} [config.resources]
* @param {Array<string>} [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<string>} 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<string>} [config.scopes]
* @param {Array<string>} [config.resources]
* @param {Array<string>} [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;
})));