mapshaper
Version:
A tool for editing vector datasets for mapping and GIS.
1,863 lines (1,650 loc) • 1.47 MB
JavaScript
(function () {
var utils = /*#__PURE__*/Object.freeze({
__proto__: null,
get addThousandsSep () { return addThousandsSep; },
get addslashes () { return addslashes; },
get arrayToIndex () { return arrayToIndex; },
get clamp () { return clamp; },
get cleanNumericString () { return cleanNumericString; },
get contains () { return contains; },
get copyElements () { return copyElements; },
get countValues () { return countValues; },
get createBuffer () { return createBuffer; },
get default () { return utils; },
get defaults () { return defaults; },
get difference () { return difference; },
get endsWith () { return endsWith; },
get every () { return every; },
get expandoBuffer () { return expandoBuffer; },
get extend () { return extend$1; },
get extendBuffer () { return extendBuffer; },
get find () { return find; },
get findMedian () { return findMedian; },
get findQuantile () { return findQuantile; },
get findRankByValue () { return findRankByValue; },
get findStringPrefix () { return findStringPrefix; },
get findValueByPct () { return findValueByPct; },
get findValueByRank () { return findValueByRank; },
get forEach () { return forEach; },
get forEachProperty () { return forEachProperty; },
get format () { return format; },
get formatDateISO () { return formatDateISO; },
get formatIntlNumber () { return formatIntlNumber; },
get formatNumber () { return formatNumber$1; },
get formatNumberForDisplay () { return formatNumberForDisplay; },
get formatVersionedName () { return formatVersionedName; },
get formatter () { return formatter; },
get genericSort () { return genericSort; },
get getArrayBounds () { return getArrayBounds; },
get getGenericComparator () { return getGenericComparator; },
get getKeyComparator () { return getKeyComparator; },
get getSortedIds () { return getSortedIds; },
get getUniqueName () { return getUniqueName; },
get groupBy () { return groupBy; },
get htmlEscape () { return htmlEscape; },
get indexOf () { return indexOf; },
get indexOn () { return indexOn; },
get inherit () { return inherit; },
get initializeArray () { return initializeArray; },
get intersection () { return intersection; },
get isArray () { return isArray; },
get isArrayLike () { return isArrayLike; },
get isBoolean () { return isBoolean; },
get isDate () { return isDate; },
get isEven () { return isEven; },
get isFiniteNumber () { return isFiniteNumber; },
get isFunction () { return isFunction; },
get isInteger () { return isInteger; },
get isNonNegNumber () { return isNonNegNumber; },
get isNumber () { return isNumber; },
get isObject () { return isObject; },
get isOdd () { return isOdd$1; },
get isPromise () { return isPromise; },
get isString () { return isString; },
get isValidNumber () { return isValidNumber; },
get lpad () { return lpad; },
get ltrim () { return ltrim; },
get mean () { return mean; },
get merge () { return merge; },
get mergeNames () { return mergeNames; },
get numToStr () { return numToStr; },
get parseIntlNumber () { return parseIntlNumber; },
get parseNumber () { return parseNumber; },
get parsePercent () { return parsePercent; },
get parseString () { return parseString; },
get pickOne () { return pickOne; },
get pluck () { return pluck; },
get pluralSuffix () { return pluralSuffix; },
get promisify () { return promisify; },
get quicksort () { return quicksort$1; },
get quicksortPartition () { return quicksortPartition; },
get range () { return range; },
get reduceAsync () { return reduceAsync; },
get regexEscape () { return regexEscape; },
get reorderArray () { return reorderArray; },
get repeat () { return repeat; },
get repeatString () { return repeatString; },
get replaceArray () { return replaceArray; },
get rpad () { return rpad; },
get rtrim () { return rtrim; },
get shuffle () { return shuffle; },
get some () { return some; },
get sortArrayIndex () { return sortArrayIndex; },
get sortOn () { return sortOn; },
get splitLines () { return splitLines; },
get sum () { return sum$1; },
get toArray () { return toArray; },
get toBuffer () { return toBuffer; },
get trim () { return trim; },
get trimQuotes () { return trimQuotes; },
get uniq () { return uniq; },
get uniqifyNames () { return uniqifyNames; },
get wildcardToRegExp () { return wildcardToRegExp; }
});
// This module provides a way for multiple jobs to run together asynchronously
// while keeping job-level context variables (like "defs") separate.
var stash = {};
function stashVar(key, val) {
if (key in stash) {
error('Tried to replace a stashed variable:', key);
}
stash[key] = val;
}
function getStashedVar(key) {
if (key in stash === false) {
return undefined; // to support running commands in tests
// error('Tried to read a nonexistent variable from the stash:', key);
}
return stash[key];
}
function clearStash() {
stash = {};
}
var Stash = /*#__PURE__*/Object.freeze({
__proto__: null,
clearStash: clearStash,
getStashedVar: getStashedVar,
stashVar: stashVar
});
// Several dependencies are loaded via require()
var f;
if (typeof require == 'function') {
// Node.js context: native require() function
f = require;
} else if (typeof window == 'object' && window.modules) {
// running in web GUI
f = function(name) {
return window.modules[name];
};
} else {
// stub to avoid runtime error in a handful of tests
f = function() {};
}
var require$1 = f;
// Fall back to browserify's Buffer polyfill
var B$3 = typeof Buffer != 'undefined' ? Buffer : require$1('buffer').Buffer;
var uniqCount = 0;
function getUniqueName(prefix) {
return (prefix || "__id_") + (++uniqCount);
}
function isFunction(obj) {
return typeof obj == 'function';
}
function isPromise(arg) {
return arg ? isFunction(arg.then) : false;
}
function isObject(obj) {
return obj === Object(obj); // via underscore
}
function clamp(val, min, max) {
return val < min ? min : (val > max ? max : val);
}
function isArray(obj) {
return Array.isArray(obj);
}
// Is obj a valid number or NaN? (test if obj is type number)
function isNumber(obj) {
return obj != null && obj.constructor == Number;
}
function isValidNumber(val) {
return isNumber(val) && !isNaN(val);
}
// Similar to isFinite() but does not coerce strings or other types
function isFiniteNumber(val) {
return isValidNumber(val) && val !== Infinity && val !== -Infinity;
}
// This uses type conversion
// export function isFiniteNumber(val) {
// return val > -Infinity && val < Infinity;
// }
function isNonNegNumber(val) {
return isNumber(val) && val >= 0;
}
function isInteger(obj) {
return isNumber(obj) && ((obj | 0) === obj);
}
function isEven(obj) {
return (obj % 2) === 0;
}
function isOdd$1(obj) {
return (obj % 2) === 1;
}
function isString(obj) {
return obj != null && obj.toString === String.prototype.toString;
// TODO: replace w/ something better.
}
function isDate(obj) {
return !!obj && obj.getTime === Date.prototype.getTime;
}
function isBoolean(obj) {
return obj === true || obj === false;
}
function formatDateISO(d) {
if (!isDate(d)) return '';
return d.toISOString().replace(':00.000Z', 'Z');
}
// Convert an array-like object to an Array, or make a copy if @obj is an Array
function toArray(obj) {
var arr;
if (!isArrayLike(obj)) error("toArray() requires an array-like object");
try {
arr = Array.prototype.slice.call(obj, 0); // breaks in ie8
} catch(e) {
// support ie8
arr = [];
for (var i=0, n=obj.length; i<n; i++) {
arr[i] = obj[i];
}
}
return arr;
}
// Array like: has length property, is numerically indexed and mutable.
// TODO: try to detect objects with length property but no indexed data elements
function isArrayLike(obj) {
if (!obj) return false;
if (isArray(obj)) return true;
if (isString(obj)) return false;
if (obj.length === 0 || obj.length > 0) return true;
return false;
}
// See https://raw.github.com/kvz/phpjs/master/functions/strings/addslashes.js
function addslashes(str) {
return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
}
// Escape a literal string to use in a regexp.
// Ref.: http://simonwillison.net/2006/Jan/20/escape/
function regexEscape(str) {
return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
// See https://github.com/janl/mustache.js/blob/master/mustache.js
var entityMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
};
function htmlEscape(s) {
return String(s).replace(/[&<>"'/]/g, function(s) {
return entityMap[s];
});
}
function defaults(dest) {
for (var i=1, n=arguments.length; i<n; i++) {
var src = arguments[i] || {};
for (var key in src) {
if (key in dest === false && src.hasOwnProperty(key)) {
dest[key] = src[key];
}
}
}
return dest;
}
function extend$1(o) {
var dest = o || {},
n = arguments.length,
key, i, src;
for (i=1; i<n; i++) {
src = arguments[i] || {};
for (key in src) {
if (src.hasOwnProperty(key)) {
dest[key] = src[key];
}
}
}
return dest;
}
// Pseudoclassical inheritance
//
// Inherit from a Parent function:
// inherit(Child, Parent);
// Call parent's constructor (inside child constructor):
// this.__super__([args...]);
function inherit(targ, src) {
var f = function() {
if (this.__super__ == f) {
// add __super__ of parent to front of lookup chain
// so parent class constructor can call its parent using this.__super__
this.__super__ = src.prototype.__super__;
// call parent constructor function. this.__super__ now points to parent-of-parent
src.apply(this, arguments);
// remove temp __super__, expose targ.prototype.__super__ again
delete this.__super__;
}
};
f.prototype = src.prototype || src; // added || src to allow inheriting from objects as well as functions
// Extend targ prototype instead of wiping it out --
// in case inherit() is called after targ.prototype = {stuff}; statement
targ.prototype = extend$1(new f(), targ.prototype); //
targ.prototype.constructor = targ;
targ.prototype.__super__ = f;
}
function promisify(asyncFn) {
return function() {
var args = toArray(arguments);
return new Promise((resolve, reject) => {
var cb = function(err, data) {
if (err) reject(err);
else resolve(data);
};
args.push(cb);
asyncFn.apply(this, args);
});
};
}
// Call @iter on each member of an array (similar to Array#reduce(iter))
// iter: function(memo, item, callback)
// Call @done when all members have been processed or if an error occurs
// done: function(err, memo)
// @memo: Initial value
//
function reduceAsync(arr, memo, iter, done) {
var call = typeof setImmediate == 'undefined' ? setTimeout : setImmediate;
var i=0;
next(null, memo);
function next(err, memo) {
// Detach next operation from call stack to prevent overflow
// Don't use setTimeout(, 0) if setImmediate is available
// (setTimeout() can introduce a long delay if previous operation was slow,
// as of Node 0.10.32 -- a bug?)
if (err) {
return done(err, null);
}
call(function() {
if (i < arr.length === false) {
done(null, memo);
} else {
iter(memo, arr[i++], next);
}
}, 0);
}
}
// Append elements of @src array to @dest array
function merge(dest, src) {
if (!isArray(dest) || !isArray(src)) {
error("Usage: merge(destArray, srcArray);");
}
for (var i=0, n=src.length; i<n; i++) {
dest.push(src[i]);
}
return dest;
}
// Returns elements in arr and not in other
// (similar to underscore diff)
function difference(arr, other) {
var index = arrayToIndex(other);
return arr.filter(function(el) {
return !Object.prototype.hasOwnProperty.call(index, el);
});
}
// Return the intersection of two arrays
function intersection(a, b) {
return a.filter(function(el) {
return b.includes(el);
});
}
function indexOf(arr, item) {
var nan = item !== item;
for (var i = 0, len = arr.length || 0; i < len; i++) {
if (arr[i] === item) return i;
if (nan && arr[i] !== arr[i]) return i;
}
return -1;
}
// Test a string or array-like object for existence of substring or element
function contains(container, item) {
if (isString(container)) {
return container.indexOf(item) != -1;
}
else if (isArrayLike(container)) {
return indexOf(container, item) != -1;
}
error("Expected Array or String argument");
}
function some(arr, test) {
return arr.reduce(function(val, item) {
return val || test(item); // TODO: short-circuit?
}, false);
}
function every(arr, test) {
return arr.reduce(function(val, item) {
return val && test(item);
}, true);
}
function find(arr, test, ctx) {
var matches = arr.filter(test, ctx);
return matches.length === 0 ? null : matches[0];
}
function range(len, start, inc) {
var arr = [],
v = start === void 0 ? 0 : start,
i = inc === void 0 ? 1 : inc;
while(len--) {
arr.push(v);
v += i;
}
return arr;
}
function repeat(times, func) {
var values = [],
val;
for (var i=0; i<times; i++) {
val = func(i);
if (val !== void 0) {
values[i] = val;
}
}
return values.length > 0 ? values : void 0;
}
// Calc sum, skip falsy and NaN values
// Assumes: no other non-numeric objects in array
//
function sum$1(arr, info) {
if (!isArrayLike(arr)) error ("sum() expects an array, received:", arr);
var tot = 0,
nan = 0,
val;
for (var i=0, n=arr.length; i<n; i++) {
val = arr[i];
if (val) {
tot += val;
} else if (isNaN(val)) {
nan++;
}
}
if (info) {
info.nan = nan;
}
return tot;
}
// Calculate min and max values of an array, ignoring NaN values
function getArrayBounds(arr) {
var min = Infinity,
max = -Infinity,
nan = 0, val;
for (var i=0, len=arr.length; i<len; i++) {
val = arr[i];
if (val !== val) nan++;
if (val < min) min = val;
if (val > max) max = val;
}
return {
min: min,
max: max,
nan: nan
};
}
// export function uniq(src) {
// var index = {};
// return src.reduce(function(memo, el) {
// if (el in index === false) {
// index[el] = true;
// memo.push(el);
// }
// return memo;
// }, []);
// }
function uniq(src) {
var index = new Set();
var arr = [];
var item;
for (var i=0, n=src.length; i<n; i++) {
item = src[i];
if (!index.has(item)) {
arr.push(item);
index.add(item);
}
}
return arr;
}
function pluck(arr, key) {
return arr.map(function(obj) {
return obj[key];
});
}
function countValues(arr) {
return arr.reduce(function(memo, val) {
memo[val] = (val in memo) ? memo[val] + 1 : 1;
return memo;
}, {});
}
function indexOn(arr, k) {
return arr.reduce(function(index, o) {
index[o[k]] = o;
return index;
}, {});
}
function groupBy(arr, k) {
return arr.reduce(function(index, o) {
var keyval = o[k];
if (keyval in index) {
index[keyval].push(o);
} else {
index[keyval] = [o];
}
return index;
}, {});
}
function arrayToIndex(arr, val) {
var init = arguments.length > 1;
return arr.reduce(function(index, key) {
index[key] = init ? val : true;
return index;
}, {});
}
// Support for iterating over array-like objects, like typed arrays
function forEach(arr, func, ctx) {
if (!isArrayLike(arr)) {
throw new Error("#forEach() takes an array-like argument. " + arr);
}
for (var i=0, n=arr.length; i < n; i++) {
func.call(ctx, arr[i], i);
}
}
function forEachProperty(o, func, ctx) {
Object.keys(o).forEach(function(key) {
func.call(ctx, o[key], key);
});
}
function initializeArray(arr, init) {
for (var i=0, len=arr.length; i<len; i++) {
arr[i] = init;
}
return arr;
}
function replaceArray(arr, arr2) {
arr.splice(0, arr.length);
for (var i=0, n=arr2.length; i<n; i++) {
arr.push(arr2[i]);
}
}
function repeatString(src, n) {
var str = "";
for (var i=0; i<n; i++)
str += src;
return str;
}
function splitLines(str) {
return str.split(/\r?\n/);
}
function pluralSuffix(count) {
return count != 1 ? 's' : '';
}
function endsWith(str, ending) {
return str.indexOf(ending, str.length - ending.length) !== -1;
}
function lpad(str, size, pad) {
pad = pad || ' ';
str = String(str);
return repeatString(pad, size - str.length) + str;
}
function rpad(str, size, pad) {
pad = pad || ' ';
str = String(str);
return str + repeatString(pad, size - str.length);
}
function trim(str) {
return ltrim(rtrim(str));
}
var ltrimRxp = /^\s+/;
function ltrim(str) {
return str.replace(ltrimRxp, '');
}
var rtrimRxp = /\s+$/;
function rtrim(str) {
return str.replace(rtrimRxp, '');
}
function addThousandsSep(str) {
var fmt = '',
start = str[0] == '-' ? 1 : 0,
dec = str.indexOf('.'),
end = str.length,
ins = (dec == -1 ? end : dec) - 3;
while (ins > start) {
fmt = ',' + str.substring(ins, end) + fmt;
end = ins;
ins -= 3;
}
return str.substring(0, end) + fmt;
}
function numToStr(num, decimals) {
return decimals >= 0 ? num.toFixed(decimals) : String(num);
}
function formatNumber$1(val) {
return val + '';
}
function formatIntlNumber(val) {
var str = formatNumber$1(val);
return '"' + str.replace('.', ',') + '"'; // need to quote if comma-delimited
}
function formatNumberForDisplay(num, decimals, nullStr, showPos) {
var fmt;
if (isNaN(num)) {
fmt = nullStr || '-';
} else {
fmt = numToStr(num, decimals);
fmt = addThousandsSep(fmt);
if (showPos && parseFloat(fmt) > 0) {
fmt = "+" + fmt;
}
}
return fmt;
}
function shuffle(arr) {
var tmp, i, j;
for (i = arr.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
function pickOne(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
// Sort an array of objects based on one or more properties.
// Usage: sortOn(array, key1, asc?[, key2, asc? ...])
//
function sortOn(arr) {
var comparators = [];
for (var i=1; i<arguments.length; i+=2) {
comparators.push(getKeyComparator(arguments[i], arguments[i+1]));
}
arr.sort(function(a, b) {
var cmp = 0,
i = 0,
n = comparators.length;
while (i < n && cmp === 0) {
cmp = comparators[i](a, b);
i++;
}
return cmp;
});
return arr;
}
// Sort array of values that can be compared with < > operators (strings, numbers)
// null, undefined and NaN are sorted to the end of the array
// default order is ascending
//
function genericSort(arr, ascending) {
var compare = getGenericComparator(ascending);
Array.prototype.sort.call(arr, compare);
return arr;
}
function getSortedIds(arr, asc) {
var ids = range(arr.length);
sortArrayIndex(ids, arr, asc);
return ids;
}
function sortArrayIndex(ids, arr, asc) {
var compare = getGenericComparator(asc);
ids.sort(function(i, j) {
// added i, j comparison to guarantee that sort is stable
var cmp = compare(arr[i], arr[j]);
return cmp > 0 || cmp === 0 && i > j ? 1 : -1;
});
}
function reorderArray(arr, idxs) {
var len = idxs.length;
var arr2 = [];
for (var i=0; i<len; i++) {
var idx = idxs[i];
if (idx < 0 || idx >= len) error("Out-of-bounds array idx");
arr2[i] = arr[idx];
}
replaceArray(arr, arr2);
}
function getKeyComparator(key, asc) {
var compare = getGenericComparator(asc);
return function(a, b) {
return compare(a[key], b[key]);
};
}
function getGenericComparator(asc) {
asc = asc !== false && asc != 'descending'; // ascending is the default
return function(a, b) {
var retn = 0;
if (b == null) {
retn = a == null ? 0 : -1;
} else if (a == null) {
retn = 1;
} else if (a < b) {
retn = asc ? -1 : 1;
} else if (a > b) {
retn = asc ? 1 : -1;
} else if (a !== a) {
retn = 1;
} else if (b !== b) {
retn = -1;
}
return retn;
};
}
// Generic in-place sort (null, NaN, undefined not handled)
function quicksort$1(arr, asc) {
quicksortPartition(arr, 0, arr.length-1);
if (asc === false) Array.prototype.reverse.call(arr); // Works with typed arrays
return arr;
}
// Moved out of quicksort() (saw >100% speedup in Chrome with deep recursion)
function quicksortPartition (a, lo, hi) {
var i = lo,
j = hi,
pivot, tmp;
while (i < hi) {
pivot = a[lo + hi >> 1]; // avoid n^2 performance on sorted arrays
while (i <= j) {
while (a[i] < pivot) i++;
while (a[j] > pivot) j--;
if (i <= j) {
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i++;
j--;
}
}
if (lo < j) quicksortPartition(a, lo, j);
lo = i;
j = hi;
}
}
function findRankByValue(arr, value) {
if (isNaN(value)) return arr.length;
var rank = 1;
for (var i=0, n=arr.length; i<n; i++) {
if (value > arr[i]) rank++;
}
return rank;
}
function findValueByPct(arr, pct) {
var rank = Math.ceil((1-pct) * (arr.length));
return findValueByRank(arr, rank);
}
// See http://ndevilla.free.fr/median/median/src/wirth.c
// Elements of @arr are reordered
//
function findValueByRank(arr, rank) {
if (!arr.length || rank < 1 || rank > arr.length) error("[findValueByRank()] invalid input");
rank = clamp(rank | 0, 1, arr.length);
var k = rank - 1, // conv. rank to array index
n = arr.length,
l = 0,
m = n - 1,
i, j, val, tmp;
while (l < m) {
val = arr[k];
i = l;
j = m;
do {
while (arr[i] < val) {i++;}
while (val < arr[j]) {j--;}
if (i <= j) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
} while (i <= j);
if (j < k) l = i;
if (k < i) m = j;
}
return arr[k];
}
function findMedian(arr) {
return findQuantile(arr, 0.5);
}
function findQuantile(arr, k) {
var n = arr.length,
i1 = Math.floor((n - 1) * k),
i2 = Math.ceil((n - 1) * k);
if (i1 < 0 || i2 >= n) return NaN;
var v1 = findValueByRank(arr, i1 + 1);
if (i1 == i2) return v1;
var v2 = findValueByRank(arr, i2 + 1);
// use linear interpolation
var w1 = i2 / (n - 1) - k;
var w2 = k - i1 / (n - 1);
var v = (v1 * w1 + v2 * w2) * (n - 1);
return v;
}
function mean(arr) {
var count = 0,
avg = NaN,
val;
for (var i=0, n=arr.length; i<n; i++) {
val = arr[i];
if (isNaN(val)) continue;
avg = ++count == 1 ? val : val / count + (count - 1) / count * avg;
}
return avg;
}
/*
A simplified version of printf formatting
Format codes: %[flags][width][.precision]type
supported flags:
+ add '+' before positive numbers
0 left-pad with '0'
' Add thousands separator
width: 1 to many
precision: .(1 to many)
type:
s string
di integers
f decimal numbers
xX hexidecimal (unsigned)
% literal '%'
Examples:
code val formatted
%+d 1 '+1'
%4i 32 ' 32'
%04i 32 '0032'
%x 255 'ff'
%.2f 0.125 '0.13'
%'f 1000 '1,000'
*/
// Usage: format(formatString, [values])
// Tip: When reusing the same format many times, use formatter() for 5x - 10x better performance
//
function format(fmt) {
var fn = formatter(fmt);
var str = fn.apply(null, Array.prototype.slice.call(arguments, 1));
return str;
}
function formatValue(val, matches) {
var flags = matches[1];
var padding = matches[2];
var decimals = matches[3] ? parseInt(matches[3].substr(1)) : void 0;
var type = matches[4];
var isString = type == 's',
isHex = type == 'x' || type == 'X',
// isInt = type == 'd' || type == 'i',
// isFloat = type == 'f',
isNumber = !isString;
var sign = "",
padDigits = 0,
isZero = false,
isNeg = false;
var str, padChar, padStr;
if (isString) {
str = String(val);
}
else if (isHex) {
str = val.toString(16);
if (type == 'X')
str = str.toUpperCase();
}
else if (isNumber) {
// str = formatNumberForDisplay(val, isInt ? 0 : decimals);
str = numToStr(val, decimals);
if (str[0] == '-') {
isNeg = true;
str = str.substr(1);
}
isZero = parseFloat(str) == 0;
if (flags.indexOf("'") != -1 || flags.indexOf(',') != -1) {
str = addThousandsSep(str);
}
if (!isZero) { // BUG: sign is added when num rounds to 0
if (isNeg) {
sign = "\u2212"; // U+2212
} else if (flags.indexOf('+') != -1) {
sign = '+';
}
}
}
if (padding) {
var strLen = str.length + sign.length;
var minWidth = parseInt(padding, 10);
if (strLen < minWidth) {
padDigits = minWidth - strLen;
padChar = flags.indexOf('0') == -1 ? ' ' : '0';
padStr = repeatString(padChar, padDigits);
}
}
if (padDigits == 0) {
str = sign + str;
} else if (padChar == '0') {
str = sign + padStr + str;
} else {
str = padStr + sign + str;
}
return str;
}
// Get a function for interpolating formatted values into a string.
function formatter(fmt) {
var codeRxp = /%([',+0]*)([1-9]?)((?:\.[1-9])?)([sdifxX%])/g;
var literals = [],
formatCodes = [],
startIdx = 0,
prefix = "",
matches = codeRxp.exec(fmt),
literal;
while (matches) {
literal = fmt.substring(startIdx, codeRxp.lastIndex - matches[0].length);
if (matches[0] == '%%') {
prefix += literal + '%';
} else {
literals.push(prefix + literal);
prefix = '';
formatCodes.push(matches);
}
startIdx = codeRxp.lastIndex;
matches = codeRxp.exec(fmt);
}
literals.push(prefix + fmt.substr(startIdx));
return function() {
var str = literals[0],
n = arguments.length;
if (n != formatCodes.length) {
error("[format()] Data does not match format string; format:", fmt, "data:", arguments);
}
for (var i=0; i<n; i++) {
str += formatValue(arguments[i], formatCodes[i]) + literals[i+1];
}
return str;
};
}
function wildcardToRegExp(name) {
var rxp = name.split('*').map(function(str) {
return regexEscape(str);
}).join('.*');
return new RegExp('^' + rxp + '$');
}
function createBuffer(arg, arg2) {
if (isInteger(arg)) {
return B$3.allocUnsafe ? B$3.allocUnsafe(arg) : new B$3(arg);
} else {
// check allocUnsafe to make sure Buffer.from() will accept strings (it didn't before Node v5.10)
return B$3.from && B$3.allocUnsafe ? B$3.from(arg, arg2) : new B$3(arg, arg2);
}
}
function toBuffer(src) {
if (src instanceof B$3) return src;
if (src instanceof ArrayBuffer) return B$3.from(src);
if (src instanceof Uint8Array) {
return B$3.from(src.buffer, src.byteOffset, src.byteLength);
}
error('Unexpected argument type');
}
function expandoBuffer(constructor, rate) {
var capacity = 0,
k = rate >= 1 ? rate : 1.2,
buf;
return function(size) {
if (size > capacity) {
capacity = Math.ceil(size * k);
buf = constructor ? new constructor(capacity) : createBuffer(capacity);
}
return buf;
};
}
function copyElements(src, i, dest, j, n, rev) {
var same = src == dest || src.buffer && src.buffer == dest.buffer;
var inc = 1,
offs = 0,
k;
if (rev) {
if (same) error('copy error');
inc = -1;
offs = n - 1;
}
if (same && j > i) {
for (k=n-1; k>=0; k--) {
dest[j + k] = src[i + k];
}
} else {
for (k=0; k<n; k++, offs += inc) {
dest[k + j] = src[i + offs];
}
}
}
function extendBuffer(src, newLen, copyLen) {
var len = Math.max(src.length, newLen);
var n = copyLen || src.length;
var dest = new src.constructor(len);
copyElements(src, 0, dest, 0, n);
return dest;
}
function mergeNames(name1, name2) {
var merged;
if (name1 && name2) {
merged = findStringPrefix(name1, name2).replace(/[-_]$/, '');
}
return merged || '';
}
function findStringPrefix(a, b) {
var i = 0;
for (var n=a.length; i<n; i++) {
if (a[i] !== b[i]) break;
}
return a.substr(0, i);
}
function parsePercent(o) {
var str = String(o);
var isPct = str.indexOf('%') > 0;
var pct;
if (isPct) {
pct = Number(str.replace('%', '')) / 100;
} else {
pct = Number(str);
}
if (!(pct >= 0 && pct <= 1)) {
stop(format("Invalid percentage: %s", str));
}
return pct;
}
function formatVersionedName(name, i) {
var suffix = String(i);
if (/[0-9]$/.test(name)) {
suffix = '-' + suffix;
}
return name + suffix;
}
function uniqifyNames(names, formatter) {
var counts = countValues(names),
format = formatter || formatVersionedName,
names2 = [];
names.forEach(function(name) {
var i = 0,
candidate = name,
versionedName;
while (
names2.indexOf(candidate) > -1 || // candidate name has already been used
candidate == name && counts[candidate] > 1 || // duplicate unversioned names
candidate != name && counts[candidate] > 0) { // versioned name is a preexisting name
i++;
versionedName = format(name, i);
if (!versionedName || versionedName == candidate) {
throw new Error("Naming error"); // catch buggy versioning function
}
candidate = versionedName;
}
names2.push(candidate);
});
return names2;
}
// Assume: @raw is string, undefined or null
function parseString(raw) {
return raw ? raw : "";
}
// Assume: @raw is string, undefined or null
// Use null instead of NaN for unparsable values
// (in part because if NaN is used, empty strings get converted to "NaN"
// when re-exported).
function parseNumber(raw) {
return parseToNum(raw, cleanNumericString);
}
function parseIntlNumber(raw) {
return parseToNum(raw, convertIntlNumString);
}
function parseToNum(raw, clean) {
var str = String(raw).trim();
var parsed = str ? Number(clean(str)) : NaN;
return isNaN(parsed) ? null : parsed;
}
// Remove comma separators from strings
function cleanNumericString(str) {
return (str.indexOf(',') > 0) ? str.replace(/,([0-9]{3})/g, '$1') : str;
}
function convertIntlNumString(str) {
str = str.replace(/[ .]([0-9]{3})/g, '$1');
return str.replace(',', '.');
}
function trimQuotes(str) {
var len = str.length, first, last;
if (len >= 2) {
first = str.charAt(0);
last = str.charAt(len-1);
if (first == '"' && last == '"' && !str.includes('","') ||
first == "'" && last == "'" && !str.includes("','")) {
str = str.substr(1, len-2);
// remove string escapes
str = str.replace(first == '"' ? /\\(?=")/g : /\\(?=')/g, '');
}
}
return str;
}
var LOGGING = false;
var STDOUT = false; // use stdout for status messages
var _error, _stop, _message, _warn;
var _interrupt = function() {
throw new NonFatalError(formatLogArgs(arguments));
};
setLoggingForCLI();
function getLoggingSetter() {
var e = _error, s = _stop, m = _message, w = _warn;
return function() {
setLoggingFunctions(m, e, s, w);
};
}
function setLoggingForCLI() {
function stop() {
throw new UserError(formatLogArgs(arguments));
}
function error() {
var msg = utils.toArray(arguments).join(' ');
throw new Error(msg);
}
function message() {
logArgs(arguments);
}
// CLI warning is just a message (GUI behaves differently)
var warn = message;
setLoggingFunctions(message, error, stop, warn);
}
function enableLogging() {
LOGGING = true;
}
function loggingEnabled() {
return !!LOGGING;
}
// Handle an unexpected condition (internal error)
function error() {
_error.apply(null, utils.toArray(arguments));
}
// Handle an error caused by invalid input or misuse of API
function stop() {
// _stop.apply(null, utils.toArray(arguments));
_stop.apply(null, messageArgs(arguments));
}
function interrupt() {
_interrupt.apply(null, utils.toArray(arguments));
}
// Print a status message
function message() {
_message.apply(null, messageArgs(arguments));
}
function warn() {
_warn.apply(null, messageArgs(arguments));
}
// A way for the GUI to replace the CLI logging functions
function setLoggingFunctions(message, error, stop, warn) {
_message = message;
_error = error;
_stop = stop;
_warn = warn;
}
// get detailed error information from error stack (if available)
// Example stack string (Node.js):
/*
/Users/someuser/somescript.js:226
opacity: Math.round(weight * 5 / 5 // 0.2 0.4 0.6 etc
^
SyntaxError: missing ) after argument list
at internalCompileFunction (node:internal/vm:73:18)
at wrapSafe (node:internal/modules/cjs/loader:1149:20)
at Module._compile (node:internal/modules/cjs/loader:1190:27)
...
*/
function getErrorDetail(e) {
var parts = (typeof e.stack == 'string') ? e.stack.split(/\n\s*\n/) : [];
if (parts.length > 1 || true) {
return '\nError details:\n' + parts[0];
}
}
// print a message to stdout
function print() {
STDOUT = true; // tell logArgs() to print to stdout, not stderr
// calling message() adds the "[command name]" prefix
_message(utils.toArray(arguments));
STDOUT = false;
}
function verbose() {
// verbose can be set globally with the -verbose command or separately for each command
if (getStashedVar('VERBOSE')) {
message.apply(null, arguments);
}
}
function debug() {
if (getStashedVar('DEBUG')) {
logArgs(arguments);
}
}
function printError(err) {
var msg;
if (!LOGGING) return;
if (utils.isString(err)) {
err = new UserError(err);
}
if (err.name == 'NonFatalError') {
console.error(messageArgs([err.message]).join(' '));
} else if (err.name == 'UserError') {
msg = err.message;
if (!/Error/.test(msg)) {
msg = "Error: " + msg;
}
console.error(messageArgs([msg]).join(' '));
console.error("Run mapshaper -h to view help");
} else {
// not a user error (i.e. a bug in mapshaper)
console.error(err);
// throw err;
}
}
function UserError(msg) {
var err = new Error(msg);
err.name = 'UserError';
return err;
}
function NonFatalError(msg) {
var err = new Error(msg);
err.name = 'NonFatalError';
return err;
}
function formatColumns(arr, alignments) {
var widths = arr.reduce(function(memo, line) {
return line.map(function(str, i) {
return memo ? Math.max(memo[i], str.length) : str.length;
});
}, null);
return arr.map(function(line) {
line = line.map(function(str, i) {
var rt = alignments && alignments[i] == 'right';
var pad = (rt ? str.padStart : str.padEnd).bind(str);
return pad(widths[i], ' ');
});
return ' ' + line.join(' ');
}).join('\n');
}
// Format an array of (preferably short) strings in columns for console logging.
function formatStringsAsGrid(arr, width) {
// TODO: variable column width
var longest = arr.reduce(function(len, str) {
return Math.max(len, str.length);
}, 0),
colWidth = longest + 2,
perLine = Math.floor((width || 80) / colWidth) || 1;
return arr.reduce(function(memo, name, i) {
var col = i % perLine;
if (i > 0 && col === 0) memo += '\n';
if (col < perLine - 1) { // right-pad all but rightmost column
name = utils.rpad(name, colWidth - 2, ' ');
}
return memo + ' ' + name;
}, '');
}
// expose so GUI can use it
function formatLogArgs(args) {
return utils.toArray(args).join(' ');
}
function messageArgs(args) {
var arr = utils.toArray(args);
var cmd = getStashedVar('current_command');
if (cmd && cmd != 'help') {
arr.unshift('[' + cmd + ']');
}
return arr;
}
function logArgs(args) {
if (!LOGGING || getStashedVar('QUIET') || !utils.isArrayLike(args)) return;
var msg = formatLogArgs(args);
if (STDOUT) console.log(msg);
else console.error(msg);
}
function truncateString(str, maxLen) {
maxLen = maxLen || 80;
if (str.length > maxLen) {
str = str.substring(0, maxLen - 3).trimEnd() + '...';
}
return str;
}
var Logging = /*#__PURE__*/Object.freeze({
__proto__: null,
NonFatalError: NonFatalError,
UserError: UserError,
debug: debug,
enableLogging: enableLogging,
error: error,
formatColumns: formatColumns,
formatLogArgs: formatLogArgs,
formatStringsAsGrid: formatStringsAsGrid,
getErrorDetail: getErrorDetail,
getLoggingSetter: getLoggingSetter,
interrupt: interrupt,
logArgs: logArgs,
loggingEnabled: loggingEnabled,
message: message,
print: print,
printError: printError,
setLoggingForCLI: setLoggingForCLI,
setLoggingFunctions: setLoggingFunctions,
stop: stop,
truncateString: truncateString,
verbose: verbose,
warn: warn
});
function Transform() {
this.mx = this.my = 1;
this.bx = this.by = 0;
}
Transform.prototype.isNull = function() {
return !this.mx || !this.my || isNaN(this.bx) || isNaN(this.by);
};
Transform.prototype.invert = function() {
var inv = new Transform();
inv.mx = 1 / this.mx;
inv.my = 1 / this.my;
//inv.bx = -this.bx * inv.mx;
//inv.by = -this.by * inv.my;
inv.bx = -this.bx / this.mx;
inv.by = -this.by / this.my;
return inv;
};
Transform.prototype.transform = function(x, y, xy) {
xy = xy || [];
xy[0] = x * this.mx + this.bx;
xy[1] = y * this.my + this.by;
return xy;
};
Transform.prototype.toString = function() {
return JSON.stringify(Object.assign({}, this));
};
function Bounds() {
if (arguments.length > 0) {
this.setBounds.apply(this, arguments);
}
}
Bounds.from = function() {
var b = new Bounds();
return b.setBounds.apply(b, arguments);
};
Bounds.prototype.toString = function() {
return JSON.stringify({
xmin: this.xmin,
xmax: this.xmax,
ymin: this.ymin,
ymax: this.ymax
});
};
Bounds.prototype.toArray = function() {
return this.hasBounds() ? [this.xmin, this.ymin, this.xmax, this.ymax] : [];
};
Bounds.prototype.hasBounds = function() {
return this.xmin <= this.xmax && this.ymin <= this.ymax;
};
Bounds.prototype.sameBounds =
Bounds.prototype.equals = function(bb) {
return bb && this.xmin === bb.xmin && this.xmax === bb.xmax &&
this.ymin === bb.ymin && this.ymax === bb.ymax;
};
Bounds.prototype.width = function() {
return (this.xmax - this.xmin) || 0;
};
Bounds.prototype.height = function() {
return (this.ymax - this.ymin) || 0;
};
Bounds.prototype.area = function() {
return this.width() * this.height() || 0;
};
Bounds.prototype.empty = function() {
this.xmin = this.ymin = this.xmax = this.ymax = void 0;
return this;
};
Bounds.prototype.setBounds = function(a, b, c, d) {
if (arguments.length == 1) {
// assume first arg is a Bounds or array
if (utils.isArrayLike(a)) {
b = a[1];
c = a[2];
d = a[3];
a = a[0];
} else {
b = a.ymin;
c = a.xmax;
d = a.ymax;
a = a.xmin;
}
}
this.xmin = a;
this.ymin = b;
this.xmax = c;
this.ymax = d;
if (a > c || b > d) this.update();
return this;
};
Bounds.prototype.centerX = function() {
var x = (this.xmin + this.xmax) * 0.5;
return x;
};
Bounds.prototype.centerY = function() {
var y = (this.ymax + this.ymin) * 0.5;
return y;
};
Bounds.prototype.containsPoint = function(x, y) {
if (x >= this.xmin && x <= this.xmax &&
y <= this.ymax && y >= this.ymin) {
return true;
}
return false;
};
// intended to speed up slightly bubble symbol detection; could use intersects() instead
// TODO: fix false positive where circle is just outside a corner of the box
Bounds.prototype.containsBufferedPoint =
Bounds.prototype.containsCircle = function(x, y, buf) {
if ( x + buf > this.xmin && x - buf < this.xmax ) {
if ( y - buf < this.ymax && y + buf > this.ymin ) {
return true;
}
}
return false;
};
Bounds.prototype.intersects = function(bb) {
if (bb.xmin <= this.xmax && bb.xmax >= this.xmin &&
bb.ymax >= this.ymin && bb.ymin <= this.ymax) {
return true;
}
return false;
};
Bounds.prototype.contains = function(bb) {
if (bb.xmin >= this.xmin && bb.ymax <= this.ymax &&
bb.xmax <= this.xmax && bb.ymin >= this.ymin) {
return true;
}
return false;
};
Bounds.prototype.shift = function(x, y) {
this.setBounds(this.xmin + x,
this.ymin + y, this.xmax + x, this.ymax + y);
};
Bounds.prototype.padBounds = function(a, b, c, d) {
this.xmin -= a;
this.ymin -= b;
this.xmax += c;
this.ymax += d;
};
// Rescale the bounding box by a fraction. TODO: implement focus.
// @param {number} pct Fraction of original extents
// @param {number} pctY Optional amount to scale Y
//
Bounds.prototype.scale = function(pct, pctY) { /*, focusX, focusY*/
var halfWidth = (this.xmax - this.xmin) * 0.5;
var halfHeight = (this.ymax - this.ymin) * 0.5;
var kx = pct - 1;
var ky = pctY === undefined ? kx : pctY - 1;
this.xmin -= halfWidth * kx;
this.ymin -= halfHeight * ky;
this.xmax += halfWidth * kx;
this.ymax += halfHeight * ky;
return this;
};
// Return a bounding box with the same extent as this one.
Bounds.prototype.cloneBounds = // alias so child classes can override clone()
Bounds.prototype.clone = function() {
return new Bounds(this.xmin, this.ymin, this.xmax, this.ymax);
};
Bounds.prototype.clearBounds = function() {
this.setBounds(new Bounds());
};
Bounds.prototype.mergePoint = function(x, y) {
if (this.xmin === void 0) {
this.setBounds(x, y, x, y);
} else {
// this works even if x,y are NaN
if (x < this.xmin) this.xmin = x;
else if (x > this.xmax) this.xmax = x;
if (y < this.ymin) this.ymin = y;
else if (y > this.ymax) this.ymax = y;
}
};
// expands either x or y dimension to match @aspect (width/height ratio)
// @focusX, @focusY (optional): expansion focus, as a fraction of width and height
Bounds.prototype.fillOut = function(aspect, focusX, focusY) {
if (arguments.length < 3) {
focusX = 0.5;
focusY = 0.5;
}
var w = this.width(),
h = this.height(),
currAspect = w / h,
pad;
if (isNaN(aspect) || aspect <= 0) ; else if (currAspect < aspect) { // fill out x dimension
pad = h * aspect - w;
this.xmin -= (1 - focusX) * pad;
this.xmax += focusX * pad;
} else {
pad = w / aspect - h;
this.ymin -= (1 - focusY) * pad;
this.ymax += focusY * pad;
}
return this;
};
Bounds.prototype.update = function() {
var tmp;
if (this.xmin > this.xmax) {
tmp = this.xmin;
this.xmin = this.xmax;
this.xmax = tmp;
}
if (this.ymin > this.ymax) {
tmp = this.ymin;
this.ymin = this.ymax;
this.ymax = tmp;
}
};
Bounds.prototype.transform = function(t) {
this.xmin = this.xmin * t.mx + t.bx;
this.xmax = this.xmax * t.mx + t.bx;
this.ymin = this.ymin * t.my + t.by;
this.ymax = this.ymax * t.my + t.by;
this.update();
return this;
};
// Returns a Transform object for mapping this onto Bounds @b2
// @flipY (optional) Flip y-axis coords, for converting to/from pixel coords
//
Bounds.prototype.getTransform = function(b2, flipY) {
var t = new Transform();
t.mx = b2.width() / this.width() || 1; // TODO: better handling of 0 w,h
t.bx = b2.xmin - t.mx * this.xmin;
if (flipY) {
t.my = -b2.height() / this.height() || 1;
t.by = b2.ymax - t.my * this.ymin;
} else {
t.my = b2.height() / this.height() || 1;
t.by = b2.ymin - t.my * this.ymin;
}
return t;
};
Bounds.prototype.mergeCircle = function(x, y, r) {
if (r < 0) r = -r;
this.mergeBounds([x - r, y - r, x + r, y + r]);
};
Bounds.prototype.mergeBounds = function(bb) {
var a, b, c, d;
if (bb instanceof Bounds) {
a = bb.xmin;
b = bb.ymin;
c = bb.xmax;
d = bb.ymax;
} else if (arguments.length == 4) {
a = arguments[0];
b = arguments[1];
c = arguments[2];
d = arguments[3];
} else if (bb.length == 4) {
// assume array: [xmin, ymin, xmax, ymax]
a = bb[0];
b = bb[1];
c = bb[2];
d = bb[3];
} else {
error("Bounds#mergeBounds() invalid argument:", bb);
}
if (this.xmin === void 0) {
this.setBounds(a, b, c, d);
} else {
if (a < this.xmin) this.xmin = a;
if (b < this.ymin) this.ymin = b;
if (c > this.xmax) this.xmax = c;
if (d > this.ymax) this.ymax = d;
}
return this;
};
function countPointsInLayer(lyr) {
var count = 0;
if (layerHasPoints(lyr)) {
forEachPoint(lyr.shapes, function() {count++;});
}
return count;
}
// export function getPointBounds(shapes) {
// var bounds = new Bounds();
// forEachPoint(shapes, function(p) {
// bounds.mergePoint(p[0], p[1]);
// });
// return bounds;
// }
function getPointBounds$1(shapes) {
new Bounds();
var shp, x, y, xmin = Infinity, ymin = Infinity, xmax = -Infinity, ymax = -Infinity;
for (var i=0, n=shapes.length; i<n; i++) {
shp = shapes[i];
for (var j=0, m=shp ? shp.length : 0; j<m; j++) {
x = shp[j][0];
y = shp[j][1];
if (x > xmax) xmax = x;
if (x < xmin) xmin = x;
if (y > ymax) ymax = y;
if (y < ymin) ymin = y;
}
}
return shp ? new Bounds(xmin, ymin, xmax, ymax) : new Bounds();
}
function getPointFeatureBounds(shape, bounds) {
var n = shape ? shape.length : 0;
var p;
if (!bounds) bounds = new Bounds();
for (var i=0; i<n; i++) {
p = shape[i];
bounds.mergePoint(p[0], p[1]);
}
return bounds;
}
// NOTE: layers can have multipoint features and null features
function getPointsInLayer(lyr) {
var coords = [];
forEachPoint(lyr.shapes, function(p) {
coords.push(p);
});
return coords;
}
// Iterate over each [x,y] point in a layer
// shapes: one layer's "shapes" array
function forEachPoint(shapes, cb) {
var i, n, j, m, shp;
for (i=0, n=shapes.length; i<n; i++) {
shp = shapes[i];
for (j=0, m=shp ? shp.length : 0; j<m; j++) {
cb(shp[j], i);
}
}
}
var PointUtils = /*#__PURE__*/Object.freeze({
__proto__: null,
countPointsInLayer: countPointsInLayer,
forEachPoint: forEachPoint,
getPointBounds: getPointBounds$1,
getPointFeatureBounds: getPointFeatureBounds,
getPointsInLayer: getPointsInLayer
});
function absArcId(arcId) {
return arcId >= 0 ? arcId : ~arcId;
}
function calcArcBounds(xx, yy, start, len) {
var i = start | 0,
n = isNaN(len) ? xx.length - i : len + i,
x, y, xmin, ymin, xmax, ymax;
if (n > 0) {
xmin = xmax = xx[i];
ymin = ymax = yy[i];
}
for (i++; i<n; i++) {
x = xx[i];
y = yy[i];
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
if (y < ymin) ymin = y;
if (y > ym