UNPKG

vega

Version:

The Vega visualization grammar.

1,695 lines (1,637 loc) 1.3 MB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.vega = {})); })(this, (function (exports) { 'use strict'; function accessor(fn, fields, name) { fn.fields = fields || []; fn.fname = name; return fn; } function accessorName(fn) { return fn == null ? null : fn.fname; } function accessorFields(fn) { return fn == null ? null : fn.fields; } function getter$1(path) { return path.length === 1 ? get1(path[0]) : getN(path); } const get1 = field => function (obj) { return obj[field]; }; const getN = path => { const len = path.length; return function (obj) { for (let i = 0; i < len; ++i) { obj = obj[path[i]]; } return obj; }; }; function error(message) { throw Error(message); } function splitAccessPath(p) { const path = [], n = p.length; let q = null, b = 0, s = '', i, j, c; p = p + ''; function push() { path.push(s + p.substring(i, j)); s = ''; i = j + 1; } for (i = j = 0; j < n; ++j) { c = p[j]; if (c === '\\') { s += p.substring(i, j++); i = j; } else if (c === q) { push(); q = null; b = -1; } else if (q) { continue; } else if (i === b && c === '"') { i = j + 1; q = c; } else if (i === b && c === "'") { i = j + 1; q = c; } else if (c === '.' && !b) { if (j > i) { push(); } else { i = j + 1; } } else if (c === '[') { if (j > i) push(); b = i = j + 1; } else if (c === ']') { if (!b) error('Access path missing open bracket: ' + p); if (b > 0) push(); b = 0; i = j + 1; } } if (b) error('Access path missing closing bracket: ' + p); if (q) error('Access path missing closing quote: ' + p); if (j > i) { j++; push(); } return path; } function field$1(field, name, opt) { const path = splitAccessPath(field); field = path.length === 1 ? path[0] : field; return accessor((opt && opt.get || getter$1)(path), [field], name || field); } const id = field$1('id'); const identity$6 = accessor(_ => _, [], 'identity'); const zero$3 = accessor(() => 0, [], 'zero'); const one$2 = accessor(() => 1, [], 'one'); const truthy = accessor(() => true, [], 'true'); const falsy = accessor(() => false, [], 'false'); /** Utilities common to vega-interpreter and vega-expression for evaluating expresions */ /** JSON authors are not allowed to set these properties, as these are built-in to the JS Object Prototype and should not be overridden. */ const DisallowedObjectProperties = new Set([...Object.getOwnPropertyNames(Object.prototype).filter(name => typeof Object.prototype[name] === 'function'), '__proto__']); function log$1$1(method, level, input) { const args = [level].concat([].slice.call(input)); console[method].apply(console, args); // eslint-disable-line no-console } const None$2 = 0; const Error$1 = 1; const Warn = 2; const Info = 3; const Debug = 4; function logger(_, method, handler = log$1$1) { let level = _ || None$2; return { level(_) { if (arguments.length) { level = +_; return this; } else { return level; } }, error() { if (level >= Error$1) handler(method || 'error', 'ERROR', arguments); return this; }, warn() { if (level >= Warn) handler(method || 'warn', 'WARN', arguments); return this; }, info() { if (level >= Info) handler(method || 'log', 'INFO', arguments); return this; }, debug() { if (level >= Debug) handler(method || 'log', 'DEBUG', arguments); return this; } }; } var isArray = Array.isArray; function isObject(_) { return _ === Object(_); } const isLegalKey = key => key !== '__proto__'; function mergeConfig(...configs) { return configs.reduce((out, source) => { for (const key in source) { if (key === 'signals') { // for signals, we merge the signals arrays // source signals take precedence over // existing signals with the same name out.signals = mergeNamed(out.signals, source.signals); } else { // otherwise, merge objects subject to recursion constraints // for legend block, recurse for the layout entry only // for style block, recurse for all properties // otherwise, no recursion: objects overwrite, no merging const r = key === 'legend' ? { layout: 1 } : key === 'style' ? true : null; writeConfig(out, key, source[key], r); } } return out; }, {}); } function writeConfig(output, key, value, recurse) { if (!isLegalKey(key)) return; let k, o; if (isObject(value) && !isArray(value)) { o = isObject(output[key]) ? output[key] : output[key] = {}; for (k in value) { if (recurse && (recurse === true || recurse[k])) { writeConfig(o, k, value[k]); } else if (isLegalKey(k)) { o[k] = value[k]; } } } else { output[key] = value; } } function mergeNamed(a, b) { if (a == null) return b; const map = {}, out = []; function add(_) { if (!map[_.name]) { map[_.name] = 1; out.push(_); } } b.forEach(add); a.forEach(add); return out; } function peek$1(array) { return array[array.length - 1]; } function toNumber(_) { return _ == null || _ === '' ? null : +_; } const exp$2 = sign => x => sign * Math.exp(x); const log$4 = sign => x => Math.log(sign * x); const symlog$1 = c => x => Math.sign(x) * Math.log1p(Math.abs(x / c)); const symexp = c => x => Math.sign(x) * Math.expm1(Math.abs(x)) * c; const pow$4 = exponent => x => x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent); function pan(domain, delta, lift, ground) { const d0 = lift(domain[0]), d1 = lift(peek$1(domain)), dd = (d1 - d0) * delta; return [ground(d0 - dd), ground(d1 - dd)]; } function panLinear(domain, delta) { return pan(domain, delta, toNumber, identity$6); } function panLog(domain, delta) { var sign = Math.sign(domain[0]); return pan(domain, delta, log$4(sign), exp$2(sign)); } function panPow(domain, delta, exponent) { return pan(domain, delta, pow$4(exponent), pow$4(1 / exponent)); } function panSymlog(domain, delta, constant) { return pan(domain, delta, symlog$1(constant), symexp(constant)); } function zoom$1(domain, anchor, scale, lift, ground) { const d0 = lift(domain[0]), d1 = lift(peek$1(domain)), da = anchor != null ? lift(anchor) : (d0 + d1) / 2; return [ground(da + (d0 - da) * scale), ground(da + (d1 - da) * scale)]; } function zoomLinear(domain, anchor, scale) { return zoom$1(domain, anchor, scale, toNumber, identity$6); } function zoomLog(domain, anchor, scale) { const sign = Math.sign(domain[0]); return zoom$1(domain, anchor, scale, log$4(sign), exp$2(sign)); } function zoomPow(domain, anchor, scale, exponent) { return zoom$1(domain, anchor, scale, pow$4(exponent), pow$4(1 / exponent)); } function zoomSymlog(domain, anchor, scale, constant) { return zoom$1(domain, anchor, scale, symlog$1(constant), symexp(constant)); } function quarter(date) { return 1 + ~~(new Date(date).getMonth() / 3); } function utcquarter(date) { return 1 + ~~(new Date(date).getUTCMonth() / 3); } function array$5(_) { return _ != null ? isArray(_) ? _ : [_] : []; } /** * Span-preserving range clamp. If the span of the input range is less * than (max - min) and an endpoint exceeds either the min or max value, * the range is translated such that the span is preserved and one * endpoint touches the boundary of the min/max range. * If the span exceeds (max - min), the range [min, max] is returned. */ function clampRange(range, min, max) { let lo = range[0], hi = range[1], span; if (hi < lo) { span = hi; hi = lo; lo = span; } span = hi - lo; return span >= max - min ? [min, max] : [lo = Math.min(Math.max(lo, min), max - span), lo + span]; } function isFunction(_) { return typeof _ === 'function'; } const DESCENDING = 'descending'; function compare$1(fields, orders, opt) { opt = opt || {}; orders = array$5(orders) || []; const ord = [], get = [], fmap = {}, gen = opt.comparator || comparator; array$5(fields).forEach((f, i) => { if (f == null) return; ord.push(orders[i] === DESCENDING ? -1 : 1); get.push(f = isFunction(f) ? f : field$1(f, null, opt)); (accessorFields(f) || []).forEach(_ => fmap[_] = 1); }); return get.length === 0 ? null : accessor(gen(get, ord), Object.keys(fmap)); } const ascending$2 = (u, v) => (u < v || u == null) && v != null ? -1 : (u > v || v == null) && u != null ? 1 : (v = v instanceof Date ? +v : v, u = u instanceof Date ? +u : u) !== u && v === v ? -1 : v !== v && u === u ? 1 : 0; const comparator = (fields, orders) => fields.length === 1 ? compare1(fields[0], orders[0]) : compareN(fields, orders, fields.length); const compare1 = (field, order) => function (a, b) { return ascending$2(field(a), field(b)) * order; }; const compareN = (fields, orders, n) => { orders.push(0); // pad zero for convenient lookup return function (a, b) { let f, c = 0, i = -1; while (c === 0 && ++i < n) { f = fields[i]; c = ascending$2(f(a), f(b)); } return c * orders[i]; }; }; function constant$5(_) { return isFunction(_) ? _ : () => _; } function debounce(delay, handler) { let tid; return e => { if (tid) clearTimeout(tid); tid = setTimeout(() => (handler(e), tid = null), delay); }; } function extend$1(_) { for (let x, k, i = 1, len = arguments.length; i < len; ++i) { x = arguments[i]; for (k in x) { _[k] = x[k]; } } return _; } /** * Return an array with minimum and maximum values, in the * form [min, max]. Ignores null, undefined, and NaN values. */ function extent(array, f) { let i = 0, n, v, min, max; if (array && (n = array.length)) { if (f == null) { // find first valid value for (v = array[i]; i < n && (v == null || v !== v); v = array[++i]); min = max = v; // visit all other values for (; i < n; ++i) { v = array[i]; // skip null/undefined; NaN will fail all comparisons if (v != null) { if (v < min) min = v; if (v > max) max = v; } } } else { // find first valid value for (v = f(array[i]); i < n && (v == null || v !== v); v = f(array[++i])); min = max = v; // visit all other values for (; i < n; ++i) { v = f(array[i]); // skip null/undefined; NaN will fail all comparisons if (v != null) { if (v < min) min = v; if (v > max) max = v; } } } } return [min, max]; } function extentIndex(array, f) { const n = array.length; let i = -1, a, b, c, u, v; if (f == null) { while (++i < n) { b = array[i]; if (b != null && b >= b) { a = c = b; break; } } if (i === n) return [-1, -1]; u = v = i; while (++i < n) { b = array[i]; if (b != null) { if (a > b) { a = b; u = i; } if (c < b) { c = b; v = i; } } } } else { while (++i < n) { b = f(array[i], i, array); if (b != null && b >= b) { a = c = b; break; } } if (i === n) return [-1, -1]; u = v = i; while (++i < n) { b = f(array[i], i, array); if (b != null) { if (a > b) { a = b; u = i; } if (c < b) { c = b; v = i; } } } } return [u, v]; } function has$1(object, property) { return Object.hasOwn(object, property); } const NULL = {}; function fastmap(input) { let obj = {}, test; function has$1$1(key) { return has$1(obj, key) && obj[key] !== NULL; } const map = { size: 0, empty: 0, object: obj, has: has$1$1, get(key) { return has$1$1(key) ? obj[key] : undefined; }, set(key, value) { if (!has$1$1(key)) { ++map.size; if (obj[key] === NULL) --map.empty; } obj[key] = value; return this; }, delete(key) { if (has$1$1(key)) { --map.size; ++map.empty; obj[key] = NULL; } return this; }, clear() { map.size = map.empty = 0; map.object = obj = {}; }, test(_) { if (arguments.length) { test = _; return map; } else { return test; } }, clean() { const next = {}; let size = 0; for (const key in obj) { const value = obj[key]; if (value !== NULL && (!test || !test(value))) { next[key] = value; ++size; } } map.size = size; map.empty = 0; map.object = obj = next; } }; if (input) Object.keys(input).forEach(key => { map.set(key, input[key]); }); return map; } function flush(range, value, threshold, left, right, center) { if (!threshold && threshold !== 0) return center; const t = +threshold; let a = range[0], b = peek$1(range), l; // swap endpoints if range is reversed if (b < a) { l = a; a = b; b = l; } // compare value to endpoints l = Math.abs(value - a); const r = Math.abs(b - value); // adjust if value is within threshold distance of endpoint return l < r && l <= t ? left : r <= t ? right : center; } function inherits(child, parent, members) { const proto = child.prototype = Object.create(parent.prototype); Object.defineProperty(proto, 'constructor', { value: child, writable: true, enumerable: true, configurable: true }); return extend$1(proto, members); } /** * Predicate that returns true if the value lies within the span * of the given range. The left and right flags control the use * of inclusive (true) or exclusive (false) comparisons. */ function inrange(value, range, left, right) { let r0 = range[0], r1 = range[range.length - 1], t; if (r0 > r1) { t = r0; r0 = r1; r1 = t; } left = left === undefined || left; right = right === undefined || right; return (left ? r0 <= value : r0 < value) && (right ? value <= r1 : value < r1); } function isBoolean$1(_) { return typeof _ === 'boolean'; } function isDate$1(_) { return Object.prototype.toString.call(_) === '[object Date]'; } function isIterable(_) { return _ && isFunction(_[Symbol.iterator]); } function isNumber$1(_) { return typeof _ === 'number'; } function isRegExp(_) { return Object.prototype.toString.call(_) === '[object RegExp]'; } function isString(_) { return typeof _ === 'string'; } function key(fields, flat, opt) { if (fields) { fields = flat ? array$5(fields).map(f => f.replace(/\\(.)/g, '$1')) : array$5(fields); } const len = fields && fields.length, gen = opt && opt.get || getter$1, map = f => gen(flat ? [f] : splitAccessPath(f)); let fn; if (!len) { fn = function () { return ''; }; } else if (len === 1) { const get = map(fields[0]); fn = function (_) { return '' + get(_); }; } else { const get = fields.map(map); fn = function (_) { let s = '' + get[0](_), i = 0; while (++i < len) s += '|' + get[i](_); return s; }; } return accessor(fn, fields, 'key'); } function lerp(array, frac) { const lo = array[0], hi = peek$1(array), f = +frac; return !f ? lo : f === 1 ? hi : lo + f * (hi - lo); } const DEFAULT_MAX_SIZE = 10000; // adapted from https://github.com/dominictarr/hashlru/ (MIT License) function lruCache(maxsize) { maxsize = +maxsize || DEFAULT_MAX_SIZE; let curr, prev, size; const clear = () => { curr = {}; prev = {}; size = 0; }; const update = (key, value) => { if (++size > maxsize) { prev = curr; curr = {}; size = 1; } return curr[key] = value; }; clear(); return { clear, has: key => has$1(curr, key) || has$1(prev, key), get: key => has$1(curr, key) ? curr[key] : has$1(prev, key) ? update(key, prev[key]) : undefined, set: (key, value) => has$1(curr, key) ? curr[key] = value : update(key, value) }; } function merge$3(compare, array0, array1, output) { const n0 = array0.length, n1 = array1.length; if (!n1) return array0; if (!n0) return array1; const merged = output || new array0.constructor(n0 + n1); let i0 = 0, i1 = 0, i = 0; for (; i0 < n0 && i1 < n1; ++i) { merged[i] = compare(array0[i0], array1[i1]) > 0 ? array1[i1++] : array0[i0++]; } for (; i0 < n0; ++i0, ++i) { merged[i] = array0[i0]; } for (; i1 < n1; ++i1, ++i) { merged[i] = array1[i1]; } return merged; } function repeat(str, reps) { let s = ''; while (--reps >= 0) s += str; return s; } function pad$2(str, length, padchar, align) { const c = padchar || ' ', s = str + '', n = length - s.length; return n <= 0 ? s : align === 'left' ? repeat(c, n) + s : align === 'center' ? repeat(c, ~~(n / 2)) + s + repeat(c, Math.ceil(n / 2)) : s + repeat(c, n); } /** * Return the numerical span of an array: the difference between * the last and first values. */ function span(array) { return array && peek$1(array) - array[0] || 0; } function $(x) { return isArray(x) ? `[${x.map(v => v === null ? 'null' : $(v))}]` : isObject(x) || isString(x) ? // Output valid JSON and JS source strings. // See https://github.com/judofyr/timeless/blob/master/posts/json-isnt-a-javascript-subset.md JSON.stringify(x).replaceAll('\u2028', '\\u2028').replaceAll('\u2029', '\\u2029') : x; } function toBoolean(_) { return _ == null || _ === '' ? null : !_ || _ === 'false' || _ === '0' ? false : !!_; } const defaultParser = _ => isNumber$1(_) ? _ : isDate$1(_) ? _ : Date.parse(_); function toDate(_, parser) { parser = parser || defaultParser; return _ == null || _ === '' ? null : parser(_); } function toString(_) { return _ == null || _ === '' ? null : _ + ''; } function toSet(_) { const s = {}, n = _.length; for (let i = 0; i < n; ++i) s[_[i]] = true; return s; } function truncate$1(str, length, align, ellipsis) { const e = ellipsis != null ? ellipsis : '\u2026', s = str + '', n = s.length, l = Math.max(0, length - e.length); return n <= length ? s : align === 'left' ? e + s.slice(n - l) : align === 'center' ? s.slice(0, Math.ceil(l / 2)) + e + s.slice(n - ~~(l / 2)) : s.slice(0, l) + e; } function visitArray(array, filter, visitor) { if (array) { if (filter) { const n = array.length; for (let i = 0; i < n; ++i) { const t = filter(array[i]); if (t) visitor(t, i, array); } } else { array.forEach(visitor); } } } var EOL = {}, EOF = {}, QUOTE = 34, NEWLINE = 10, RETURN = 13; function objectConverter(columns) { return new Function("d", "return {" + columns.map(function (name, i) { return JSON.stringify(name) + ": d[" + i + "] || \"\""; }).join(",") + "}"); } function customConverter(columns, f) { var object = objectConverter(columns); return function (row, i) { return f(object(row), i, columns); }; } // Compute unique columns in order of discovery. function inferColumns(rows) { var columnSet = Object.create(null), columns = []; rows.forEach(function (row) { for (var column in row) { if (!(column in columnSet)) { columns.push(columnSet[column] = column); } } }); return columns; } function pad$1(value, width) { var s = value + "", length = s.length; return length < width ? new Array(width - length + 1).join(0) + s : s; } function formatYear$1(year) { return year < 0 ? "-" + pad$1(-year, 6) : year > 9999 ? "+" + pad$1(year, 6) : pad$1(year, 4); } function formatDate(date) { var hours = date.getUTCHours(), minutes = date.getUTCMinutes(), seconds = date.getUTCSeconds(), milliseconds = date.getUTCMilliseconds(); return isNaN(date) ? "Invalid Date" : formatYear$1(date.getUTCFullYear()) + "-" + pad$1(date.getUTCMonth() + 1, 2) + "-" + pad$1(date.getUTCDate(), 2) + (milliseconds ? "T" + pad$1(hours, 2) + ":" + pad$1(minutes, 2) + ":" + pad$1(seconds, 2) + "." + pad$1(milliseconds, 3) + "Z" : seconds ? "T" + pad$1(hours, 2) + ":" + pad$1(minutes, 2) + ":" + pad$1(seconds, 2) + "Z" : minutes || hours ? "T" + pad$1(hours, 2) + ":" + pad$1(minutes, 2) + "Z" : ""); } function dsvFormat (delimiter) { var reFormat = new RegExp("[\"" + delimiter + "\n\r]"), DELIMITER = delimiter.charCodeAt(0); function parse(text, f) { var convert, columns, rows = parseRows(text, function (row, i) { if (convert) return convert(row, i - 1); columns = row, convert = f ? customConverter(row, f) : objectConverter(row); }); rows.columns = columns || []; return rows; } function parseRows(text, f) { var rows = [], // output rows N = text.length, I = 0, // current character index n = 0, // current line number t, // current token eof = N <= 0, // current token followed by EOF? eol = false; // current token followed by EOL? // Strip the trailing newline. if (text.charCodeAt(N - 1) === NEWLINE) --N; if (text.charCodeAt(N - 1) === RETURN) --N; function token() { if (eof) return EOF; if (eol) return eol = false, EOL; // Unescape quotes. var i, j = I, c; if (text.charCodeAt(j) === QUOTE) { while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE); if ((i = I) >= N) eof = true;else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; } return text.slice(j + 1, i - 1).replace(/""/g, "\""); } // Find next delimiter or newline. while (I < N) { if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; } else if (c !== DELIMITER) continue; return text.slice(j, i); } // Return last token before EOF. return eof = true, text.slice(j, N); } while ((t = token()) !== EOF) { var row = []; while (t !== EOL && t !== EOF) row.push(t), t = token(); if (f && (row = f(row, n++)) == null) continue; rows.push(row); } return rows; } function preformatBody(rows, columns) { return rows.map(function (row) { return columns.map(function (column) { return formatValue(row[column]); }).join(delimiter); }); } function format(rows, columns) { if (columns == null) columns = inferColumns(rows); return [columns.map(formatValue).join(delimiter)].concat(preformatBody(rows, columns)).join("\n"); } function formatBody(rows, columns) { if (columns == null) columns = inferColumns(rows); return preformatBody(rows, columns).join("\n"); } function formatRows(rows) { return rows.map(formatRow).join("\n"); } function formatRow(row) { return row.map(formatValue).join(delimiter); } function formatValue(value) { return value == null ? "" : value instanceof Date ? formatDate(value) : reFormat.test(value += "") ? "\"" + value.replace(/"/g, "\"\"") + "\"" : value; } return { parse: parse, parseRows: parseRows, format: format, formatBody: formatBody, formatRows: formatRows, formatRow: formatRow, formatValue: formatValue }; } function identity$5 (x) { return x; } function transform$3 (transform) { if (transform == null) return identity$5; var x0, y0, kx = transform.scale[0], ky = transform.scale[1], dx = transform.translate[0], dy = transform.translate[1]; return function (input, i) { if (!i) x0 = y0 = 0; var j = 2, n = input.length, output = new Array(n); output[0] = (x0 += input[0]) * kx + dx; output[1] = (y0 += input[1]) * ky + dy; while (j < n) output[j] = input[j], ++j; return output; }; } function reverse$1 (array, n) { var t, j = array.length, i = j - n; while (i < --j) t = array[i], array[i++] = array[j], array[j] = t; } function feature (topology, o) { if (typeof o === "string") o = topology.objects[o]; return o.type === "GeometryCollection" ? { type: "FeatureCollection", features: o.geometries.map(function (o) { return feature$1(topology, o); }) } : feature$1(topology, o); } function feature$1(topology, o) { var id = o.id, bbox = o.bbox, properties = o.properties == null ? {} : o.properties, geometry = object$1(topology, o); return id == null && bbox == null ? { type: "Feature", properties: properties, geometry: geometry } : bbox == null ? { type: "Feature", id: id, properties: properties, geometry: geometry } : { type: "Feature", id: id, bbox: bbox, properties: properties, geometry: geometry }; } function object$1(topology, o) { var transformPoint = transform$3(topology.transform), arcs = topology.arcs; function arc(i, points) { if (points.length) points.pop(); for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length; k < n; ++k) { points.push(transformPoint(a[k], k)); } if (i < 0) reverse$1(points, n); } function point(p) { return transformPoint(p); } function line(arcs) { var points = []; for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points); if (points.length < 2) points.push(points[0]); // This should never happen per the specification. return points; } function ring(arcs) { var points = line(arcs); while (points.length < 4) points.push(points[0]); // This may happen if an arc has only two points. return points; } function polygon(arcs) { return arcs.map(ring); } function geometry(o) { var type = o.type, coordinates; switch (type) { case "GeometryCollection": return { type: type, geometries: o.geometries.map(geometry) }; case "Point": coordinates = point(o.coordinates); break; case "MultiPoint": coordinates = o.coordinates.map(point); break; case "LineString": coordinates = line(o.arcs); break; case "MultiLineString": coordinates = o.arcs.map(line); break; case "Polygon": coordinates = polygon(o.arcs); break; case "MultiPolygon": coordinates = o.arcs.map(polygon); break; default: return null; } return { type: type, coordinates: coordinates }; } return geometry(o); } function stitch (topology, arcs) { var stitchedArcs = {}, fragmentByStart = {}, fragmentByEnd = {}, fragments = [], emptyIndex = -1; // Stitch empty arcs first, since they may be subsumed by other arcs. arcs.forEach(function (i, j) { var arc = topology.arcs[i < 0 ? ~i : i], t; if (arc.length < 3 && !arc[1][0] && !arc[1][1]) { t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t; } }); arcs.forEach(function (i) { var e = ends(i), start = e[0], end = e[1], f, g; if (f = fragmentByEnd[start]) { delete fragmentByEnd[f.end]; f.push(i); f.end = end; if (g = fragmentByStart[end]) { delete fragmentByStart[g.start]; var fg = g === f ? f : f.concat(g); fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg; } else { fragmentByStart[f.start] = fragmentByEnd[f.end] = f; } } else if (f = fragmentByStart[end]) { delete fragmentByStart[f.start]; f.unshift(i); f.start = start; if (g = fragmentByEnd[start]) { delete fragmentByEnd[g.end]; var gf = g === f ? f : g.concat(f); fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf; } else { fragmentByStart[f.start] = fragmentByEnd[f.end] = f; } } else { f = [i]; fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f; } }); function ends(i) { var arc = topology.arcs[i < 0 ? ~i : i], p0 = arc[0], p1; if (topology.transform) p1 = [0, 0], arc.forEach(function (dp) { p1[0] += dp[0], p1[1] += dp[1]; });else p1 = arc[arc.length - 1]; return i < 0 ? [p1, p0] : [p0, p1]; } function flush(fragmentByEnd, fragmentByStart) { for (var k in fragmentByEnd) { var f = fragmentByEnd[k]; delete fragmentByStart[f.start]; delete f.start; delete f.end; f.forEach(function (i) { stitchedArcs[i < 0 ? ~i : i] = 1; }); fragments.push(f); } } flush(fragmentByEnd, fragmentByStart); flush(fragmentByStart, fragmentByEnd); arcs.forEach(function (i) { if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]); }); return fragments; } function mesh (topology) { return object$1(topology, meshArcs.apply(this, arguments)); } function meshArcs(topology, object, filter) { var arcs, i, n; if (arguments.length > 1) arcs = extractArcs(topology, object, filter);else for (i = 0, arcs = new Array(n = topology.arcs.length); i < n; ++i) arcs[i] = i; return { type: "MultiLineString", arcs: stitch(topology, arcs) }; } function extractArcs(topology, object, filter) { var arcs = [], geomsByArc = [], geom; function extract0(i) { var j = i < 0 ? ~i : i; (geomsByArc[j] || (geomsByArc[j] = [])).push({ i: i, g: geom }); } function extract1(arcs) { arcs.forEach(extract0); } function extract2(arcs) { arcs.forEach(extract1); } function extract3(arcs) { arcs.forEach(extract2); } function geometry(o) { switch (geom = o, o.type) { case "GeometryCollection": o.geometries.forEach(geometry); break; case "LineString": extract1(o.arcs); break; case "MultiLineString": case "Polygon": extract2(o.arcs); break; case "MultiPolygon": extract3(o.arcs); break; } } geometry(object); geomsByArc.forEach(filter == null ? function (geoms) { arcs.push(geoms[0].i); } : function (geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); }); return arcs; } function ascending$1(a, b) { return a == null || b == null ? NaN : a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; } function descending$1(a, b) { return a == null || b == null ? NaN : b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; } function bisector(f) { let compare1, compare2, delta; // If an accessor is specified, promote it to a comparator. In this case we // can test whether the search value is (self-) comparable. We can’t do this // for a comparator (except for specific, known comparators) because we can’t // tell if the comparator is symmetric, and an asymmetric comparator can’t be // used to test whether a single value is comparable. if (f.length !== 2) { compare1 = ascending$1; compare2 = (d, x) => ascending$1(f(d), x); delta = (d, x) => f(d) - x; } else { compare1 = f === ascending$1 || f === descending$1 ? f : zero$2; compare2 = f; delta = f; } function left(a, x, lo = 0, hi = a.length) { if (lo < hi) { if (compare1(x, x) !== 0) return hi; do { const mid = lo + hi >>> 1; if (compare2(a[mid], x) < 0) lo = mid + 1;else hi = mid; } while (lo < hi); } return lo; } function right(a, x, lo = 0, hi = a.length) { if (lo < hi) { if (compare1(x, x) !== 0) return hi; do { const mid = lo + hi >>> 1; if (compare2(a[mid], x) <= 0) lo = mid + 1;else hi = mid; } while (lo < hi); } return lo; } function center(a, x, lo = 0, hi = a.length) { const i = left(a, x, lo, hi - 1); return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i; } return { left, center, right }; } function zero$2() { return 0; } function number$6(x) { return x === null ? NaN : +x; } function* numbers$2(values, valueof) { if (valueof === undefined) { for (let value of values) { if (value != null && (value = +value) >= value) { yield value; } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) { yield value; } } } } const ascendingBisect = bisector(ascending$1); const bisectRight$1 = ascendingBisect.right; const bisectLeft$1 = ascendingBisect.left; bisector(number$6).center; function variance(values, valueof) { let count = 0; let delta; let mean = 0; let sum = 0; if (valueof === undefined) { for (let value of values) { if (value != null && (value = +value) >= value) { delta = value - mean; mean += delta / ++count; sum += delta * (value - mean); } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) { delta = value - mean; mean += delta / ++count; sum += delta * (value - mean); } } } if (count > 1) return sum / (count - 1); } function deviation(values, valueof) { const v = variance(values, valueof); return v ? Math.sqrt(v) : v; } // https://github.com/python/cpython/blob/a74eea238f5baba15797e2e8b570d153bc8690a7/Modules/mathmodule.c#L1423 class Adder { constructor() { this._partials = new Float64Array(32); this._n = 0; } add(x) { const p = this._partials; let i = 0; for (let j = 0; j < this._n && j < 32; j++) { const y = p[j], hi = x + y, lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x); if (lo) p[i++] = lo; x = hi; } p[i] = x; this._n = i + 1; return this; } valueOf() { const p = this._partials; let n = this._n, x, y, lo, hi = 0; if (n > 0) { hi = p[--n]; while (n > 0) { x = hi; y = p[--n]; hi = x + y; lo = y - (hi - x); if (lo) break; } if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) { y = lo * 2; x = hi + y; if (y == x - hi) hi = x; } } return hi; } } class InternMap extends Map { constructor(entries, key = keyof) { super(); Object.defineProperties(this, { _intern: { value: new Map() }, _key: { value: key } }); if (entries != null) for (const [key, value] of entries) this.set(key, value); } get(key) { return super.get(intern_get(this, key)); } has(key) { return super.has(intern_get(this, key)); } set(key, value) { return super.set(intern_set(this, key), value); } delete(key) { return super.delete(intern_delete(this, key)); } } class InternSet extends Set { constructor(values, key = keyof) { super(); Object.defineProperties(this, { _intern: { value: new Map() }, _key: { value: key } }); if (values != null) for (const value of values) this.add(value); } has(value) { return super.has(intern_get(this, value)); } add(value) { return super.add(intern_set(this, value)); } delete(value) { return super.delete(intern_delete(this, value)); } } function intern_get({ _intern, _key }, value) { const key = _key(value); return _intern.has(key) ? _intern.get(key) : value; } function intern_set({ _intern, _key }, value) { const key = _key(value); if (_intern.has(key)) return _intern.get(key); _intern.set(key, value); return value; } function intern_delete({ _intern, _key }, value) { const key = _key(value); if (_intern.has(key)) { value = _intern.get(key); _intern.delete(key); } return value; } function keyof(value) { return value !== null && typeof value === "object" ? value.valueOf() : value; } function permute(source, keys) { return Array.from(keys, key => source[key]); } function compareDefined(compare = ascending$1) { if (compare === ascending$1) return ascendingDefined; if (typeof compare !== "function") throw new TypeError("compare is not a function"); return (a, b) => { const x = compare(a, b); if (x || x === 0) return x; return (compare(b, b) === 0) - (compare(a, a) === 0); }; } function ascendingDefined(a, b) { return (a == null || !(a >= a)) - (b == null || !(b >= b)) || (a < b ? -1 : a > b ? 1 : 0); } const e10 = Math.sqrt(50), e5 = Math.sqrt(10), e2 = Math.sqrt(2); function tickSpec(start, stop, count) { const step = (stop - start) / Math.max(0, count), power = Math.floor(Math.log10(step)), error = step / Math.pow(10, power), factor = error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1; let i1, i2, inc; if (power < 0) { inc = Math.pow(10, -power) / factor; i1 = Math.round(start * inc); i2 = Math.round(stop * inc); if (i1 / inc < start) ++i1; if (i2 / inc > stop) --i2; inc = -inc; } else { inc = Math.pow(10, power) * factor; i1 = Math.round(start / inc); i2 = Math.round(stop / inc); if (i1 * inc < start) ++i1; if (i2 * inc > stop) --i2; } if (i2 < i1 && 0.5 <= count && count < 2) return tickSpec(start, stop, count * 2); return [i1, i2, inc]; } function ticks(start, stop, count) { stop = +stop, start = +start, count = +count; if (!(count > 0)) return []; if (start === stop) return [start]; const reverse = stop < start, [i1, i2, inc] = reverse ? tickSpec(stop, start, count) : tickSpec(start, stop, count); if (!(i2 >= i1)) return []; const n = i2 - i1 + 1, ticks = new Array(n); if (reverse) { if (inc < 0) for (let i = 0; i < n; ++i) ticks[i] = (i2 - i) / -inc;else for (let i = 0; i < n; ++i) ticks[i] = (i2 - i) * inc; } else { if (inc < 0) for (let i = 0; i < n; ++i) ticks[i] = (i1 + i) / -inc;else for (let i = 0; i < n; ++i) ticks[i] = (i1 + i) * inc; } return ticks; } function tickIncrement(start, stop, count) { stop = +stop, start = +start, count = +count; return tickSpec(start, stop, count)[2]; } function tickStep(start, stop, count) { stop = +stop, start = +start, count = +count; const reverse = stop < start, inc = reverse ? tickIncrement(stop, start, count) : tickIncrement(start, stop, count); return (reverse ? -1 : 1) * (inc < 0 ? 1 / -inc : inc); } function max$2(values, valueof) { let max; if (valueof === undefined) { for (const value of values) { if (value != null && (max < value || max === undefined && value >= value)) { max = value; } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (max < value || max === undefined && value >= value)) { max = value; } } } return max; } function min$2(values, valueof) { let min; if (valueof === undefined) { for (const value of values) { if (value != null && (min > value || min === undefined && value >= value)) { min = value; } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (min > value || min === undefined && value >= value)) { min = value; } } } return min; } // Based on https://github.com/mourner/quickselect // ISC license, Copyright 2018 Vladimir Agafonkin. function quickselect(array, k, left = 0, right = Infinity, compare) { k = Math.floor(k); left = Math.floor(Math.max(0, left)); right = Math.floor(Math.min(array.length - 1, right)); if (!(left <= k && k <= right)) return array; compare = compare === undefined ? ascendingDefined : compareDefined(compare); while (right > left) { if (right - left > 600) { const n = right - left + 1; const m = k - left + 1; const z = Math.log(n); const s = 0.5 * Math.exp(2 * z / 3); const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); const newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); quickselect(array, k, newLeft, newRight, compare); } const t = array[k]; let i = left; let j = right; swap$1(array, left, k); if (compare(array[right], t) > 0) swap$1(array, left, right); while (i < j) { swap$1(array, i, j), ++i, --j; while (compare(array[i], t) < 0) ++i; while (compare(array[j], t) > 0) --j; } if (compare(array[left], t) === 0) swap$1(array, left, j);else ++j, swap$1(array, j, right); if (j <= k) left = j + 1; if (k <= j) right = j - 1; } return array; } function swap$1(array, i, j) { const t = array[i]; array[i] = array[j]; array[j] = t; } function quantile$1(values, p, valueof) { values = Float64Array.from(numbers$2(values, valueof)); if (!(n = values.length) || isNaN(p = +p)) return; if (p <= 0 || n < 2) return min$2(values); if (p >= 1) return max$2(values); var n, i = (n - 1) * p, i0 = Math.floor(i), value0 = max$2(quickselect(values, i0).subarray(0, i0 + 1)), value1 = min$2(values.subarray(i0 + 1)); return value0 + (value1 - value0) * (i - i0); } function quantileSorted(values, p, valueof = number$6) { if (!(n = values.length) || isNaN(p = +p)) return; if (p <= 0 || n < 2) return +valueof(values[0], 0, values); if (p >= 1) return +valueof(values[n - 1], n - 1, values); var n, i = (n - 1) * p, i0 = Math.floor(i), value0 = +valueof(values[i0], i0, values), value1 = +valueof(values[i0 + 1], i0 + 1, values); return value0 + (value1 - value0) * (i - i0); } function mean(values, valueof) { let count = 0; let sum = 0; if (valueof === undefined) { for (let value of values) { if (value != null && (value = +value) >= value) { ++count, sum += value; } } } else { let index = -1; for (let value of values) { if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) { ++count, sum += value; } } } if (count) return sum / count; } function median(values, valueof) { return quantile$1(values, 0.5, valueof); } function* flatten(arrays) { for (const array of arrays) { yield* array; } } function merge$2(arrays) { return Array.from(flatten(arrays)); } function range$3(start, stop, step) { start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step; var i = -1, n = Math.max(0, Math.ceil((stop - start) / step)) | 0, range = new Array(n); while (++i < n) { range[i] = start + i * step; } return range; } function sum$1(values, valueof) { let sum = 0; { for (let value of values) { if (value = +value) { sum += value; } } } return sum; } function intersection(values, ...others) { values = new InternSet(values); others = others.map(set$4); out: for (const value of values) { for (const other of others) { if (!other.has(value)) { values.delete(value); continue out; } } } return values; } function set$4(values) { return values instanceof InternSet ? values : new InternSet(values); } function union(...others) { const set = new InternSet(); for (const other of others) { for (const o of other) { set.add(o); } } return set; } function formatDecimal (x) { return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10); } // Computes the decimal coefficient and exponent of the specified number x with // significant digits p, where x is positive and p is in [1, 21] or undefined. // For example, formatDecimalParts(1.23) returns ["123", 0]. function formatDecimalParts(x, p) { if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity var i, coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+ // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3). return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)]; } function exponent (x) { return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN; } function formatGroup (grouping, thousands) { return function (value, width) { var i = value.length, t = [], j = 0, g = grouping[0], length = 0; while (i > 0 && g > 0) { if (length + g + 1 > width) g = Math.max(1, width - length); t.push(value.substring(i -= g, i + g)); if ((length += g + 1) > width) break; g = grouping[j = (j + 1) % grouping.length]; } return t.reverse().join(thousands); }; } function formatNumerals (numerals) { return function (value) { return value.replace(/[0-9]/g, function (i) { return numerals[+i]; }); }; } // [[fill]align][sign][symbol][0][width][,][.precision][~][type] var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i; function formatSpecifier(specifier) { if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); var match; return new FormatSpecifier({ fill: match[1], align: match[2], sign: match[3], symbol: match[4], zero: match[5], width: match[6], comma: match[7], precision: match[8] && match[8].slice(1), trim: match[9], type: match[10] }); } formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof function FormatSpecifier(specifier) { this.fill = specifier.fill === undefined ? " " : specifier.fill + ""; this.align = specifier.align === undefined ? ">" : specifier.align + ""; this.sign = specifier.sign === undefined ? "-" : specifier.sign + ""; this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + ""; this.zero = !!specifier.zero; this.width = specifier.width === undefined ? undefined : +specifier.width; this.comma = !!specifier.comma; th